我遇到的问题是,一个元素的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中都能运行。
这是我的解决方案(https://jsfiddle.net/42mh0fd5/8):
<div id="droppable">
<div id="overlay"></div>
<a href="">test child 1</a>
<br /><br />
<button>test child 2</button>
<br /><br />
<button>test child 3</button>
<br />
</div>
<p id="draggable" draggable="true">This element is draggable.</p>
<script type="text/javascript">
var dropElem = document.getElementById('droppable');
var overlayElem = document.getElementById('overlay');
overlayElem.addEventListener('drop', function(ev) {
ev.preventDefault();
console.log('drop', ev.dataTransfer.files)
overlayElem.classList.remove('dragover')
dropElem.classList.add('dropped')
console.log('drop')
}, false);
overlayElem.addEventListener('dragover', function(ev) {
ev.preventDefault();
}, false);
overlayElem.addEventListener('dragleave', function(ev) {
console.log('dragleave')
overlayElem.classList.remove('dragover')
}, false);
dropElem.addEventListener('dragenter', function(ev) {
console.log('dragenter')
overlayElem.classList.add('dragover')
}, false);
</script>
<style>
#draggable{
padding:5px;
background: #fec;
display: inline-block;
}
#droppable{
width: 300px;
background: #eef;
border: 1px solid #ccd;
position: relative;
text-align: center;
padding:30px;
}
#droppable.dropped{
background: #fee;
}
#overlay.dragover{
content:"";
position: absolute;
z-index: 1;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,.2);
border:3px dashed #999;
}
</style>
下面是另一种基于事件时间的方法。
从子元素分派的dragenter事件可以被父元素捕获,并且它总是发生在dragleave之前。这两个事件之间的时间间隔非常短,比任何可能的人类鼠标操作都要短。所以,这个想法是记住dragenter发生的时间,过滤dragleave事件发生“不是太快”之后…
这个简短的例子适用于Chrome和Firefox:
var node = document.getElementById('someNodeId'),
on = function(elem, evt, fn) { elem.addEventListener(evt, fn, false) },
time = 0;
on(node, 'dragenter', function(e) {
e.preventDefault();
time = (new Date).getTime();
// Drag start
})
on(node, 'dragleave', function(e) {
e.preventDefault();
if ((new Date).getTime() - time > 5) {
// Drag end
}
})
我也遇到过同样的问题,下面是我的解决方案——我认为比上面的要简单得多。我不确定它是否跨浏览器(可能取决于冒泡顺序)
为了简单起见,我将使用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在父上触发,我们延迟它稍微颠倒顺序,所以类先被删除,然后在一毫秒后重新应用。