我遇到的问题是元素的 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
子项,则 dragleave
被触发并且 div
不再是红色。回到下降 div
也不会使它再次变红。有必要完全移出拖放 div
并再次拖回它以使其变为红色。
拖入子元素时是否可以防止 dragleave
触发?
2017 更新: TL;DR,查找 CSS pointer-events: none;
,如下面的@HD 回答中所述,适用于现代浏览器和 IE11。
dragleave
。
你只需要保留一个引用计数器,当你得到一个 dragenter 时增加它,当你得到一个 dragleave 时减少它。当计数器为 0 时 - 删除该类。
var counter = 0;
$('#drop').bind({
dragenter: function(ev) {
ev.preventDefault(); // needed for IE
counter++;
$(this).addClass('red');
},
dragleave: function() {
counter--;
if (counter === 0) {
$(this).removeClass('red');
}
}
});
注意:在 drop 事件中,将计数器重置为零,并清除添加的类。
您可以运行它here
拖入子元素时是否可以防止dragleave触发?
是的。
#drop * {pointer-events: none;}
那个 CSS 对 Chrome 来说似乎已经足够了。
在 Firefox 中使用它时,#drop 不应该直接有文本节点(否则会有一个奇怪的 issue where a element "leave it to itself"),所以我建议只保留一个元素(例如,在 #drop 中使用 div 将所有内容放入其中)
Here's a jsfiddle 解决 original question (broken) example。
我还从 @Theodore Brown 示例中创建了一个 simplified version,但仅基于此 CSS。
不过,并非所有浏览器都实现了此 CSS:http://caniuse.com/pointer-events
看到 Facebook 源代码,我可以多次找到这个 pointer-events: none;
,但它可能与优雅降级后备一起使用。至少它是如此简单并且解决了很多环境的问题。
pointer-events: none;
添加到类。使用 ondragenter 将类应用到放置目标。然后从 ondragleave 中的放置目标中删除该类。
在提出这个问题并提供了很多解决方案(包括丑陋的黑客)之后已经有一段时间了。
由于此 answer 中的答案,我设法解决了我最近遇到的相同问题,并认为这可能对访问此页面的人有所帮助。整个想法是每次在任何父元素或子元素上调用 evenet.target
时都将其存储在 ondrageenter
中。然后在 ondragleave
检查当前目标 (event.target
) 是否等于您存储在 ondragenter
中的对象。
这两个匹配的唯一情况是当您的拖动离开浏览器窗口时。
这工作正常的原因是当鼠标离开一个元素(比如 el1
)并进入另一个元素(比如 el2
)时,首先调用 el2.ondragenter
,然后调用 el1.ondragleave
。只有当拖动离开/进入浏览器窗口时,el2.ondragenter
和 el1.ondragleave
中的 event.target
才会是 ''
。
这是我的工作示例。我已经在 IE9+、Chrome、Firefox 和 Safari 上测试过它。
(function() {
var bodyEl = document.body;
var flupDiv = document.getElementById('file-drop-area');
flupDiv.onclick = function(event){
console.log('HEy! some one clicked me!');
};
var enterTarget = null;
document.ondragenter = function(event) {
console.log('on drag enter: ' + event.target.id);
enterTarget = event.target;
event.stopPropagation();
event.preventDefault();
flupDiv.className = 'flup-drag-on-top';
return false;
};
document.ondragleave = function(event) {
console.log('on drag leave: currentTarget: ' + event.target.id + ', old target: ' + enterTarget.id);
//Only if the two target are equal it means the drag has left the window
if (enterTarget == event.target){
event.stopPropagation();
event.preventDefault();
flupDiv.className = 'flup-no-drag';
}
};
document.ondrop = function(event) {
console.log('on drop: ' + event.target.id);
event.stopPropagation();
event.preventDefault();
flupDiv.className = 'flup-no-drag';
return false;
};
})();
这是一个简单的html页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Multiple File Uploader</title>
<link rel="stylesheet" href="my.css" />
</head>
<body id="bodyDiv">
<div id="cntnr" class="flup-container">
<div id="file-drop-area" class="flup-no-drag">blah blah</div>
</div>
<script src="my.js"></script>
</body>
</html>
通过适当的样式,我所做的是在将文件拖入屏幕时使内部 div (#file-drop-area) 更大,以便用户可以轻松地将文件拖放到适当的位置。
==
而不是 ===
。您正在比较事件目标对象引用,因此 ===
也可以正常工作。
onDragEnter
并在 onDragLeave
中检查一样简单。这应该是公认的答案。
在这里,最简单的跨浏览器解决方案(认真):
jsfiddle <-- 尝试在框内拖动一些文件
你可以这样做:
var dropZone= document.getElementById('box');
var dropMask = document.getElementById('drop-mask');
dropZone.addEventListener('dragover', drag_over, false);
dropMask.addEventListener('dragleave', drag_leave, false);
dropMask.addEventListener('drop', drag_drop, false);
简而言之,您在 dropzone 内创建了一个“蒙版”,继承了宽度和高度,绝对位置,将在拖动开始时显示。因此,在显示该掩码后,您可以通过在其上附加其他拖放事件来完成此操作。
离开或放下后,您只需再次隐藏面具。简单且没有并发症。
(Obs.:Greg Pettit 的建议——你必须确保面具悬停在整个盒子上,包括边框)
#drop::before
或 ::after
时,此解决方案效果很好。另外,请注意,有时在快速拖动时,“dragleave”会在“dragenter”完成之前触发。如果 dragenter 添加类/伪元素,然后 dragleave 删除,这可能会导致问题。
解决此问题的“正确”方法是禁用放置目标的子元素上的指针事件(如@HD 的回答)。 Here's a jsFiddle I created which demonstrates this technique。不幸的是,这在 IE11 之前的 Internet Explorer 版本中不起作用,因为它们didn't support pointer events。
幸运的是,我想出了一个在旧版本的 IE 中确实工作的解决方法。基本上,它涉及识别和忽略在拖动子元素时发生的 dragleave
事件。因为 dragenter
事件是在父节点上的 dragleave
事件之前在子节点上触发的,所以可以将单独的事件侦听器添加到每个子节点,从而从放置目标添加或删除“ignore-drag-leave”类。然后放置目标的 dragleave
事件侦听器可以简单地忽略此类存在时发生的调用。这是一个jsFiddle demonstrating this workaround。它在 Chrome、Firefox 和 IE8+ 中经过测试和工作。
更新:
我使用功能检测创建了 a jsFiddle demonstrating a combined solution,如果支持,则使用指针事件(当前为 Chrome、Firefox 和 IE11),如果指针事件支持不可用,则浏览器回退到向子节点添加事件 (IE8-10) .
dragleave
事件在 Chrome、Firefox 和 IE11+ 中触发的信息。除了 IE10,我还更新了我的其他解决方法以支持 IE8 和 IE9。仅在拖动“Drag me”链接时才添加 dropzone 效果是有意的。其他人可以根据需要随意更改此行为以支持他们的用例。
到目前为止,这个相当简单的解决方案对我有用,假设您的事件单独附加到每个拖动元素。
if (evt.currentTarget.contains(evt.relatedTarget)) {
return;
}
如果您使用的是 HTML5,则可以获取父级的 clientRect:
let rect = document.getElementById("drag").getBoundingClientRect();
然后在 parent.dragleave() 中:
dragleave(e) {
if(e.clientY < rect.top || e.clientY >= rect.bottom || e.clientX < rect.left || e.clientX >= rect.right) {
//real leave
}
}
border-radius
的元素上使用时,将指针移近角落会实际上离开元素,但这段代码仍会认为我们在里面(我们已经离开了元素但我们仍然在边界矩形)。然后根本不会调用 dragleave
事件处理程序。
一个非常简单的解决方案是使用 pointer-events
CSS property。只需在每个子元素的 dragstart 上将其值设置为 none
。这些元素将不再触发鼠标相关事件,因此它们不会在它们上方捕捉鼠标,因此不会触发父元素上的 dragleave。
完成拖动时不要忘记将此属性设置回 auto
;)
一个简单的解决方案是在子组件中添加css规则pointer-events: none
,以防止触发ondragleave
。参见示例:
function enter(event) { document.querySelector('div').style.border = '1px 蓝色虚线'; } function leave(event) { document.querySelector('div').style.border = ''; } div { 边框:1px 虚线银;填充:16px;边距:8px; }文章{边框:1px纯银;填充:8px;边距:8px; } p { 指针事件:无;背景:白烟; }
child没有触发dragleave
dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
.no-pointer-events {pointer-events: none;}
然后将 no-pointer-events
添加到每个子元素。完毕。
问题是当鼠标移到子元素前面时会触发 dragleave
事件。
我尝试了各种方法来检查 e.target
元素是否与 this
元素相同,但没有得到任何改进。
我解决这个问题的方法有点小技巧,但是 100% 有效。
dragleave: function(e) {
// Get the location on screen of the element.
var rect = this.getBoundingClientRect();
// Check the mouseEvent coordinates are outside of the rectangle
if(e.x > rect.left + rect.width || e.x < rect.left
|| e.y > rect.top + rect.height || e.y < rect.top) {
$(this).removeClass('red');
}
}
if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
e.x
和 e.y
。
非常简单的解决方案:
parent.addEventListener('dragleave', function(evt) {
if (!parent.contains(evt.relatedTarget)) {
// Here it is only dragleave on the parent
}
}
您可以从 jQuery source code 获得一些灵感,在 Firefox 中修复它:
dragleave: function(e) {
var related = e.relatedTarget,
inside = false;
if (related !== this) {
if (related) {
inside = jQuery.contains(this, related);
}
if (!inside) {
$(this).removeClass('red');
}
}
}
不幸的是,它在 Chrome 中不起作用,因为 relatedTarget
在 dragleave
事件中似乎不存在,并且我假设您在 Chrome 中工作,因为您的示例在 Firefox 中不起作用。 Here's a version 实现了上述代码。
在这里,Chrome 的解决方案:
.bind('dragleave', function(event) {
var rect = this.getBoundingClientRect();
var getXY = function getCursorPosition(event) {
var x, y;
if (typeof event.clientX === 'undefined') {
// try touch screen
x = event.pageX + document.documentElement.scrollLeft;
y = event.pageY + document.documentElement.scrollTop;
} else {
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { x: x, y : y };
};
var e = getXY(event.originalEvent);
// Check the mouseEvent coordinates are outside of the rectangle
if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
console.log('Drag is really out of area!');
}
})
border-radius
时,这会导致问题,正如我在上面对@azlar 答案的评论中所解释的那样。
我遇到了同样的问题,并尝试使用 pk7s 解决方案。它可以工作,但如果没有任何额外的 dom 元素,它可以做得更好。
基本上这个想法是一样的——在可放置区域上添加一个额外的不可见覆盖。只允许在没有任何额外 dom 元素的情况下执行此操作。这是 CSS 伪元素发挥作用的部分。
Javascript
var dragOver = function (e) {
e.preventDefault();
this.classList.add('overlay');
};
var dragLeave = function (e) {
this.classList.remove('overlay');
};
var dragDrop = function (e) {
this.classList.remove('overlay');
window.alert('Dropped');
};
var dropArea = document.getElementById('box');
dropArea.addEventListener('dragover', dragOver, false);
dropArea.addEventListener('dragleave', dragLeave, false);
dropArea.addEventListener('drop', dragDrop, false);
CSS
此规则将为可放置区域创建一个完全覆盖的覆盖。
#box.overlay:after {
content:'';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
}
这是完整的解决方案:http://jsfiddle.net/F6GDq/8/
我希望它可以帮助任何有同样问题的人。
这是使用 document.elementFromPoint 的另一个解决方案:
dragleave: function(event) {
var event = event.originalEvent || event;
var newElement = document.elementFromPoint(event.pageX, event.pageY);
if (!this.contains(newElement)) {
$(this).removeClass('red');
}
}
希望这可行,这是一个fiddle。
event.clientX, event.clientY
,因为它们是相对于视口而不是页面的。我希望这可以帮助另一个迷失的灵魂。
不确定这是否是跨浏览器,但我在 Chrome 中进行了测试,它解决了我的问题:
我想在整个页面上拖放一个文件,但是当我拖动子元素时会触发我的dragleave。我的解决方法是查看鼠标的 x 和 y:
我有一个覆盖整个页面的 div,当页面加载时我隐藏它。
当您拖动文档时,我会显示它,当您放在父级上时它会处理它,当您离开父级时,我会检查 x 和 y。
$('#draganddrop-wrapper').hide();
$(document).bind('dragenter', function(event) {
$('#draganddrop-wrapper').fadeIn(500);
return false;
});
$("#draganddrop-wrapper").bind('dragover', function(event) {
return false;
}).bind('dragleave', function(event) {
if( window.event.pageX == 0 || window.event.pageY == 0 ) {
$(this).fadeOut(500);
return false;
}
}).bind('drop', function(event) {
handleDrop(event);
$(this).fadeOut(500);
return false;
});
我偶然发现了同样的问题,这是我的解决方案——我认为这比上面要容易得多。我不确定它是否是跨浏览器(可能取决于冒泡顺序)
为简单起见,我将使用 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 在父上之前,我们也会稍微延迟它以颠倒顺序,因此首先删除类,然后在一毫秒后重新响应。
另一种工作解决方案,更简单一些。
//Note: Due to a bug with Chrome the 'dragleave' event is fired when hovering the dropzone, then
// we must check the mouse coordinates to be sure that the event was fired only when
// leaving the window.
//Facts:
// - [Firefox/IE] e.originalEvent.clientX < 0 when the mouse is outside the window
// - [Firefox/IE] e.originalEvent.clientY < 0 when the mouse is outside the window
// - [Chrome/Opera] e.originalEvent.clientX == 0 when the mouse is outside the window
// - [Chrome/Opera] e.originalEvent.clientY == 0 when the mouse is outside the window
// - [Opera(12.14)] e.originalEvent.clientX and e.originalEvent.clientY never get
// zeroed if the mouse leaves the windows too quickly.
if (e.originalEvent.clientX <= 0 || e.originalEvent.clientY <= 0) {
clientX
。当然,我的元素是 position:absolute
console.log(e.clientX + "/" + e.clientY ); if (e.clientX == 0 && e.clientY == 0 ) { console.log('REAL leave'); }
我已经编写了一个名为 Dragster 的小库来处理这个确切的问题,除了在 IE 中默默地什么都不做(它不支持 DOM 事件构造函数,但是使用 jQuery 的自定义事件编写类似的东西会很容易) )
只需检查拖动的元素是否是子元素,如果是,则不要删除您的“dragover”样式类。非常简单,对我有用:
$yourElement.on('dragleave dragend drop', function(e) {
if(!$yourElement.has(e.target).length){
$yourElement.removeClass('is-dragover');
}
})
我编写了一个名为 drip-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
}
});
我找到了一个解决这个问题的简单方法,所以分享它。在我的情况下效果很好。
jsfiddle 试试看。
您实际上只能通过 dragenter
事件来实现这一点,甚至不需要注册 dragleave
。您所需要的只是在您的放置区周围有一个无放置区域,仅此而已。
您还可以有嵌套的拖放区,这非常有效。也检查一下nested dropzones。
$('.dropzone').on("dragenter", function(e) {
e.preventDefault();
e.stopPropagation();
$(this).addClass("over");
$(".over").not(this).removeClass("over"); // in case of multiple dropzones
});
$('.dropzone-leave').on("dragenter", function(e) {
e.preventDefault();
e.stopPropagation();
$(".over").removeClass("over");
});
// UPDATE
// As mar10 pointed out, the "Esc" key needs to be managed,
// the easiest approach is to detect the key and clean things up.
$(document).on('keyup', function(e){
if (e.key === "Escape") {
$(".over").removeClass("over");
}
});
dragend
事件来处理此问题,但我需要对其进行测试。
dragend
事件,因此使用 dragend
事件会使逻辑复杂化,因此简单的解决方案是检测 Escape你说的关键。我已经更新了答案。
在花了这么多小时后,我得到了这个建议完全按照预期工作。我只想在文件被拖动时提供提示,并且文档拖动,dragleave 在 Chrome 浏览器上导致痛苦的闪烁。
这就是我解决它的方法,也为用户提供了适当的提示。
$(document).on('dragstart dragenter dragover', function(event) {
// Only file drag-n-drops allowed, http://jsfiddle.net/guYWx/16/
if ($.inArray('Files', event.originalEvent.dataTransfer.types) > -1) {
// Needed to allow effectAllowed, dropEffect to take effect
event.stopPropagation();
// Needed to allow effectAllowed, dropEffect to take effect
event.preventDefault();
$('.dropzone').addClass('dropzone-hilight').show(); // Hilight the drop zone
dropZoneVisible= true;
// http://www.html5rocks.com/en/tutorials/dnd/basics/
// http://api.jquery.com/category/events/event-object/
event.originalEvent.dataTransfer.effectAllowed= 'none';
event.originalEvent.dataTransfer.dropEffect= 'none';
// .dropzone .message
if($(event.target).hasClass('dropzone') || $(event.target).hasClass('message')) {
event.originalEvent.dataTransfer.effectAllowed= 'copyMove';
event.originalEvent.dataTransfer.dropEffect= 'move';
}
}
}).on('drop dragleave dragend', function (event) {
dropZoneVisible= false;
clearTimeout(dropZoneTimer);
dropZoneTimer= setTimeout( function(){
if( !dropZoneVisible ) {
$('.dropzone').hide().removeClass('dropzone-hilight');
}
}, dropZoneHideDelay); // dropZoneHideDelay= 70, but anything above 50 is better
});
当鼠标指针离开目标容器的拖动区域时触发“dragleave”事件。
这很有意义,因为在许多情况下,只有父级可能是可丢弃的,而不是后代。我认为 event.stopPropogation() 应该已经处理了这种情况,但似乎它没有解决问题。
上面提到的一些解决方案似乎适用于大多数情况,但在那些不支持 dragenter / dragleave 事件的孩子的情况下失败,例如 iframe。
1 解决方法是检查 event.relatedTarget 并验证它是否位于容器内,然后像我在这里所做的那样忽略 dragleave 事件:
function isAncestor(node, target) {
if (node === target) return false;
while(node.parentNode) {
if (node.parentNode === target)
return true;
node=node.parentNode;
}
return false;
}
var container = document.getElementById("dropbox");
container.addEventListener("dragenter", function() {
container.classList.add("dragging");
});
container.addEventListener("dragleave", function(e) {
if (!isAncestor(e.relatedTarget, container))
container.classList.remove("dragging");
});
你可以找到一个工作小提琴here!
我知道这是一个老问题,但想添加我的偏好。我通过在比你的内容更高的 z-index 添加类触发的 css :after 元素来处理这个问题。这将过滤掉所有的垃圾。
.droppable{
position: relative;
z-index: 500;
}
.droppable.drag-over:after{
content: "";
display:block;
position:absolute;
left:0;
right:0;
top:0;
bottom:0;
z-index: 600;
}
然后只需在您的第一个 dragenter 事件上添加拖动类,并且不再有任何子元素触发该事件。
dragEnter(event){
dropElement.classList.add('drag-over');
}
dragLeave(event){
dropElement.classList.remove('drag-over');
}
我遇到了类似的问题——当悬停子元素时,我的用于在身体的 dragleave 事件上隐藏 dropzone 的代码被触发,使得 dropzone 在 Google Chrome 中闪烁。
我能够通过安排隐藏dropzone的函数而不是立即调用它来解决这个问题。然后,如果另一个 dragover 或 dragleave 被触发,计划的函数调用将被取消。
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);
解决了 ..!
为 ex 声明任何数组:
targetCollection : any[]
dragenter: function(e) {
this.targetCollection.push(e.target); // For each dragEnter we are adding the target to targetCollection
$(this).addClass('red');
},
dragleave: function() {
this.targetCollection.pop(); // For every dragLeave we will pop the previous target from targetCollection
if(this.targetCollection.length == 0) // When the collection will get empty we will remove class red
$(this).removeClass('red');
}
无需担心子元素。
即使在阅读了所有这些答案之后,我也为此苦苦挣扎,并认为我可以与您分享我的解决方案,因为我认为它可能是更简单的方法之一,尽管有些不同。我的想法是完全省略 dragleave
事件侦听器,并在触发每个新的 dragenter 事件时对 dragleave 行为进行编码,同时确保不会触发 dragenter 事件。
在下面的示例中,我有一个表格,我希望能够通过拖动 & 来相互交换表格行内容。丢弃 API。在 dragenter
上,应将 CSS 类添加到您当前拖动元素的行元素中,以突出显示它,而在 dragleave
上,应删除此类。
例子:
非常基本的 HTML 表格:
<table>
<tr>
<td draggable="true" class="table-cell">Hello</td>
</tr>
<tr>
<td draggable="true" clas="table-cell">There</td>
</tr>
</table>
以及添加到每个表格单元格中的 dragenter 事件处理函数(除了 dragstart
、dragover
、drop
和 dragend
处理程序,它们不是针对此问题的,因此此处不复制):
/*##############################################################################
## Dragenter Handler ##
##############################################################################*/
// When dragging over the text node of a table cell (the text in a table cell),
// while previously being over the table cell element, the dragleave event gets
// fired, which stops the highlighting of the currently dragged cell. To avoid
// this problem and any coding around to fight it, everything has been
// programmed with the dragenter event handler only; no more dragleave needed
// For the dragenter event, e.target corresponds to the element into which the
// drag enters. This fact has been used to program the code as follows:
var previousRow = null;
function handleDragEnter(e) {
// Assure that dragenter code is only executed when entering an element (and
// for example not when entering a text node)
if (e.target.nodeType === 1) {
// Get the currently entered row
let currentRow = this.closest('tr');
// Check if the currently entered row is different from the row entered via
// the last drag
if (previousRow !== null) {
if (currentRow !== previousRow) {
// If so, remove the class responsible for highlighting it via CSS from
// it
previousRow.className = "";
}
}
// Each time an HTML element is entered, add the class responsible for
// highlighting it via CSS onto its containing row (or onto itself, if row)
currentRow.className = "ready-for-drop";
// To know which row has been the last one entered when this function will
// be called again, assign the previousRow variable of the global scope onto
// the currentRow from this function run
previousRow = currentRow;
}
}
代码中留下了非常基本的注释,因此该代码也适合初学者。希望这会帮助你!请注意,您当然需要将我上面提到的所有事件侦听器添加到每个表格单元格中才能正常工作。
这是另一种基于事件时间的方法。
从子元素调度的 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
}
})
pimvdb..
为什么不尝试使用 drop 而不是 dragleave。它对我有用。希望这可以解决您的问题。
请检查 jsFiddle:http://jsfiddle.net/HU6Mk/118/
$('#drop').bind({
dragenter: function() {
$(this).addClass('red');
},
drop: function() {
$(this).removeClass('red');
}
});
$('#drag').bind({
dragstart: function(e) {
e.allowedEffect = "copy";
e.setData("text/plain", "test");
}
});
不定期副业成功案例分享
pointer-events: none
。当我开始拖动时,我附加了一个具有此属性的类,当拖动结束时,我删除了该类。在 Safari 和 Chrome 上运行良好,但在 Firefox 上运行良好。dragenter
中,我将event.currentTarget
保存在一个新变量dragEnterTarget
中。只要设置了dragEnterTarget
,我就会忽略进一步的dragenter
事件,因为它们来自子级。在所有dragleave
事件中,我检查dragEnterTarget === event.target
。如果这是错误的,则该事件将被忽略,因为它是由孩子触发的。如果这是真的,我将dragEnterTarget
重置为undefined
。