JQuery:更改dragstart事件上的DOM会立即触发dragend

JQuery: Changing the DOM on dragstart event fires dragend immediately?

本文关键字:dragend DOM 更改 dragstart 事件 JQuery      更新时间:2023-09-26

我遇到了Chrome和Opera的一个bug,我想知道它是否已知,如果已知,有解决方案吗?

如果我更改dragstart事件的DOM,它会立即引发dragend事件?!这是一个bug还是背后有什么原因?只在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)技巧

这种情况的解决方法是0时间将长时间运行的操作块封装在setTimeout函数中。

(你可以认为这是告诉浏览器中运行代码的部分,根本没有时间!,但不是字面意思。)

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

在您的特定情况下,诀窍是setTimeout将DOM更改的执行推迟到比dragend事件处理程序晚的时间(至少0),从而给人一种dragend事件在DOM更改后启动的印象。

@DVK在这里有一篇很棒的帖子解释了为什么setTimeout(fn, 0)有时很有用。请检查他(在Chrome中)的JSfiddle。

更新

正如@MojoJojo和@Pradeep所指出的,Webkit浏览器(尤其是旧版本的Chrome)似乎存在drag事件问题。然而,我试图在Chrome版本47.0.2526.106(截至2016年1月11日的最新版本)中重现该错误,drag事件在没有任何异常的情况下启动。

无论如何,即使存在错误,setTimeout技巧仍然适用于解决该问题

只需尝试将DOM操作置于dragstart的drag事件中即可:)

我认为这是一个小故障/bug,否则我们可以说浏览器就是这样工作的,因为我们正在进行DOM操作,这可能会导致重新绘制整个DOM,在dragStart事件中操作DOM会导致这个问题,将DOM操作改为dragEnter可能会解决这个问题。

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

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

你使用的是什么版本的Chrome?

没有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/

我发现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');
});