Прослушиватель событий на родительском узле также срабатывает на всех дочерних узлах.

Я пишу перетаскиваемый класс в vanilla js с нуля (это НЕ связано с перетаскиванием jQuery), и он отлично работает, только мои слушатели запускаются для дочерних элементов выбранного узла. Я пробовал e.stopPropagation() внутри слушателей, но, похоже, это не меняет результат.

Вот фрагмент кода из класса:

setListeners() {

    const targets = document.getElementsByClassName("-jsDraggable");

    [...targets].forEach(elem => {

        elem.addEventListener("mousemove", e => this.handleMouseMove(e));
        elem.addEventListener("mousedown", e => this.handleMouseDown(e));
        elem.addEventListener("mouseup", e => this.handleMouseUp(e));

    });
}

... и например:

<ul class="-jsDraggable">
    <li>No drag</li>
    <li>No drag</li>
    <li class="-jsDraggable">Can drag this item</li>
    <li>No drag</li>
</ul>

В этом сценарии я бы хотел, чтобы были затронуты как <ul class="-jsDraggable">...</ul>, так и одиночный <li class="-jsDraggable">...</li> внутри, где перетаскивание из одного из других элементов списка просто перетаскивало основной элемент <ul class="-jsDraggable">...</ul>.

Однако независимо от того, с каким дочерним узлом я взаимодействую, независимо от того, насколько далеко он находится в DOM по сравнению с узлом с классом, он запускает обработчики.

Любая помощь будет принята с благодарностью!

РЕДАКТИРОВАТЬ: Еще один пример для уточнения:

<article class="-jsDraggable">
    <header>
        <h1>Heading</h1>
    </header>
    <main>
        <p>...</p>
    </main>
</article>

Независимо от того, куда я перетаскиваю этот элемент <article>, он должен перетаскивать всю группу HTML как единое целое (поскольку родитель всего этого имеет класс). Однако в настоящее время он действует примерно так:

<article class="-jsDraggable">
    <header class="-jsDraggable">
        <h1 class="-jsDraggable">Heading</h1>
    </header>
    <main class="-jsDraggable">
        <p class="-jsDraggable">...</p>
    </main>
</article>

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


person ReactionJackson    schedule 06.02.2019    source источник
comment
Остановка распространения событий должна работать. Не могли бы вы сделать демонстрацию вашей попытки его использования?   -  person Bergi    schedule 07.02.2019
comment
Я думаю, вы бы хотели, чтобы событие mousedown запускалось только тогда, когда e.target является .-jsDraggable, иначе обработчик также будет запускаться, когда щелкнутый дочерний элемент не является .-jsDraggable. (не совсем уверен, что вы хотите делать с mousemove и mouseup, вы можете или не хотите проверять там) (живой фрагмент будет иметь большое значение для возможности отладки проблемы)   -  person CertainPerformance    schedule 07.02.2019
comment
Я хочу, чтобы перемещался только элемент, содержащий класс -jsDraggable, но в настоящее время каждый отдельный узел, вложенный в узел с классом, также можно перетаскивать.   -  person ReactionJackson    schedule 07.02.2019
comment
Простое прекращение распространения событий само по себе будет иметь свои проблемы.   -  person Kevin B    schedule 07.02.2019


Ответы (2)


Я решил это, это не сработало, потому что я устанавливал текущий элемент как this.elem = e.target вместо this.elem = e.currentTarget. После этого e.stopPropagation() в mousedown заработало как положено.

Спасибо, что познакомили меня с e.currentTarget, я не знал об этом, пока не прочитал справку в этой теме.

person ReactionJackson    schedule 06.02.2019

Если я правильно понимаю ваш вопрос, то один из вариантов (среди прочих) состоит в том, чтобы различать источник события и фильтровать обработку событий с помощью полей currentTarget и target объекта event следующим образом:

if(event.currentTarget === event.target) {
   /* The user is interacting with the actual -jsDraggable element */
}
else {
   /* The user is interacting with a descendant of a -jsDraggable element */
}

Фактически это означает, что если элемент, порождающий событие (т. е. currentTarget), является тем же элементом, к которому был привязан обработчик события (т. е. target), то мы определяем, что событие связано с самим фактическим элементом -jsDraggable. (и не потомок). Этот подход дополняет ваш текущий код, как показано ниже:

function handleMouseMove(event) {
  if(event.currentTarget === event.target) {
    console.log(Date.now(), 'Mouse move over draggable');
  }
}
function handleMouseDown(event) {
  if(event.currentTarget === event.target) {
    console.log(Date.now(), 'Drag start on draggable');
  }
}
function handleMouseUp(event) {
  if(event.currentTarget === event.target) {
    console.log(Date.now(), 'Drag end on draggable');
  }
}

function setListeners() {

    const targets = document.querySelectorAll(".-jsDraggable");

    targets.forEach(elem => {

        elem.addEventListener("mousemove", e => handleMouseMove(e));
        elem.addEventListener("mousedown", e => handleMouseDown(e));
        elem.addEventListener("mouseup", e => handleMouseUp(e));

    });
}

setListeners();
ul {
  padding:1rem;
  background:lightgreen;
}
li {
  padding:1rem;
  background:pink;
}
.-jsDraggable {
  background:lightblue;
}
<ul class="-jsDraggable">
    <li>No drag</li>
    <li>No drag</li>
    <li class="-jsDraggable">Can drag this item</li>
    <li>No drag</li>
</ul>

person Dacre Denny    schedule 06.02.2019
comment
Это действительно останавливает срабатывание дочерних элементов, однако родитель также не срабатывает (ничего не происходит). Желаемый эффект заключается в том, что даже при взаимодействии с дочерним узлом, если родительский узел имеет класс, он должен активировать функциональность для родительского узла. - person ReactionJackson; 07.02.2019