StencilJS中composed:false的自定义事件可在Shadow DOM外捕获的问题
为什么StencilJS中设置composed: false的自定义事件仍能在Shadow DOM外部被捕获?
我在StencilJS中使用@Event装饰器派发自定义事件,明确设置了{composed: false}(这也是默认值),按预期应该只能在Shadow DOM范围内监听该事件,但实际却能在Shadow DOM外部通过document.addEventListener捕获到它,有人能解释这种行为吗?
Stencil官方文档说明
composed?: boolean;
一个布尔值,指示事件是否可以跨越Shadow DOM和常规DOM之间的边界冒泡。
组件代码(component.tsx)
@Event() rate: EventEmitter<number>; private handleStarClick(starIndex: number) { this.rate.emit(starIndex + 1); }
页面代码(index.html)
<!DOCTYPE html> <html dir="ltr" lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" href="/build/web-components.css" /> <title>Stencil Component Starter</title> <script type="module" src="/build/web-components.esm.js"></script> <script nomodule src="/build/web-components.js"></script> <script type="module"> document.querySelector("my-rating-stars").texts=["Terrible", "Bad", "Okay", "Good", "Excellent"]; // 监听自定义事件 document.addEventListener("rate", (e) => console.log(e)) </script> </head> <body> <my-rating-stars></my-rating-stars> </body> </html>
原因解析
问题出在StencilJS的@Event装饰器默认行为上:当你使用@Event()而不指定target时,事件默认是派发在组件本身(即自定义元素<my-rating-stars>),而非Shadow DOM内部的元素上。
因为事件的目标是属于常规DOM的自定义元素本身,所以即使设置了composed: false,事件也会在常规DOM的冒泡链中传播,自然能被document捕获到。composed属性控制的是事件是否能从Shadow DOM内部跨越到外部,而这里事件根本就没从Shadow DOM里派发出来,所以composed的设置不会影响它的传播范围。
如果想让事件只能在Shadow DOM内部被监听,需要修改@Event的配置,指定事件派发到Shadow DOM根节点,并确保composed: false:
@Event({ target: 'shadow', composed: false }) rate: EventEmitter<number>;
这样事件会被派发在组件的Shadow根上,由于composed: false,它无法跨越到常规DOM,此时只有在Shadow DOM内部添加的监听器才能捕获到这个事件。
内容的提问来源于stack exchange,提问作者saksonia
相关产品推荐
相关产品推荐

