I want to have several classes of draggables, each one corresponding to a class of droppables. But in addition, I want to have a separate "waste bin", where all the draggables can be dropped until a suitable droppable can be found for them.
Now, this can be easily achieved with an accept function. However, I may have up to 20 classes, each one with 30-40 draggables/droppables. So if I use an "accept" function for this, the moment I pick up a draggable, my chrome freezes as it runs tests for every droppable on the screen :(
This can be solved if I use the 'scope' property, since it seems to be using some different way. However, when I use a scope, I can't seem to implement the "waste bin" concept, since it can only have one scope!
Is there some way to by-pass this problem? Give the draggables more than one scope, or giving the waste bin many scopes? Or maybe some other solution I can't think of?
Internally jQuery UI will run the following code whenever you start dragging a draggable
to determine which droppable
s are eligible to receive the draggable
.
var m = $.ui.ddmanager.droppables[t.options.scope] || [];
var type = event ? event.type : null; // workaround for #2317
var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
droppablesLoop: for (var i = 0; i < m.length; i++) {
if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
m[i].offset = m[i].element.offset();
m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
}
As you can see the code is non trivial and would explain why you're seeing slow performance every time you start dragging.
One thing to note is that the very first thing checked in the droppablesLoop
is whether the droppable
is disabled.
Therefore, to increase performance you could always manually disable the appropriate droppable
widgets which will make you quickly jump out of the code block above. You can do this by using the start
event on the draggable
, which will fire first.
$('.draggable').draggable({
start: function() {
$('.invalid-droppable-elements').droppable('option', 'disabled', true);
},
stop: function() {
$('.invalid-droppable-elements').droppable('option', 'disabled', false);
}
});
This essentially makes you implement the accept
/ scope
logic yourself and the performance impact is up to your algorithm. It shouldn't be that bad to implement though. The reason the plugins are as slow as they are is because they have to handle for a LOT of different situations.
jQuery UI does not support adding multiple scopes to individual draggable
/ droppable
elements but you could roll that functionality on your own.
I put an example together to show this here - http://jsfiddle.net/tj_vantoll/TgQTP/1/.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With