JQuery:在dragstart事件上更改DOM会立即触发dragend吗?

时间:2015-02-09 11:30:31

标签: javascript jquery

我遇到了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函数中似乎可以解决问题!

7 个答案:

答案 0 :(得分:14)

似乎不同的浏览器对长时间运行的操作表现出不同的行为。

JavaScript有一个线程可以在同一个队列中运行所有指令。每个队列项按顺序运行,一旦项完成执行,就会抓取并运行下一个项( from queue )。

长时间运行操作的罪魁祸首是你试图给DOM带来的变化(我假设之前是使用find()进行大量搜索,它将为每个匹配的元素运行DOM操作)。

  

当你拖动元素时会发生什么,dragstart处理程序中的所有代码行,当你停止拖动时,dragend处理程序分别被推送到消息队列以便成为连续执行。但是,在停止拖动之前, DOM操作比执行dragend处理程序花费更多时间(可能多几毫秒),因此,它看起来好像{{ 1}}太快了。

注意:有时代码块 会创建一个新事件 ,因此被推送到浏览器事件队列的末尾( 或者可能在正在运行的项目之后的某个地方),导致稍后执行。 (我认为它的性质因浏览器而异。)

DOM操作部分代码可能在Chrome和Opera中面临这样的问题,但我不确定。

dragend技巧

此类情况的解决方法是在setTimeout(fn, 0)函数中包装长时间运行的操作块,时间为setTimeout

(您可以将此视为告诉浏览器运行代码的一部分,在 根本没有时间! ,而不是从字面上看。)

一旦代码块执行完毕,浏览器将搜索等待运行的可用项目,并且0setTimeout的代码将在第一个可用时刻推送到队列中

在您的特定情况下,诀窍是setInterval 推迟将DOM更改的执行推迟到以后的时间(至少setTimeout )而不是0事件处理程序,从而给出了在DOM更改后触发dragend事件的印象。

@DVK here有一篇很棒的文章解释为什么dragend有时候很有用。请同时查看JSfiddle(在Chrome中)。

更新

正如@MojoJojo和@Pradeep所指出的,Webkit浏览器(特别是旧版本的Chrome)似乎存在setTimeout(fn, 0)事件的问题。但是,我尝试重现Chrome版本47.0.2526.106(截至2016年1月11日的最新版本)中的错误,并且drag事件在没有任何违规行为的情况下被解雇。

无论如何,即使有错误,drag技巧仍然适用于此问题的正确解决方法。

答案 1 :(得分:1)

我认为这是一个小故障/错误或者我们可以说这是浏览器如何工作,因为我们正在进行DOM操作,这可能导致再次重绘整个DOM,在dragStart事件中操纵DOM导致此问题,转移对dragEnter进行DOM操作可以解决问题。

另一个解决方案是设置你已经提到的setTimeout。

答案 2 :(得分:0)

尝试将您的DOM操作放在dragstart的拖动事件中:)

答案 3 :(得分:0)

没有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);

http://jsfiddle.net/nwkv75ot/4/

答案 4 :(得分:0)

这似乎是Chrome中的一个问题 https://groups.google.com/a/chromium.org/forum/?fromgroups=#!msg/chromium-bugs/YHs3orFC8Dc/ryT25b7J-NwJ

您使用的是哪个版本的Chrome?

答案 5 :(得分:0)

因为你正在移动可拖动项目而被解雇了。拖动时不要改变可拖动的位置。

创建两个div。一个是拖动,另一个是拖动效果。

答案 6 :(得分:0)

我发现setTimeout()解决方法在Firefox中非常错误。例如,当您在拖动后立即释放鼠标按钮时, dragstart 中的上下文可能不再可访问,并且脚本可能会崩溃。特别是与 dragend 事件结合使用时。

我创建了以下过程,为我的脚本提供了更高的可靠性:

DELETE FROM [MYDB].[dbo].[MYSIGN] where USERID in
(select t.USERID from [MYDB].[dbo].[MYUSER] t where Surname = 'Rossi');