jQuery UI - Droppable only accept one draggable
I'm making an app, that is using one droppable div
and a few draggable div
s. How can I make the droppable to not accept more than one draggable div
? I Googled, but didn't find any workaroun开发者_如何学God.
A workaround came up in mi mind. How can i check is there's dropped element in this droppable div? If it's busy then revert this draggable, which is trying to be dropped
OK found a nice solution for this, essentially on 'drop' I set the droppable to only accept the item which has been dragged into it.
When you 'disable', the 'out' event that you need to re-initialize isn't available anymore, so instead I just switched the eligible items around.
Then it's possible for me to use the OUT event to re-accept all draggable items and because nothing else is accepted the OUT won't be triggered by other draggables:
$(".drop-zone").droppable({
drop: function(event, ui) {
$(this).droppable('option', 'accept', ui.draggable);
},
out: function(event, ui){
$(this).droppable('option', 'accept', '.drag-item');
}
});
});
You can destroy the .droppable()
widget after the first drop
, like this:
$(".droppable").droppable({
drop: function( event, ui ) {
$(this).droppable("destroy");
}
});
You can try out a demo here.
Easy Peasey. Just enable all the .drop-zone's when hovered over them, and then check if the currently hovered .drop-zone contains a draggable element
$('.drop-zone').droppable({
over: function(event, ui) {
// Enable all the .droppable elements
$('.droppable').droppable('enable');
// If the droppable element we're hovered over already contains a .draggable element,
// don't allow another one to be dropped on it
if($(this).has('.draggable').length) {
$(this).droppable('disable');
}
}
});
I spend many hours to figure it out and finally it works for me like this:
$( ".drop-zone" ).droppable({
classes: {
"ui-droppable-active": "ui-state-active",
"ui-droppable-hover": "ui-state-hover"
},
accept: function( draggable ){
if (!$(this).hasClass('dropped') || draggable.hasClass('dropped')){
return true;
}
return false;
},
drop: function( event, ui ) {
$(this).addClass('dropped');
ui.draggable.addClass('dropped');
},
out: function( event, ui ){
$(this).removeClass('dropped');
ui.draggable.removeClass('dropped');
}
});
This solution solves a major bug in Likwid_T's answer.
$('.draggable').draggable({
start: function(ev, ui) {
$('.ui-droppable').each(function(i, el) {
if (!$(el).find('.ui-draggable').length) $(el).droppable('enable');
});
}
});
$('.droppable').droppable({
drop: function(ev, ui) {
$(ev['target']).droppable('disable');
}
});
How about this:
$(".drop-zone").droppable({
accept: function(draggable) {
return $(this).find("*").length == 0;
});
});
This way the accept funcion return true only when no elements have been dropped yet.
To enable it, use the option: $(".selector").droppable({ disabled: **false** });
You could also do it the other way around, by reverting the draggable when the droppable has a certain class or attribute (building on this example: https://stackoverflow.com/a/3418306/1005334).
So for example, using the rel
attribute (you could also use class
or something else), for the droppable:
$('.drop-zone').droppable({
drop: function () {
drop.attr('rel', 'filled');
}
});
And the draggable:
$('.draggable').draggable({
revert: function (droppable) {
if (droppable.attr('rel') == 'filled') {
return true;
}
}
});
My solution is similar to Likwid_T's, except it uses the droppable drop
event as well as maintaining the links between draggables and droppables instead of droppable's out
event. I think the problem with using out
is that it is fired even when a draggable is dragged over an already "full" droppable and then "out" of it.
droppable({
drop: function(event, ui) {
var $droppable = $(this);
var $draggable = ui.draggable;
// If the draggable is moved from another droppable, unlink it from the old droppable
var oldDropped = $draggable.data('dropped');
if(oldDropped) {
$draggable.data('dropped', null);
oldDropped.data('dragged', null);
}
// Link the draggable and droppable
$draggable.data('dropped', $droppable);
$droppable.data('dragged', $draggable);
},
accept: function() {
// Only accept if there is no draggable already associated
return !$(this).data('dragged');
}
});
A related feature is that one dragging one item over a droppable that already has a draggable, the old one would get replaced and revert to its initial position. This is how I do it:
droppable({
drop: function(event, ui) {
var $droppable = $(this);
var $draggable = ui.draggable;
// Reset position of any old draggable here
var oldDragged = $droppable.data('dragged');
if(oldDragged) {
// In the CSS I have transitions on top and left for .ui-draggable, so that it moves smoothly
oldDragged.css({top: 0, left: 0});
oldDragged.data('dropped', null);
}
// If the draggable is moved from another droppable, unlink it from the old droppable
var oldDropped = $draggable.data('dropped');
if(oldDropped) {
$draggable.data('dropped', null);
oldDropped.data('dragged', null);
}
// Link the draggable and droppable
$draggable.data('dropped', $droppable);
$droppable.data('dragged', $draggable);
},
});
精彩评论