JQuery: Изменение DOM при событии dragstart сразу же запускает dragend?

я столкнулся с ошибкой для Chrome и Opera, и мне хотелось бы узнать, известно ли это, и если да, есть ли решение?

Если я меняю DOM на событие dragstart, он сразу же запускает событие 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");

Ответы

Ответ 1

Кажется, что разные браузеры проявляют разные поведения в отношении длительных операций.

В JavaScript есть один поток, который выполняет все ваши инструкции в одной очереди. Каждый элемент очереди запускается последовательно, и как только элемент завершается, следующий элемент (из очереди) захватывается и запускается.

Преступником для длительной работы является изменение, которое вы пытаетесь внести в DOM (которому, как я полагаю, предшествует тяжелый поиск с использованием find(), который будет запускать манипуляцию DOM для каждого согласованного элемента).

Что происходит, когда вы перетаскиваете элемент, это то, что все строки кода в обработчике dragstart, а при остановке перетаскивания обработчик dragend помещается в очередь сообщений соответственно для последовательного запуска. Однако манипуляция DOM занимает больше времени (возможно, несколько миллисекунд больше) для выполнения, чем выполнение обработчика dragend, прежде чем вы перестанете перетаскивать, и, следовательно, он выглядит так, как будто dragend запущен слишком рано.

Примечание. Иногда блок кода создает новое событие и, следовательно, переносится в конец очереди событий браузера (или, возможно, где-то после элемента, который выполняется), что приводит к более позднему исполнению. (Я полагаю, что характер этого отличается от браузера браузером.)

Часть манипулирования DOM вашего кода может столкнуться с такой проблемой в Chrome и Opera, хотя я не уверен.

setTimeout(fn, 0) Trick

Обходной путь для таких ситуаций - обернуть длинный рабочий блок в функции setTimeout со временем 0.

(Вы можете подумать об этом, указав браузеру на запуск части вашего кода, в не время вообще!, но не буквально.)

После того, как блок кода выполнил выполнение, браузер будет искать доступные элементы, ожидающие запуска, а те, которые с setTimeout или setInterval будут перенесены в очередь в первый доступный момент.

В вашем конкретном случае трюк заключается в том, что setTimeout отменил выполнение изменения DOM на более позднее время (не менее 0 секунд), чем обработчик события dragend, тем самым создавая впечатление, как будто dragend событие, произошедшее после изменения DOM.

Есть отличный пост от @DVK здесь, объясняющий, почему setTimeout(fn, 0) иногда полезно. Просмотрите JSfiddle им (в Chrome).

Update

Как указано @MojoJojo и @Pradeep, похоже, что браузеры Webkit (более старые версии Chrome в частности) имеют проблемы с событиями drag. Однако я попытался воспроизвести ошибку в Chrome версии 47.0.2526.106 (новейшая версия от 11 января 2016 года), а события drag запускались без каких-либо нарушений.

В любом случае, даже если возникла ошибка, трюк setTimeout по-прежнему применяется в качестве надлежащего обходного пути для проблемы.

Ответ 2

Я думаю, что это глюк/ошибка, иначе мы можем сказать, что так работает браузер, так как мы делаем манипуляции с DOM, что может привести к перерисовке всей DOM снова, манипулирование DOM в событии dragStart вызывает эту проблему, сдвиг DOM-манипуляция с dragEnter может решить проблему.

Другим решением может быть установка setTimeout, о котором вы уже упоминали.

Ответ 3

Просто попробуйте помещать DOM-манипуляцию в перетащить событие dragstart вместо:)

Ответ 4

без jquery works

    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/

Ответ 6

Драйдер был уволен, потому что вы перемещаете перетаскиваемый элемент. вы не меняете положение перетаскивания, а перетаскиваете его.

Создайте два div. Один будет перетаскиваться, а другой будет выполнять эффект перетаскивания.

Ответ 7

Я обнаружил, что обходное решение setTimeout() очень плохо работает в Firefox. Например, когда вы сразу же отпустите кнопку мыши после перетаскивания, контекст в dragstart может быть уже недоступен, и может произойти сбой script. Особенно при использовании в сочетании с событием dragend.

Я создал следующую процедуру, которая значительно повысила надежность моего script:

$('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');

});