5

There is a div with id parentDiv, and there are some div with class block1 in parentDiv divl

like

<div id="btn"><input name="click" type="button" value="Click" class='button" /></div>

<div id="parentDiv">
    <div class="block1" style=" width:100px; height:100px; background:orange;">I am Block1</div>
    <div class="block1" style=" width:100px; height:100px; background:orange;">I am Block1</div>
</div>

and In JQuery, div with class block1, are draggable.

$(function(){
    $('.block1').draggable({
        drag: function() {
           $('.block1').text('Drag Now!');
        },
        stop: function() {
           $('.block1').text('Stop Now!');
        }
    });
});

These div are working as aspect, but the problem is, if any new div with block1 is appended in the parentDiv by clicking on btn input like

$('#btn').on('click', 'input.button', function(){
    var $newDiv=$('<div class="block1" style=" width:100px; height:100px; background:green;">I am Block1</div>');
});

that is not draggable.

Yes, It will not work, because it was not in DOM.

We are able to define a click event on #btn div to its children input.button, and if we add new input with class button in this #btn div, all will work as aspect.

So my question is, Is there a way to make all div draggable with in a parent container parentDiv, like we can do with #btn div?

Yograj Gupta
  • 9,811
  • 3
  • 29
  • 48

4 Answers4

5

You can use the jQuery on method with the mouseover event to bind uninitialized draggable children:

$(".parentDiv").on("mouseover", ".block1", function() {

    // Use the UI pseudo-selector to check that
    // it is not already draggable
    if(!$(this).is(":ui-draggable"))
        $(this).draggable({
            /* Options */
        });
});

For convenience, wrapped in a function that extends jQuery:

$.fn.extend({

    /**
     * Children of the element with the given selector
     * will automatically be made draggable
     * @param {String} selector
     *  The selector of the child / descendant elements
     *  to automatically be made draggable
     * @param {Object} opts
     *  The options that would normally be passed to the
     *  draggable method
     * @returns
     *  The jQuery array for chaining
     */
    draggableChildren: function(selector, opts) {

        // On using event delegation to automatically
        // handle new child events
        $(this).on("mouseover", selector, function() {

           // Check that draggable not already initialized
           if(!$(this).is(":ui-draggable"))

               // Initialize draggable
               $(this).draggable(opts);
        });

        // Return this for chaining
        return this;
    }
});

You can use this as follows:

$(".parentDiv").draggableChildren(".block1", {
    drag: function() {
        $(this).text('Drag Now!');
    },
    stop: function() {
        $(this).text('Stop Now!');
    }
});

Here is a fiddle showing it in action

Stuart Wakefield
  • 6,294
  • 24
  • 35
  • Thanks a lot of this! +100 if I could. I made also a .resizable version of this. This technique allowed me to reduce .draggable and .resizable creation time of 260 divs from 2600 ms to 20 ms, because .d and .r are now initialized only when needed. And what the best: this have to be done only once per page load. – Timo Kähkönen May 14 '15 at 15:38
2

You need to use the "live" function of jquery to add events and functions on future elements.

This code is borrowed from another post (http://stackoverflow.com/questions/1805210/jquery-drag-and-drop-using-live-events)

(function ($) {
   $.fn.liveDraggable = function (opts) {
      this.live("mouseover", function() {
         if (!$(this).data("init")) {
            $(this).data("init", true).draggable(opts);
         }
      });
      return $();
   };
}(jQuery));

Now instead of calling it like:

$(selector).draggable({opts});

...just use:

$(selector).liveDraggable({opts})
Carlos Martinez T
  • 6,458
  • 1
  • 34
  • 40
1

You need to set the containment property to parent to restrict the area.

$("#yourItem" ).draggable({ containment: "parent" });

To enable draggable for the new dynamic item, what you can do is, move the code which binds the draggablity feature to a method and call that method after you add new item to the DOM

 function BindDraggable()
 {
    $('.block1').draggable({
        drag: function() {
           $('.block1').text('Drag Now!');
        },
        stop: function() {
           $('.block1').text('Stop Now!');
        },
        containment: 'parent'
    });
 }

Now call it on document ready and soon after you add new content

$(function(){
  BindDraggable();

  $('#btn').on('click', 'input#button', function(){
    var $newDiv=$('<div class="block1" style=" width:100px; height:100px; background:green;">I am Block1</div>');
    //to do :attach the new item to the DOM

    BindDraggable();
});

});

Shyju
  • 214,206
  • 104
  • 411
  • 497
  • Yes, Currently I am using as you describe, But Is there any way to only call this BindDraggable() once, and It work for new once. – Yograj Gupta Oct 22 '12 at 13:56
-1
$('#maindiv div").draggable({container:"#maindiv",scroll:false}) 

and now you can do what ever you want

René Höhle
  • 26,716
  • 22
  • 73
  • 82
  • There is no `container` option in draggable object as far as I know. Did you mean `containment`..? – T J Nov 28 '14 at 17:01