Firefox在拖动文本时会触发dragleave。

7

我正在尝试跟踪整个屏幕的拖入/离开事件,在Chrome / Safari中一切正常,这要归功于来自https://dev59.com/wWkv5IYBdhLWcg3w71H7#10310815 的draghover插件,如下所示:

$.fn.draghover = function(options) {
    return this.each(function() {

        var collection = $(),
            self = $(this);

        self.on('dragenter', function(e) {
            if (collection.size() === 0) {
                self.trigger('draghoverstart');
            }
            collection = collection.add(e.target);
        });

        self.on('dragleave drop', function(e) {
            // timeout is needed because Firefox 3.6 fires the dragleave event on
            // the previous element before firing dragenter on the next one
            setTimeout( function() {
                collection = collection.not(e.target);
                if (collection.size() === 0) {
                    self.trigger('draghoverend');
                }
            }, 1);
        });
    });
};

function setText(text) {
    $('p.target').text(text);
}

$(document).ready(function() {
    $(window).draghover().on({
        'draghoverstart': function() {
            setText('enter');
        },
        'draghoverend': function() {
            setText('leave');
        }
    });
});

然而,当我拖动文本项时,火狐浏览器仍然给我带来问题,这里有一个演示的小工具:http://jsfiddle.net/tusRy/6/。这是火狐的bug还是可以用JS解决?或者有没有更稳健的方法来执行所有这些操作?
谢谢!更新:更新了小工具http://jsfiddle.net/tusRy/6/以减少一些杂乱。为了解释小工具的预期行为:
- 将文件拖入窗口,p.target应为"ENTER",变成黄色。 - 将文件拖出窗口,p.target应为"LEAVE",变成红色。 - 在窗口中放置文件,p.target应为"LEAVE",变成红色。
在Firefox中,当您将文件拖动到文本上方时,会触发"LEAVE"事件。

我目前通过使用覆盖层div来解决了这个问题,参考http://jsfiddle.net/tusRy/7/。然而,我并不是很满意这是唯一的解决方案,所以我会保持这个问题的开放状态,直到有更好的想法出现。 - DanH
4个回答

10

截至版本22.0,Firefox仍然会这样做。当您在文本节点上拖动时,它将触发两种类型的dragenterdragleave事件:一个事件的目标和相关目标都是文本节点的父元素,另一个事件的目标是父元素,而相关目标是实际的文本节点(甚至不是一个正确的DOM元素)。

解决方法就是在您的dragenterdragleave处理程序中检查这两种事件并忽略它们:

try {
    if(event.relatedTarget.nodeType == 3) return;
} catch(err) {}
if(event.target === event.relatedTarget) return;

我使用try/catch语句块来检查节点类型,因为有时事件会从文档外部(例如其他iframe)不可预知地触发,尝试访问它们的节点类型会引发权限错误。

以下是实现代码: http://jsfiddle.net/9A7te/


1
我认为jQuery-2.1.1的拖放事件处理存在问题:我注意到,在拖入和拖出时,它传递的不是有问题的文本节点作为event.target,而是传递了文本节点的父元素。如果您仍然遇到问题,我想知道在此处使用原生方法是否有帮助。 - Gavin
1
顺便说一句,这个答案对我帮助最大。如果我上面的回答不够清楚的话,我的意思是在这个 bug 中,jQuery 本身不准确地将事件目标复制到它的自定义事件对象中。我已经根据 event.originalEvent.target 和 event.originalEvent.relatedTarget 实现了一个 nodeType 检查,并取得了不错的结果。 - Gavin
1
非常完美!我已经将它添加到插件本身中,以避免最终事件处理程序中需要重新编写它。在Chrome和Firefox中完美运行。尚未在其他浏览器中测试以验证任何怪异的错误,但这两个主要浏览器通常是我需要支持的唯一浏览器。 - Demonslay335

2

1) 您的dropzone应该只有一个子元素,该元素可能包含您需要的所有其他内容。例如:

<div id="#dropzone">
    <div><!--Your contents here--></div>
</div>

2) 使用以下CSS:

#dropzone * { pointer-events: none; }

你可能需要包含:before:after,因为*不适用于它们。
这应该足以让下拉菜单在Firefox和Chrome中正常工作。在你的示例中,只需添加以下内容即可:
body * { pointer-events: none; }

在 CSS 的结尾处。我已经在这里完成了: 其他示例:

2
我在这个SO问题的非选定答案中找到了答案,该问题询问子元素上的dragleave触发。我有一个<div>有很多子元素。每当页面上有dragenter时,一个半透明遮罩层<span>就会出现在<div>上。正如你所发现的那样,'dragover'并不像mouseover。当您悬停在子元素上时,它会触发dragleave

解决方案? Dragout 它使dragover更像mouseover。非常简短。


2

我想出了一种解决方案,但还未在除Chrome和FF之外的其他浏览器上进行测试,但到目前为止已经可以工作。这就是现在的setTimeout的样子:

setTimeout( function() {
    var isChild = false;

    // in order to get permission errors, use the try-catch
    // to check if the relatedTarget is a child of the body
    try {
        isChild = $('body').find(e.relatedTarget).length ? true : isChild;
    }
    catch(err){} // do nothing

    collection = collection.not(e.target);
    if (collection.size() === 0 && !isChild) {
        self.trigger('draghoverend');
    }
}, 1);

这里是整个代码 - http://jsfiddle.net/tusRy/13/
思路是检查相关标签是否是body的子元素,在这种情况下,我们仍然在浏览器中,不应该触发“draghoverend”事件。由于移出窗口时可能会引发错误,因此我使用了try方法来避免这种情况。
也许某些在JS方面更有技巧的人可以改进这个方法 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接