两个droppables相互接触时,不能放下拖动器

时间:2011-06-08 21:57:23

标签: javascript jquery jquery-ui draggable droppable

当使用两个相互接触的jquery UI droppable时,似乎没有正确触发droppable事件。如果在将一个元素的拖动拖动到下面的元素下面的只是的同时,则会为第一个droppable触发out事件,但是第二个不会触发over事件。如果你在此时掉线,则不会触发掉线事件。

一个例子是最好的。试试this fiddle(在IE7,8,9和Chrome11中测试过)。确保浏览器的控制台日志已打开。如果将拖动拖动到第一行,然后慢慢向第二行拖动,您很快就会在日志中看到第一行的out事件被触发,但第二行的over事件不是。如果在发生这种情况时丢弃,则不会触发drop事件。

似乎只是导致问题的行之间的1像素线。再拖动一个像素会导致触发事件,并使drop事件正常工作。

这对我来说看起来像个错误,但我找不到其他人使用表行作为droppables并报告了问题。我设置了表格,这样你就可以看到行确实是齐平的,两者之间没有空格。

这对我来说是个大问题,因为在我们的应用程序中,表行是嵌套的贪婪droppables。因此,如果用户在发生这种情况时掉线,则实际上是由外部droppable拾取掉落。

此外,我们会在可拖动帮助程序中以图标和消息的形式向用户提供反馈,该消息会根据您所覆盖的可丢弃内容而发生变化。当你在行之间拖动时,它会闪烁片刻,因为它认为你实际上并没有超过任何可丢弃的那些。

我的问题:

  1. 此问题是否有任何修复或解决方法?
  2. 我应该将此报告为错误吗?
  3. 更新

    @davin,

    我们最终改变了$ .ui.ddmanager中的拖动功能来修复事件排序。我们的问题是我们嵌套了贪婪的droppables。当您从这些嵌套的droppable中的一个移动到另一个从底部到顶部时,over事件实际上会为父进程触发,导致不好的事情发生。

    所以我们添加了逻辑来基本检查是否从一个嵌套的贪婪移动到另一个,如果是,则不会激发父事件。

    要求让你快速看一下并确保我们的逻辑有意义吗?有两个逻辑上的变化。如果我们从贪婪的孩子变成贪婪的孩子:

    1. 不要取消设置parentInstance.greedyChild
    2. 不要激活parentInstance._over事件。
    3. 这是代码。请参阅我们添加的处理isParentStateChanged闭包var的行:

          drag: function(draggable, event) {
      
          //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
          if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
      
          var isParentStateChanged = false;
      
          //Run through all droppables and check their positions based on specific tolerance options
          $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
      
              if(this.options.disabled || this.greedyChild || !this.visible) return;
              var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
      
              var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
              if(!c) return;
      
              var parentInstance;
              if (this.options.greedy && !isParentStateChanged) {
                  var parent = this.element.parents(':data(droppable):eq(0)');
                  if (parent.length) {
                      parentInstance = $.data(parent[0], 'droppable');
                      parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
                  }
              }
      
              // we just moved into a greedy child
              if (parentInstance && c == 'isover') {
                  isParentStateChanged = true;
      
                  parentInstance['isover'] = 0;
                  parentInstance['isout'] = 1;
                  parentInstance._out.call(parentInstance, event);
              }
      
              this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
              this[c == "isover" ? "_over" : "_out"].call(this, event);
      
              // we just moved out of a greedy child
              if (parentInstance && c == 'isout') {
      
                  if (!isParentStateChanged) {
                      parentInstance['isout'] = 0;
                      parentInstance['isover'] = 1;
                      parentInstance._over.call(parentInstance, event);
                  }
              }
          });
      
      }
      

3 个答案:

答案 0 :(得分:3)

这不是一个bug本身,它是一个功能。这都是定义问题。您已将可放置项目的容差定义为指针,根据文档为:

  

指针:鼠标指针与可放置的

重叠

当我的鼠标指针位于(10,10)并且我的方框的左上角结束于(10,10)时,是否重叠?这取决于你的定义。 jQueryUI的定义是强不等式或强重叠(see the relevant code)。这对我来说是有意义的,因为如果我只是在边缘,我就不在盒子里,所以我不希望事件发生。

虽然为了您的目的,您需要在重叠条件下弱不等式(即弱重叠),您可以通过添加以下内容来修改源代码中的代码行,或覆盖它:

$.ui.isOverAxis = function( x, reference, size ) {
    return ( x >= reference ) && ( x <= ( reference + size ) );
};

工作示例:http://jsfiddle.net/vwLhD/8/

请注意,如果弱不平等会导致其他问题:您的out事件将在over事件发生后触发,因此您可能会在单out之前发生两次事件被解雇。这不是很难处理,但你需要确保你处理这种情况。

<强>更新

重要的是要注意,如果你添加我粘贴在上面的代码,它会影响$范围内的所有其他ui小部件,如果这很重要的话。也许subbing $可以避免这种情况。

在任何情况下,我都有第二种解决方法可以完全解决上述问题,现在每次鼠标移动时,指针都可以独占于每个元素之内或之外:

$.ui.isOverAxis2 = function( x, reference, size ) {
    return ( x >= reference ) && ( x < ( reference + size ) );
};
$.ui.isOver = function( y, x, top, left, height, width ) {
    return $.ui.isOverAxis2( y, top, height ) && $.ui.isOverAxis( x, left, width );
};

工作示例:http://jsfiddle.net/vwLhD/10/

基本上我已经将上限条件视为弱不平等而下级条件是强大的不平等。所以边界完全相邻。现在事件几乎完美。几乎也不完全是因为插件仍然按顺序循环通过droppables,所以如果我从上到下拖动触发顺序是好的,因为首先它检测到我已离开较高元素,然后检测到我已进入较低的元素,而从底部向顶部拖动,射击的顺序是相反的 - 首先它会进入较高的位置,然后才会离开较低的位置。

这个和之前的解决方法之间的区别在于,即使有一半的时间订单不好,但这一切都发生在一个滴答中,即超出或彻底总是在一起,用户永远不会陷入困境在原始案例和第一个解决方法。

你可以通过改变ui代码来根据那些将鼠标悬停在项目上的项目进行循环,然后只有其余的(在{{}中进一步磨练这个绝对完美1}}功能)。这样,鼠标离开将始终先开火。或者,您可以交换订单并具有相反的顺序;什么都适合你。

这肯定会完全解决你的问题。

答案 1 :(得分:0)

听起来你可能会在行之间删除,这意味着你要掉到桌子上。你的桌边是否已折叠? css border-collapse: collapsed;

答案 2 :(得分:0)

我正在处理一个正在处理的项目。

我的解决方案是查看每个可放置的拖拉机的距离。如果draggable比顶部droppable高50%,那么我假设用户想要放在顶部droppable上。

类似于底部。

要做到这一点,我改变了$ .ui.intersect;

添加了变量 -     hw = droppable.proportions.width / 2,hh = droppable.proportions.height / 2,     lhw = l + hw,     thh = t + hh

然后添加一些if语句

// going down
if(y2 < b && y2 >= thh){}

// going up
if(y1 > t && y1 <= thh){}

// covered
if(y1 <= t && y2 >= b){}