有一些尝试回答这个问题:here,here和here。然而,没有一个答案给出了坚实的回应。我不是指event
阶段capture
,bubble
和target
以及stopPropagation()
如何影响整体事件。我正在寻找一个案例,将stopPropagation()
添加到DOM节点将有利于整体代码?
这真的不应该是一个答案,但只有你可以写一个评论
我不认为你的问题是通过在其标题中加上“良好实践”这个词来表达你的问题。这种意味着在大多数情况下,stopPropagation
是不好的做法。这类似于说eval是邪恶的。它完全用错误的教条主义来消除它的任何合法用例。
我从未发现自己处于使用stopPropagation
感觉不像是一种解决方法以避免解决实际问题的情况。
在理想的世界中,应用程序是由较小的组件构建的,这些组件本身很少但是可重复使用且可组合。为了实现这一点,配方很简单但很难执行:每个组件都必须对外部世界一无所知。
因此,如果一个组件需要使用stopPropagation()
,那只能是因为它知道链上的某些东西会破坏,或者它会使你的应用程序进入不良状态。
在这种情况下,您应该问自己这是否不是设计问题的症状。也许你需要一个能够协调和管理孩子事件的组件?
您还应该考虑防止事件传播可能导致其他组件行为异常的事实。典型的示例是当您在其外部单击时关闭的下拉列表。如果停止该点击,您的下拉列表可能永远不会关闭。
将事件视为数据来源。您不想丢失数据。 Au逆转!放手吧,让它免费;)
虽然我没有看到使用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
只有在点击实际上来自节点本身而不是来自其中一个子节点时才会执行。
当事件遍历DOM时,Event接口的currentTarget只读属性标识事件的当前目标。它始终引用事件处理程序附加到的元素,而不是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>