Это действительно не должен быть ответ, но есть только так много, что вы можете написать в одном комментарии
Я не думаю, что вы делаете свойставить под сомнение справедливость, имея в своем названии слова «хорошая практика».Этот тип подразумевает, что в большинстве случаев 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>