41

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.

casperOne
  • 73,706
  • 19
  • 184
  • 253
devongovett
  • 4,850
  • 5
  • 34
  • 35
  • What does `if (!jQuery(this).data("init")) { jQuery(this).data("init", true);}` do? –  Jan 28 '11 at 12:45

10 Answers10

45

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})
stldoug
  • 853
  • 9
  • 14
  • This broke my js altogether. Do you need to put this somewhere in particular to extend the JQuery functionality? – Andrew Feb 03 '11 at 09:31
  • 1
    You need to declare it after you load jQuery and before you use it in your code. What error are you getting? – stldoug Apr 09 '11 at 23:11
  • You forgot to return the element for chaining – Randomblue Sep 04 '11 at 14:07
  • 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. – Morg. Apr 13 '12 at 06:50
  • Also just found this whilst trying to solve a similar problem. Excellent solution! – Phil May 10 '12 at 10:10
  • 2
    Better than storing data in elements, why not rely on the presence of the .ui-draggable CSS class ? I think this is lighter – mexique1 Oct 21 '12 at 14:33
  • 3
    Nowadays you should use `this.on('mouseover', selector, ...)` instead of `this.live('mouseover', ...)` – Dehalion Oct 23 '13 at 14:33
21

This is a sample of code that perfectly worked for me

$('.gadgets-column').live('mouseover',function(){
    $(this).draggable();
});
Jasim Muhammed
  • 1,376
  • 1
  • 20
  • 28
  • 3
    it's an easy solution, but draggable is executed on all DOM elements with gadgets-column class *everytime* you hover one of them... – Michal B. Feb 23 '12 at 20:09
  • @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). – Morg. Apr 10 '12 at 15:22
  • 2
    @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. – Michal B. Apr 11 '12 at 15:27
  • Incredibly easy, its the simplest solution. Congratulations – Yises Apr 24 '12 at 10:57
10

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})

wojtekk
  • 606
  • 7
  • 7
7

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.

john
  • 79
  • 1
  • 3
  • 2
    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')){...}"? – stldoug May 25 '11 at 01:16
  • couldn´t u use delegate instead. there you can specify an additional selector. – Luke Mar 06 '13 at 11:05
4

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.

Yarin
  • 173,523
  • 149
  • 402
  • 512
1

An example:

Turkish:

<div id="diyalogKutusu">
    <div id="diyalog-baslik">..baslik..</div>
    <div id="icerik">..icerik..</div>
</div>

$(document).on("mouseover", "#diyalogKutusu", function() {
    $(this).draggable({ handle: '#diyalog-baslik' });
});

English:

<div id="dialogBox">
    <div id="dialogBox-title">..title..</div>
    <div id="content">..content..</div>
</div>

$(document).on("mouseover", "#dialogBox", function() {
    $(this).draggable({ handle: '#dialogBox-title' });
});

Note: You can use on() instead of live() or delegate. The on() has good performance than others

1
$("html divs to drag").appendTo("#layoutDiv").draggable(options);

JSFiddle

T J
  • 42,762
  • 13
  • 83
  • 138
0

An old question. But threedubmedia has drag and drop plugin with live (as of v 1.7 known as simply "on") support. http://threedubmedia.com/code/event/drop Haven't used it to much so I can't account for it performance, etc. but looks reasonable.

Danny C
  • 2,980
  • 2
  • 28
  • 20
0

Another option is to mix the mouseover handler with a removable class, like so:

$('.outer-container').on('mouseover', '.my-draggable.drag-unbound', function(e) {
  $(this).draggable().removeClass('drag-unbound');
});

It's fairly straightforward and resolves some of the issues that other answers have with re-binding over and over as you mouseover.

Micah
  • 17,584
  • 8
  • 40
  • 46
0

An updated version that does not use live as it is deprecated:

function liveDraggable(selector, options) {
    $(document).on('mouseover', selector, function () {
        if (!$(this).data("init")) {
            $(this).data("init", true);
            $(this).draggable(options);
        }
    });
}
gevorg
  • 4,835
  • 4
  • 35
  • 52
ErwanLent
  • 606
  • 8
  • 15