有一些尝试回答这个问题的尝试:在这里,这里和这里。但是,没有一个答案给出了可靠的回应。我不是指event
阶段capture
,bubble
和target
,以及stopPropagation()
如何影响整个事件。我正在寻找将stopPropagation()
添加到DOM节点的情况,将受益于整体代码?
这确实不应该是答案,但是您只能在单个注释中写下很多东西
我认为您的标题"好实践"一词并不认为您是在做问题正义。这种意味着在大多数情况下,stopPropagation
是不良习惯。这类似于说评估是邪恶的。它完全摆脱了任何合法用例,教条主义放错了。
我从来没有发现自己处于使用stopPropagation
的情况下,避免解决现实问题。
在理想的世界中,应用程序是由较小的组件构建的,这些组件几乎没有单独使用,但可以重复使用且可组合。为此,配方很简单,但很难执行:每个组件必须对外界一无所知。
因此,如果组件需要使用stopPropagation()
,则只能是因为它知道链条进一步的某些东西会破裂或将您的应用程序置于不良状态。
在这种情况下,您应该问自己这是否不是设计问题的症状。也许您需要一个组件来协调和管理孩子的事件?
您还应该考虑这样一个事实,即防止事件的传播可能导致其他组件表现不佳。经典的示例是一个下拉示例,当您单击外部时关闭。如果点击停止,您的下拉可能永远不会关闭。
将事件视为数据源。您不想丢失数据。 au contaire!放开它,放开;)
虽然我认为使用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仅读取属性标识事件的当前目标,因为事件越过DOM。它始终是指附加事件处理程序的元素,而不是事件。标识,该元素标识了事件发生的元素。
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>