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/
Ответ 5
Это похоже на проблему в Chrome
https://groups.google.com/a/chromium.org/forum/?fromgroups=#!msg/chromium-bugs/YHs3orFC8Dc/ryT25b7J-NwJ
Какую версию Chrome вы используете?
Ответ 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');
});