我正在为我的Web组件实现Orchestrator模式,如下所示:
<body>
<my-controller>
<div>
<my-list>
<span>
<my-item></my-item>
</span>
</my-list>
</div>
</my-controller>
</body>
我创建的所有自定义元素都使用const root = super.attachShadow({mode: "open"}); root.appendChild(...);
来使用Shadow DOM。
从我的内部Web组件,我想在my-controller
中访问我的connectedCallback()
组件:
public connectedCallback(): void
{
if (this.isConnected)
{
for (let node = this.parentElement; node; node = node.parentElement)
if (node instanceof ContainerBase)
{
this._service = (<ContainerBase>node).GetService(this);
break;
}
if (this._service) this.Reset();
else throw new ReferenceError(`${this.nodeName.toLowerCase()}: Couldn't find host element while connecting to document.`);
}
}
奇怪的是:我只能到达直接的父网页控件。
因此,如果在connectedCallback()
上调用<my-list>
,我可以到达<my-controller>
,但如果在connectedCallback()
上调用<my-item>
,我只能到达<span>
。当我用<my-list>
开始搜索时,我甚至无法到达<my-item>
。
甚至当我在调用connectedCallback()
之后走DOM树时,当我开始在<span>
时,我无法超越<my-item>
。
这是故意吗?
为什么我可以从第一个嵌套的组件到达外部Web组件,而我无法从第二个嵌套的Web组件到达第一个嵌套的Web组件?
如何从任何嵌套级别完全上升DOM树?
使用Shadow DOM定义自定义元素内容时,可以创建不同的DOM树。 Shadow DOM是一个没有根元素的文档片段。
因此,您无法通过parentElement
属性向上移动DOM来达到其(直观)祖先。
要到达Shadow DOM的主机元素,而不是use getRootNode()
与host
相结合。
来自<my-item>
的connectedCallback()
方法:
connectedCallback() {
var parent = this.getRootNode().host
console.log( parent.localNode ) // my-list
}
如果你想要一个祖先,你可以试试这个recursive function。
通常认为内部/子元素能够从外部/父元素访问数据是不好的做法。
使用来自外部组件捕获的内部组件的自定义事件更安全且更少耦合。
内部组件将调度一个事件,让外部元素知道它需要某些东西,然后外部组件可以调用函数或在内部组件上设置参数。
你可以这样做:
儿童元素
connectedCallback() {
this.dispatch(new CustomEvent('request-service'));
}
set service(val) {
this._service = val;
}
get service() {
return this._service;
}
服务要素:
constructor() {
super();
this.addEventListener('request-service',
evt => {
evt.target.service = this.GetService(evt.target);
}
);
}
ShadowRoot
不是一个元素,ShadowRoot
的parentNode
不是它的主元素。你需要照顾他们。
function shadowIncludingParentElement(node) {
if (node.parentElement)
return node.parentElement;
if (!node.parentNode)
return null;
if (node.parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE)
return node.parentNode.host;
return null;
}
...
for (let node = this.parentElement; node;
node = shadowIncludingParentElement(node)) {
...
}