【问题标题】:Cannot drop a draggable where two droppables touch each other不能在两个可放置对象相互接触的地方放置可拖动对象
【发布时间】:2011-06-08 21:57:23
【问题描述】:

似乎当使用两个相互接触的 jquery UI droppables 时,droppable 事件没有正确触发。如果在将一个可拖动元素从其中一个元素上拖动到其下方的 到下一个元素上时,则会为第一个可放置元素触发 out 事件,但不会为第二个元素触发 over 事件。如果此时您放下,则不会触发任何放置事件。

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

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

这对我来说似乎是一个错误,但我找不到其他任何人使用表行作为可放置对象并报告了该问题。我对表格进行了样式设置,因此您可以看到行确实是齐平的,中间没有空格。

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

此外,我们在可拖动帮助器中以图标和消息的形式向用户提供反馈,该图标和消息会根据您结束的拖放操作而变化。当您在行之间拖动时,它会闪烁片刻,因为它认为您实际上并没有超过任何可放置对象。

我的问题:

  1. 是否有针对此问题的修复或解决方法?
  2. 我应该将此报告为错误吗?

更新

@davin,

我们最终确实更改了 $.ui.ddmanager 中的拖动功能以修复事件顺序。我们的问题是我们嵌套了贪婪的droppables。当您从其中一个嵌套的 droppables 从下到上移动到另一个时,over 事件实际上会为父级触发,从而导致坏事发生。

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

要求您快速检查一下并确保我们的逻辑合理,会不会太过分了?有两个逻辑变化。如果我们从贪婪的孩子变成了贪婪的孩子:

  1. 不要取消设置 parentInstance.greedyChild
  2. 不要触发 parentInstance._over 事件。

这是代码。请参阅我们添加的处理 isParentStateChanged 闭包变量的行:

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

}

【问题讨论】:

  • 出于好奇,我修改了您的 jsfiddle here 以使用 div 并得到相同的结果,因此您使用表格似乎并不重要。也许这种行为是有意的。
  • @squidbe,感谢您提供的好信息。考虑到它不仅发生在表格行中,编辑的标题和初始描述更通用。
  • 看看我的插件.. myabe它会帮助你:[stackoverflow.com/a/16720944/1308461][1] [1]:stackoverflow.com/a/16720944/1308461

标签: javascript jquery jquery-ui draggable droppable


【解决方案1】:

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

指针:鼠标指针与droppable重叠

当我的鼠标指针位于 (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 触发之前有两个 over 事件。这并不难处理,但您需要确保处理好这种情况。

更新:

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

无论如何,我有第二个解决方法可以完全解决上述问题,现在每次鼠标移动时,指针都只能进入或退出每个元素:

$.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/

本质上,我已将上限条件设为弱不等式,将下限条件设为强不等式。所以边界是完全相邻的。现在事件几乎完美地触发了。几乎而不完全是因为插件仍然按顺序循环通过可放置对象,所以如果我从上到下拖动,触发顺序很好,因为首先它检测到我已经离开了较高的元素,然后检测到我已经进入了较低的元素,而从下到上拖动,发射顺序是相反的——首先它发射进入较高的元素,然后才离开较低的元素。

这个和以前的解决方法的区别在于,即使有一半的时间订单不好,这一切都发生在一个滴答声中,即over-out或out-over总是在一起,用户永远不会被卡住,因为在原始情况下和第一个解决方法。

您可以通过更改 ui 代码以首先根据将鼠标悬停在项目上的项目循环遍历项目,然后再将其余项目(在 @987654331 @ 功能)。这样鼠标离开总是先开火。或者,您可以交换订单并获得相反的顺序;任何更适合你的。

那肯定会彻底解决你的问题。

【讨论】:

  • @mazlix,它在 chrome 中对我有用。仔细解释为什么你认为它不起作用? “不起作用”对每个人的帮助与牙齿疼痛一样多。
  • 抱歉,我的意思是如果我拖动项目并将光标从 row2 缓慢移动到 row1,我的 console.log 会触发 over1 两次,然后我必须移动另一个像素并触发 over2。理想情况下,我会想象它会在同一个事件中触发 over1 和 out1,但现在我意识到对于 OP 来说这可能并不重要,因为他可以完成第二个 over1 事件。你做得很好!
  • @mazlix,我在帖子中提到了这一点。无论如何,我有一个解决方法,即使是一点点麻烦也可以解决。我会更新帖子
  • @davin,感谢您提供详细的解决方法和解释。我会在早上尝试。正如您所指出的,当从下到上时,我看到事件发生得很奇怪,事实上,这对我来说很重要。但是,我已经有了一个忽略过时事件的解决方案,因为我在其他情况下也遇到过这种情况。所以我可能不需要(希望!)覆盖比你给的更多的东西。
  • 作为旁注,我觉得很奇怪,droppable 默认情况下不能按照您提到的方式工作,检查是否必须在新事件之前触发事件。
【解决方案2】:

听起来您可能在 行之间放置,这意味着您正在放置在桌子上。你的表格边框折叠了吗? cssborder-collapse: collapsed;

【讨论】:

  • 事实上,我的应用程序中确实有边框折叠,但正如您在小提琴中看到的那样,同样的行为发生在没有折叠边框的情况下,实际上根本没有任何边框。我也在应用程序中对此进行了测试,结果相同。
【解决方案3】:

我正在做的一个项目遇到了这个问题。

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

底部类似。

为此,我更改了 $.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){}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    相关资源
    最近更新 更多