See the code here on jsbin. I'm basically trying to create a series of switches. Each filled red square can be dragged up and down. The red outlines on the bottom are drop zones. When a square is dragged over a drop zone that is eligible to accept it the drop zone should turn pink.
There are two problems with this code. One is that while the motion of the toggles is constrained to the y-axis, they can still be dropped on any drop zone. Click and drag a toggle and slide around the bottom row and you'll see the drop zones turn pink even though the toggle stays in place.
Which leads to the second problem. To address this issue I tried to use the scope option, which groups drags and drops. A drag can only drop on a drop zone with the same scope. In the above example the lines that add scope are mented out. The scope for each drag and drop is "Default."
If you unment those two lines (click the tab in the upper right if you're new to jsbin, then click preview after a change) you'll see that instead of restricting each drag to one drop zone, they can no longer drop on any drop zone. The callback function never fires.
For convenience, here is the javascript portion of the example:
$(document).ready( function() {
var draggables = $('div.dragMe'),
droppables = $('div.dropMe');
draggables.draggable(
{
axis: 'y',
containment: 'parent'
});
droppables.droppable(
{
hoverClass: 'dropped',
drop: dropCallBack
});
draggables.each(function(index) {
//$(this).draggable('option', 'scope', ''+index);
//droppables.eq(index).droppable('option', 'scope', ''+index);
$(this).text( $(this).draggable('option', 'scope') )
droppables.eq(index).text( droppables.eq(index).droppable('option', 'scope') );
});
function dropCallBack(e, ui) {
alert('Dropped!');
}
});
See the code here on jsbin. I'm basically trying to create a series of switches. Each filled red square can be dragged up and down. The red outlines on the bottom are drop zones. When a square is dragged over a drop zone that is eligible to accept it the drop zone should turn pink.
There are two problems with this code. One is that while the motion of the toggles is constrained to the y-axis, they can still be dropped on any drop zone. Click and drag a toggle and slide around the bottom row and you'll see the drop zones turn pink even though the toggle stays in place.
Which leads to the second problem. To address this issue I tried to use the scope option, which groups drags and drops. A drag can only drop on a drop zone with the same scope. In the above example the lines that add scope are mented out. The scope for each drag and drop is "Default."
If you unment those two lines (click the tab in the upper right if you're new to jsbin, then click preview after a change) you'll see that instead of restricting each drag to one drop zone, they can no longer drop on any drop zone. The callback function never fires.
For convenience, here is the javascript portion of the example:
$(document).ready( function() {
var draggables = $('div.dragMe'),
droppables = $('div.dropMe');
draggables.draggable(
{
axis: 'y',
containment: 'parent'
});
droppables.droppable(
{
hoverClass: 'dropped',
drop: dropCallBack
});
draggables.each(function(index) {
//$(this).draggable('option', 'scope', ''+index);
//droppables.eq(index).droppable('option', 'scope', ''+index);
$(this).text( $(this).draggable('option', 'scope') )
droppables.eq(index).text( droppables.eq(index).droppable('option', 'scope') );
});
function dropCallBack(e, ui) {
alert('Dropped!');
}
});
Share
Improve this question
asked Jun 22, 2010 at 21:36
jasongetsdownjasongetsdown
1,3051 gold badge17 silver badges22 bronze badges
2
-
I hate to give an anti-solution, but have you considered, instead of drag-drop, just to trigger the switch with the mouseDown
.mousedown(function)
event? In my opinion, it es off as a lot better for usability (being the path of least wtf) and gets along better with mobile devices. – Steven Commented Jun 22, 2010 at 21:40 - Sometimes the anti-solution is the best solution! Good point. If I get this working I could always do both. The original idea was to try to design some navigation elements that use physical analogies for interaction. The switches should of course "snap" into place when they are released as though there was a detent, just haven't written that part yet. – jasongetsdown Commented Jun 22, 2010 at 21:59
1 Answer
Reset to default 9There's a bug in jQuery when setting the scope
option of a droppable via the option function. jQuery maintains an array with all registered sortables (let's call it S
for now), where each key is one specific scope. When you drop a draggable element into the droppable, jQuery then checks the scope attribute of the draggable and checks if the droppable you are trying to drag into is present in S[scope]
. If it isn't it means that the droppable you are tring to drop into is not in the same scope as the draggable.
The problem is that when you change the scope option by doing .droppable('option', 'scope', ...)
, the array S
isn't updated. Everything else (as far as I can see) is updated correctly (option attribute of the actual jQuery object etc), leading to "correct" results being returned when getting the scope option via .droppable('option', 'scope')
.
I found a couple of other people having the same question and no solutions came up, and this very question came up when i googled for solutions ("jquery droppable scope option"
), so I thought it could be useful to provide a temporary solution until it is fixed. I made an extention function, not very well tested with regards to possible conflicts with other options, but atleast it's a start. $.ui.ddmanager.droppables
is the array I called S
previously.
jQuery.fn.extend({
setDroppableScope: function(scope) {
return this.each(function() {
var currentScope = $(this).droppable("option","scope");
if (typeof currentScope == "object" && currentScope[0] == this) return true; //continue if this is not droppable
//Remove from current scope and add to new scope
var i, droppableArrayObject;
for(i = 0; i < $.ui.ddmanager.droppables[currentScope].length; i++) {
var ui_element = $.ui.ddmanager.droppables[currentScope][i].element[0];
if (this == ui_element) {
//Remove from old scope position in jQuery's internal array
droppableArrayObject = $.ui.ddmanager.droppables[currentScope].splice(i,1)[0];
//Add to new scope
$.ui.ddmanager.droppables[scope] = $.ui.ddmanager.droppables[scope] || [];
$.ui.ddmanager.droppables[scope].push(droppableArrayObject);
//Update the original way via jQuery
$(this).droppable("option","scope",scope);
break;
}
}
});
}
});
Your example would then look like
draggables.each(function(index) {
$(this).draggable('option', 'scope', ''+index);
droppables.eq(index).setDroppableScope(''+index);
$(this).text( $(this).draggable('option', 'scope') )
droppables.eq(index).text( droppables.eq(index).droppable('option', 'scope') );
});
Here's the updated jsbin