2010-06-22 6 views
3

Voir le code here on jsbin. Je suis en train d'essayer de créer une série de commutateurs. Chaque carré rouge rempli peut être déplacé de haut en bas. Les contours rouges sur le fond sont des zones de largage. Quand un carré est déplacé au-dessus d'une zone de largage pouvant être acceptée, la zone de dépôt doit devenir rose.jQuery drag/droppable scope issue

Il y a deux problèmes avec ce code. L'une est que, tandis que le mouvement des bascules est contraint à l'axe des y, ils peuvent toujours être déposés sur n'importe quelle zone de largage. Cliquez et faites glisser une bascule et faites-la glisser autour de la rangée du bas et vous verrez les zones de largage devenir rose même si la bascule reste en place.

Ce qui conduit au deuxième problème. Pour résoudre ce problème, j'ai essayé d'utiliser l'option scope, qui groupe les drags et les drops. Un glisser peut seulement tomber sur une zone de dépôt avec la même portée. Dans l'exemple ci-dessus, les lignes qui ajoutent la portée sont commentées. La portée de chaque glisser-déposer est "Par défaut". Si vous décommentez ces deux lignes (cliquez sur l'onglet en haut à droite si vous êtes nouveau sur jsbin, puis cliquez sur Aperçu après un changement) vous verrez qu'au lieu de restreindre chaque glisser à une seule zone de dépôt, ils peuvent ne plus laisser tomber aucune zone de dépôt. La fonction de rappel ne se déclenche jamais.

Pour plus de commodité, voici la partie javascript de l'exemple:

$(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!'); 
    } 
}); 
+0

Je déteste donner un anti-solution, mais vous avez considéré, au lieu de glisser-déposer, juste pour déclencher l'interrupteur avec l'événement mouseDown '.mousedown (function)'? À mon avis, il est beaucoup mieux pour l'utilisabilité (étant le chemin du moins wtf) et s'entend mieux avec les appareils mobiles. –

+0

Parfois, l'anti-solution est la meilleure solution! Bon point. Si je fais fonctionner cela, je pourrais toujours faire les deux. L'idée originale était d'essayer de concevoir des éléments de navigation qui utilisent des analogies physiques pour l'interaction.Les interrupteurs devraient bien sûr "s'enclencher" quand ils sont relâchés comme s'il y avait un cran, mais ne l'ont pas encore écrit. – jasongetsdown

Répondre

9

Il y a un bug dans jQuery lors de la configuration de l'option scope d'un largable via la fonction de l'option. jQuery gère un tableau avec tous les triables enregistrés (appelons le S pour l'instant), où chaque clé est une portée spécifique. Lorsque vous déposez un élément déplaçable dans le droppable, jQuery vérifie alors l'attribut scope du draggable et vérifie si le droppable que vous essayez de faire glisser est présent dans S[scope]. Si ce n'est pas le cas, cela signifie que la droppable dans laquelle vous tentez de tomber n'est pas dans la même portée que le draggable.

Le problème est que lorsque vous modifiez l'option de portée en faisant .droppable('option', 'scope', ...), le tableau S n'est pas mis à jour. Tout le reste (pour autant que je puisse voir) est mis à jour correctement (attribut d'option de l'objet jQuery réel etc), conduisant à des résultats "corrects" retournés lors de l'obtention de l'option portée via .droppable('option', 'scope'). J'ai trouvé un couple d'autres personnes ayant la même question et aucune solution n'est apparue, et cette question a été soulevée lorsque j'ai cherché des solutions ("jquery droppable scope option"), alors j'ai pensé qu'il pourrait être utile de fournir une solution temporaire jusqu'à ce que c'est réglé. J'ai fait une fonction d'extension, pas très bien testée en ce qui concerne les conflits possibles avec d'autres options, mais au moins c'est un début. $.ui.ddmanager.droppables est le tableau que j'ai appelé S précédemment.

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

Votre exemple serait alors ressembler à

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

+1

Validé! Se comporte exactement comme prévu. Merci pour l'aperçu d'un bug sournois, et pour fournir le code pour le réparer. Comme vous l'avez dit, il signale la portée correcte, mais agit alors comme il s'en fout! – jasongetsdown

+0

C'est en 2013 et ce bug est toujours présent :( – pckben