已经有一些尝试来回答这个问题:
here 、 here 和 here 。然而,没有一个答案给出了可靠的回应。我不是指 event
阶段 capture
、 bubble
和 target
以及 stopPropagation()
如何影响整个事件。我正在寻找将 stopPropagation()
添加到 DOM 节点将有利于整体代码的情况?
最佳答案
这真的不应该是一个答案,但你只能在一个评论中写这么多
我认为您在标题中使用“良好实践”一词并没有公正地对待您的问题。这种暗示在大多数情况下,stopPropagation
是不好的做法。这类似于说 eval 是邪恶的。它以错误的教条主义完全抹杀了它的任何合法用例。
我从来没有发现自己处于使用 stopPropagation
感觉不像是避免解决真正问题的解决方法的情况。
在理想的世界中,应用程序由较小的组件构建而成,这些组件本身几乎没有什么作用,但具有高度的可重用性和可组合性。为此,秘诀很简单,但执行起来却非常困难:每个组件必须对外部世界一无所知。
因此,如果一个组件需要使用 stopPropagation()
,那只能是因为它知道链上的某些东西会中断,或者它会使您的应用程序进入不良状态。
在这种情况下,您应该问自己这是否不是设计问题的症状。也许您需要一个组件来编排和管理其子项的事件?
您还应该考虑这样一个事实,即阻止事件传播可能会导致其他组件行为不端。典型的例子是一个下拉菜单,当你点击它的外部时它就会关闭。如果该点击停止,您的下拉菜单可能永远不会关闭。
将事件视为数据源。您不想丢失数据。反之!让它去,让它自由;)
虽然我不认为使用 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()
调用,则将无法赢得这场比赛,因为点击不会冒泡到游戏处理程序以发出游戏结束的信号!解决方案一:确定点击来源
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
仅在点击实际上来自节点本身而不是来自其子节点之一时才会执行。https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
方案二:使用事件委托(delegate)
您实际上并不需要三个事件处理程序。只需在
#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>
关于javascript - 什么时候使用 stopPropagation() 的好习惯?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54815439/