最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - jQuery dragdroppable scope issue - Stack Overflow

programmeradmin3浏览0评论

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
Add a ment  | 

1 Answer 1

Reset to default 9

There'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

发布评论

评论列表(0)

  1. 暂无评论