Я много боролся с этим, даже после прочтения всех этих ответов, и подумал, что могу поделиться с вами своим решением, потому что я подумал, что это может быть один из более простых подходов, хотя и несколько отличный. Моя мысль заключалась в том, чтобы просто полностью исключить прослушиватель событий dragleave
и закодировать поведение dragleave с каждым новым запуском события dragenter, при этом удостоверившись, что события dragenter не будут запускаться без надобности.
В моем примере ниже у меня есть таблица, в которой я хочу иметь возможность обмениваться содержимым строк таблицы друг с другом с помощью API перетаскивания. На dragenter
класс CSS должен быть добавлен к элементу строки, в которую вы в данный момент перетаскиваете свой элемент, чтобы выделить его, а на dragleave
этот класс должен быть удален.
Пример:
Очень простая таблица HTML:
<table>
<tr>
<td draggable="true" class="table-cell">Hello</td>
</tr>
<tr>
<td draggable="true" clas="table-cell">There</td>
</tr>
</table>
И функция обработчика событий dragenter, добавленная в каждую ячейку таблицы (кроме обработчиков dragstart
, dragover
, drop
и dragend
, которые не относятся к этому вопросу, поэтому здесь не копируются):
/*##############################################################################
## Dragenter Handler ##
##############################################################################*/
// When dragging over the text node of a table cell (the text in a table cell),
// while previously being over the table cell element, the dragleave event gets
// fired, which stops the highlighting of the currently dragged cell. To avoid
// this problem and any coding around to fight it, everything has been
// programmed with the dragenter event handler only; no more dragleave needed
// For the dragenter event, e.target corresponds to the element into which the
// drag enters. This fact has been used to program the code as follows:
var previousRow = null;
function handleDragEnter(e) {
// Assure that dragenter code is only executed when entering an element (and
// for example not when entering a text node)
if (e.target.nodeType === 1) {
// Get the currently entered row
let currentRow = this.closest('tr');
// Check if the currently entered row is different from the row entered via
// the last drag
if (previousRow !== null) {
if (currentRow !== previousRow) {
// If so, remove the class responsible for highlighting it via CSS from
// it
previousRow.className = "";
}
}
// Each time an HTML element is entered, add the class responsible for
// highlighting it via CSS onto its containing row (or onto itself, if row)
currentRow.className = "ready-for-drop";
// To know which row has been the last one entered when this function will
// be called again, assign the previousRow variable of the global scope onto
// the currentRow from this function run
previousRow = currentRow;
}
}
В коде оставлены очень простые комментарии, так что этот код подходит и для новичков. Надеюсь, это поможет вам! Обратите внимание, что вам, конечно, нужно будет добавить все прослушиватели событий, о которых я упоминал выше, в каждую ячейку таблицы, чтобы это работало.
person
DevelJoe
schedule
02.06.2020
dragleave
все еще запускается. - person pimvdb   schedule 11.05.2012enter
события на элементе, а при возникновении события - создание абсолютно позиционированного оверлея над данным элементом с толькоleave
прослушивателем событий. Это устраняет необходимость отключения событий указателя на дочерних элементах (наложение берет на себя каждое событие перетаскивания), и вы уверены, чтоleave
будет запущен, когда он должен быть. У меня была эта проблема в компоненте vue: всегда запускалleave
сразу послеenter
, не понимал, почему на самом деле (у детей события указателя были установлены на none). - person Przemysław Melnarowicz   schedule 09.03.2020