JQuery:在dragstart事件上更改DOM会立即触发dragend吗?
我遇到了Chrome和Opera的一个错误,我想知道它是否已知,如果有,是否有解决方案?
如果我在dragstart事件上更改DOM,它会立即触发dragend事件?! 这是一个错误还是背后有一些原因? 只发生在Chrome和Opera中。 Firefox有效。
我很感激每一个答案。
$('body').on({ dragstart: function(e) { dragProfilefieldSrcElformid = $(this).attr("data-profilefieldid-formid"); e.dataTransfer = e.originalEvent.dataTransfer; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', $(this).attr("data-profilefieldid")); // Changing the DOM, fires the dragend Event in Chrome? $("#plugin_loginlogout_pfcontainer_" + dragProfilefieldSrcElformid).find(".plugin_loginlogout_pf_entryfield").addClass("highlight"); // This doesn't work in Chrome and Opera, but in Firefox }, dragend: function() { console.log("dragend"); } ".plugin_loginlogout_pf");
编辑:
将DOM更改放在setTimeout函数中似乎可以解决问题!
似乎不同的浏览器对长时间运行的操作表现出不同的行为。
JavaScript有一个线程,可以在同一个队列中运行所有指令。 每个队列项按顺序运行,一旦项完成执行,就会抓取并运行下一个项( 来自队列 )。
长时间运行操作的罪魁祸首是你试图给DOM带来的变化( 我假设之前是使用find()
进行大量搜索,它将为每个匹配的元素运行DOM操作 )。
拖动元素时会发生什么,即
dragstart
处理程序中的所有代码行,当您停止拖动时,dragend
处理程序将分别推送到消息队列,以便顺序执行。 然而,在停止拖动之前, DOM操作比执行dragend
处理程序花费更多时间(可能多几毫秒),因此,看起来好像dragend
太快了。
注意:有时代码块会创建一个新事件 ,因此会被推送到浏览器事件队列的末尾( 或者可能在正在运行的项目之后的某个位置 ),从而导致稍后执行。 (我想它的性质因浏览器而异。)
代码的DOM操作部分可能在Chrome和Opera中遇到这样的问题,尽管我不确定。
setTimeout(fn, 0)
Trick
这种情况的解决方法是将长时间运行的操作块包含在setTimeout
函数中0
次。
(您可以将此视为告诉浏览器在任何时间 运行代码的一部分!但不是字面意思。)
一旦代码块完成执行,浏览器将搜索等待运行的可用项,并且具有setTimeout
或setInterval
将在第一个可用时刻被推送到队列。
在您的特定情况下,诀窍是setTimeout
将 DOM更改的执行推迟到dragend
事件处理程序的稍后时间( 至少0
秒 ),从而给出了在DOM更改后触发dragend
事件的印象。
@DVK有一篇很棒的文章,解释了为什么setTimeout(fn, 0)
有时很有用 。 也可以通过他(在Chrome中)检查JSfiddle 。
更新
正如@MojoJojo和@Pradeep所指出的那样,似乎Webkit浏览器(特别是旧版本的Chrome)存在drag
事件的问题。 但是,我尝试重现Chrome版本47.0.2526.106(截至2016年1月11日的最新版本)中的错误,并且在没有任何违规行为的情况下触发了drag
事件。
无论如何,即使有错误, setTimeout
技巧仍然适用于该问题的正确解决方法。
我认为这是一个小故障/错误或者我们可以说这是浏览器如何工作,因为我们正在进行DOM操作,这可能导致再次重绘整个DOM,在dragStart事件中操纵DOM导致此问题,将DOM操作转移到dragEnter可以解决问题。
另一种解决方案是设置您已经提到的setTimeout。
只是尝试将你的DOM操作放在dragstart的拖动事件中:)
没有jquery的作品
var draggable = document.getElementById('draggable'), test = document.getElementById('test'); document.addEventListener("dragend", function(event) { // reset the transparency console.log('dragend'); }, false); draggable.addEventListener('dragstart', function(event) { test.style.color = 'red'; draggable.style.backgroundColor = 'gray'; }, false);
这似乎是Chrome中的一个问题https://groups.google.com/a/chromium.org/forum/?fromgroups=#!msg/chromium-bugs/YHs3orFC8Dc/ryT25b7J-NwJ
您使用的是哪个版本的Chrome?
由于你正在移动可拖动物品,因此被解雇了。 拖动时不要改变可拖动位置。
创建两个div。 一个是拖动,另一个是拖动效果。
我发现setTimeout()解决方法在Firefox中非常错误。 例如,拖动后立即释放鼠标按钮时,可能无法再访问dragstart中的上下文,并且脚本可能会崩溃。 特别是与dragend事件结合使用时。
我创建了以下过程,为我的脚本提供了更多的可靠性:
$('html').on('dragstart', '.somelement', function(e){ // Bind 'drag' event only once (gets triggered every 350ms) $('html').one('drag', function(){ // modify DOM here }); }); $('html').on('dragend', '.somelement', function(e){ // Edge might fire 'dragend' before executing the 'drag' event within 'dragstart' $('html').unbind('drag'); });