Когда рекомендуется использовать stopPropagation ()? - PullRequest
0 голосов
/ 21 февраля 2019

Были попытки ответить на этот вопрос: здесь , здесь и здесь .Однако ни один из ответов не дает убедительного ответа.Я не имею в виду event фазы capture, bubble и target и то, как stopPropagation() влияет на общее событие.Я ищу случай, когда добавление stopPropagation() к узлу DOM принесет пользу всему коду?

1 Ответ

0 голосов
/ 22 февраля 2019

Это действительно не должен быть ответ, но есть только так много, что вы можете написать в одном комментарии


Я не думаю, что вы делаете свойставить под сомнение справедливость, имея в своем названии слова «хорошая практика».Этот тип подразумевает, что в большинстве случаев stopPropagation является плохой практикой.Это похоже на то, что eval - это зло .Он полностью отмахивается от любых законных вариантов его использования с неуместным догматизмом.

Я никогда не оказывался в ситуации, когда использование stopPropagation не казалось обходным решением, чтобы избежать решения реальной проблемы.

В идеальном мире приложения состоят из небольших компонентов, которые сами по себе мало что делают, но их можно многократно использовать и комбинировать.Чтобы это работало, рецепт прост, но очень сложен в исполнении: каждый компонент не должен ничего знать о внешнем мире.

Поэтому, если компонент должен использовать stopPropagation(), это может быть только потому, что он знает, чточто-то дальше по цепочке сломается или что оно переведет ваше приложение в нежелательное состояние.

В этом случае вам следует спросить себя, не является ли это симптомом проблемы проектирования.Возможно, вам нужен компонент, который управляет и управляет событиями его дочерних элементов?

Вам также следует учитывать тот факт, что предотвращение распространения событий может привести к неправильной работе других компонентов.Классический пример - это раскрывающийся список, который закрывается, когда вы щелкаете за его пределами.Если этот щелчок остановлен, раскрывающийся список может никогда не закрыться.

Думайте о событиях как об источниках данных.Вы не хотите терять данные. Au contraire! Отпусти, отпусти;)

Хотя я не вижу использования stopPropagation как плохой или злой практики, я просто не думаю, что это когда-либо необходимо.


Пример: как избежать использования stopPropagation

В этом примере мы создаем очень простую игру: если вы нажмете на красную область, вы потеряетеНа зеленой территории вы выиграли.Игра заканчивается после того, как сделан щелчок.

const onClick = (selector, handler) => {
  document.querySelector(selector).addEventListener('click', handler);
};

onClick('#game', () => console.log('game over'));
onClick('#red', () => console.log('you lost'));
onClick('#green', () => console.log('you won'));
#red, #green { width: 50px; height: 50px; display: inline-block; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
  <div id="red"></div>
  <div id="green"></div>
</div>

Теперь давайте представим, что есть разные уровни, на которых красные и зеленые блоки расположены случайным образом.На уровне # 42 красный блок содержит зеленый.

const onClick = (selector, handler) => {
  document.querySelector(selector).addEventListener('click', handler);
};

onClick('#game', () => console.log('game over'));
onClick('#red', () => console.log('you lost'));
onClick('#green', () => console.log('you won'));
#red, #green { max-width: 100px; padding: 10px; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
  <div id="red">
    <div id="green"></div>
  </div>
</div>

Как вы можете видеть, когда вы нажимаете на зеленую область, вы оба выигрываете и проигрываете одновременно!И если вы должны были сделать stopPropagation() вызов в зеленом обработчике, выиграть эту игру не получится, так как щелчок не попадет в обработчик игры, сигнализирующий об окончании игры!

Решение 1: определить источник щелчка

const filter = handler => ev =>
  ev.target === ev.currentTarget ? handler(ev) : null;

const onClick = (selector, handler) => {
  document.querySelector(selector).addEventListener('click', handler);
};

onClick('#game', () => console.log('game over'));
onClick('#red', filter(() => console.log('you lost')));
onClick('#green', () => console.log('you won'));
#red, #green { max-width: 100px; padding: 10px; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
  <div id="red">
    <div id="green"></div>
  </div>
</div>

Функция ключа filter.Он будет гарантировать, что handler будет выполняться только в том случае, если щелчок фактически исходил от самого узла, а не от одного из его дочерних элементов.

Свойство currentTarget, доступное только для чтения, интерфейса интерфейса Event определяет текущийцель для события, поскольку событие проходит через DOM.Он всегда относится к элементу, к которому был прикреплен обработчик события, в отличие от Event.target, который идентифицирует элемент, на котором произошло событие.

https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget

Решение 2: использовать делегирование событий

На самом деле вам не нужны три обработчика событий.Просто установите один на узле #game.

const onClick = (selector, handler) => {
  document.querySelector(selector).addEventListener('click', handler);
};

onClick('#game', (ev) => {
  if (ev.target.id === 'red') {
    console.log('you lost');
  } else if (ev.target.id === 'green') {
    console.log('you won');
  }
  console.log('game over');
});
#red, #green { max-width: 100px; padding: 10px; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
  <div id="red">
    <div id="green"></div>
  </div>
</div>
...