jQuery Drag And Drop Using Live Events

39,649

Solution 1

Wojtek's solution worked perfectly for me. I wound up changing it a tad bit to make it extend jQuery...

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

Now instead of calling it like:

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

...just use:

$(selector).liveDraggable({opts})

Solution 2

This is a sample of code that perfectly worked for me

$('.gadgets-column').live('mouseover',function(){
    $(this).draggable();
});

Solution 3

You could make wrapper function like this:

function liveDraggable(selector, options){
  jQuery(selector).live("mouseover",function(){
    if (!jQuery(this).data("init")) {
      jQuery(this).data("init", true);
      jQuery(this).draggable(options);
    }
  });
}

(I use prototype with jQuery - that's why i placed jQuery() instead of $())

And now instead of $(selector).draggable({opts}) use liveDraggable(selector, {opts})

Solution 4

Stldoug's code worked for me, but there's no need to keep checking the element's .data("init") on every mouseover event. Also, it's better to use "mousemove", as "mouseover" doesn't always get triggered if your mouse is already over the element when the .live function kicks in.

(function ($) {
    $.fn.liveDraggable = function (opts) {
        this.live("mousemove", function() {
            $(this).draggable(opts);
        });
    };
}(jQuery));

Here's how you use it:

$('.thing:not(.ui-draggable)').liveDraggable();

The trick is to add ":not(.ui-draggable)" to your selector. Since jQuery will automatically add the "ui-draggable" class to your element when it becomes draggable, the .live function will no longer target it. In other words, it only triggers once, unlike the other solution which triggers over and over as you move stuff around.

Ideally, you could just .unbind the "mousemove", but that doesn't work with .live, unfortunately.

Solution 5

Combining the best answers from @john and @jasimmk:

Using .live:

$('li:not(.ui-draggable)').live('mouseover',function(){
    $(this).draggable(); // Only called once per li
});

.live is deprecated though, better to use .on:

$('ul').on('mouseover', 'li:not(.ui-draggable)', function(){
    $(this).draggable();  // Only called once per li
});

As @john explained, .ui-draggable is automatically added to draggable methods, so by excluding that class with the selector, you ensure that draggable() will only be called once on each element. And using .on will reduce the scope of the selector, improving performance.

Share:
39,649
Fred
Author by

Fred

All around JavaScripter. Blogger at badassjs.com. Audio hacker audiocogs.org. Engineer at Storify.

Updated on July 09, 2022

Comments

  • Fred
    Fred almost 2 years

    I have an application with a long list that changes frequently, and I need the items of that list to be draggable.

    I've been using the jQuery UI draggable plugin, but it is slow to add to 400+ list items, and has to be re-added every time new list items are added.

    Does anyone know of a plugin similar to the jQuery UI draggable plugin that uses jQuery 1.3's .live() events? This would solve both problems.

  • satyavrat
    satyavrat over 13 years
    This broke my js altogether. Do you need to put this somewhere in particular to extend the JQuery functionality?
  • stldoug
    stldoug about 13 years
    You need to declare it after you load jQuery and before you use it in your code. What error are you getting?
  • stldoug
    stldoug almost 13 years
    Yes, definitely cleaner. What do you think about moving the check for "ui-draggable" inside the plug-in function... something like "if(!this.hasClass('ui-draggable')){...}"?
  • Randomblue
    Randomblue over 12 years
    You forgot to return the element for chaining
  • Michal B.
    Michal B. about 12 years
    it's an easy solution, but draggable is executed on all DOM elements with gadgets-column class everytime you hover one of them...
  • Morg.
    Morg. about 12 years
    @MichalB. No that is not how jQuery works, the $(this) refers to the item being mouseover'd and this can be even much lighter than having draggable() set before (i.e. the draggable logic will not be active until you mouseover).
  • Michal B.
    Michal B. about 12 years
    @Morg: I agree with all you said, you just did not understand what I meant. Every time you hover an element that has class gadgets-column the code will execute. The code is $(this).draggable(); and that is not something you would like to execute every time you hover.
  • Morg.
    Morg. about 12 years
    by the way . this is incorrect, you forgot to remove the mouseover handler when you set the draggable. I'll correct the fn.liveDraggable accordingly.
  • Yises
    Yises about 12 years
    Incredibly easy, its the simplest solution. Congratulations
  • Phil
    Phil almost 12 years
    Also just found this whilst trying to solve a similar problem. Excellent solution!
  • mexique1
    mexique1 over 11 years
    Better than storing data in elements, why not rely on the presence of the .ui-draggable CSS class ? I think this is lighter
  • Luke
    Luke about 11 years
    couldn´t u use delegate instead. there you can specify an additional selector.
  • Dehalion
    Dehalion over 10 years
    Nowadays you should use this.on('mouseover', selector, ...) instead of this.live('mouseover', ...)