我遇到的问题是,一个元素的dragleave事件是在悬停该元素的子元素时触发的。同样,当再次悬停回父元素时,dragenter不会被触发。
我做了一个简化的小提琴:http://jsfiddle.net/pimvdb/HU6Mk/1/。
HTML:
<div id="drag" draggable="true">drag me</div>
<hr>
<div id="drop">
drop here
<p>child</p>
parent
</div>
使用以下JavaScript:
$('#drop').bind({
dragenter: function() {
$(this).addClass('red');
},
dragleave: function() {
$(this).removeClass('red');
}
});
$('#drag').bind({
dragstart: function(e) {
e.allowedEffect = "copy";
e.setData("text/plain", "test");
}
});
它应该做的是,当拖拽一些东西时,通过将drop div设置为红色来通知用户。这是可行的,但是如果你拖动到p子元素中,拖动键就会触发,div就不再是红色了。移动回下拉div也不会使它再次变红。有必要完全移出拖放div,并再次拖回它,使其变为红色。
是否有可能阻止dragleave在拖动到子元素时发射?
2017年更新:TL;DR,查找CSS指针事件:无;如@ h.d.所述。在现代浏览器和IE11中都能运行。
我编写了一个名为drop -drop的拖放模块来修复这种奇怪的行为。如果你正在寻找一个好的低级拖放模块,你可以用作任何事情的基础(文件上传,应用程序内拖放,从或从外部源拖放),你应该检查这个模块:
https://github.com/fresheneesz/drip-drop
这是你在点滴中尝试做的事情:
$('#drop').each(function(node) {
dripDrop.drop(node, {
enter: function() {
$(node).addClass('red')
},
leave: function() {
$(node).removeClass('red')
}
})
})
$('#drag').each(function(node) {
dripDrop.drag(node, {
start: function(setData) {
setData("text", "test") // if you're gonna do text, just do 'text' so its compatible with IE's awful and restrictive API
return "copy"
},
leave: function() {
$(node).removeClass('red')
}
})
})
为了在没有库的情况下做到这一点,我在滴漏中使用了反制技术,尽管评分最高的答案错过了重要的步骤,这将导致除了第一个滴漏之外的所有东西都被打破。以下是正确的做法:
var counter = 0;
$('#drop').bind({
dragenter: function(ev) {
ev.preventDefault()
counter++
if(counter === 1) {
$(this).addClass('red')
}
},
dragleave: function() {
counter--
if (counter === 0) {
$(this).removeClass('red');
}
},
drop: function() {
counter = 0 // reset because a dragleave won't happen in this case
}
});
我也遇到过同样的问题,下面是我的解决方案——我认为比上面的要简单得多。我不确定它是否跨浏览器(可能取决于冒泡顺序)
为了简单起见,我将使用jQuery,但解决方案应该是框架独立的。
事件以任意一种方式生成父节点,如下所示:
<div class="parent">Parent <span>Child</span></div>
我们附加事件
el = $('.parent')
setHover = function(){ el.addClass('hovered') }
onEnter = function(){ setTimeout(setHover, 1) }
onLeave = function(){ el.removeClass('hovered') }
$('.parent').bind('dragenter', onEnter).bind('dragleave', onLeave)
差不多就是这样。:)它可以工作,因为即使onEnter在子上先于onLeave在父上触发,我们延迟它稍微颠倒顺序,所以类先被删除,然后在一毫秒后重新应用。
我有一个类似的问题-我的代码隐藏dropzone dragleave事件的身体被触发时悬停子元素使dropzone闪烁在谷歌Chrome。
我能够通过调度隐藏dropzone的函数来解决这个问题,而不是立即调用它。然后,如果触发另一个拖拽或拖拽,则取消预定的函数调用。
body.addEventListener('dragover', function() {
clearTimeout(body_dragleave_timeout);
show_dropzone();
}, false);
body.addEventListener('dragleave', function() {
clearTimeout(body_dragleave_timeout);
body_dragleave_timeout = setTimeout(show_upload_form, 100);
}, false);
dropzone.addEventListener('dragover', function(event) {
event.preventDefault();
dropzone.addClass("hover");
}, false);
dropzone.addEventListener('dragleave', function(event) {
dropzone.removeClass("hover");
}, false);
解决这个问题的“正确”方法是禁用拖放目标的子元素上的指针事件(如@ h.d.。的回答)。下面是我创建的演示这种技术的jsFiddle。不幸的是,这在IE11之前的ie版本中不起作用,因为它们不支持指针事件。
Luckily, I was able to come up with a workaround which does work in old versions of IE. Basically, it involves identifying and ignoring dragleave events which occur when dragging over child elements. Because the dragenter event is fired on child nodes before the dragleave event on the parent, separate event listeners can be added to each child node which add or remove an "ignore-drag-leave" class from the drop target. Then the drop target's dragleave event listener can simply ignore calls which occur when this class exists. Here's a jsFiddle demonstrating this workaround. It is tested and working in Chrome, Firefox, and IE8+.
更新:
我创建了一个jsFiddle,演示了一个使用特性检测的组合解决方案,其中如果支持指针事件,则使用指针事件(目前是Chrome、Firefox和IE11),如果指针事件支持不可用,则浏览器退回到向子节点添加事件(IE8-10)。