I'm implementing an Orchestrator pattern for my web components, like this:
<body>
<my-controller>
<div>
<my-list>
<span>
<my-item></my-item>
</span>
</my-list>
</div>
</my-controller>
</body>
All custom elements I created utilize Shadow DOM using const root = super.attachShadow({mode: "open"}); root.appendChild(...);
.
From my inner web components I want to reach my my-controller
component in 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.`);
}
}
The strange thing is: I can only reach the immediate parent web control.
So, if connectedCallback()
is called on <my-list>
I can reach <my-controller>
, but if connectedCallback()
is called on <my-item>
I only reach <span>
. I can't even reach <my-list>
when I'm starting my search with <my-item>
.
Even when I walk the DOM tree after connectedCallback()
is called, I cannot reach beyond <span>
when I start at <my-item>
.
Is this by intention?
Why can I reach an outer web component from the first nested one while I cannot reach the first nested web component from the second nested one?
How can I go up the DOM tree completely, from any nested level?
When you define a Custom Element content whith a Shadow DOM, you create a distinct DOM tree. The Shadow DOM is a DocumentFragment with no root element.
As a consequence, you cannot reach its (intuitive) ancestor simply by walking the DOM up by the parentElement
property.
To reach the host element of a Shadow DOM, instead use getRootNode()
combined with host
.
From <my-item>
's connectedCallback()
method:
connectedCallback() {
var parent = this.getRootNode().host
console.log( parent.localNode ) // my-list
}
If you want to get an ancestor, you could try this recursive function.