-
Notifications
You must be signed in to change notification settings - Fork 382
Multiple Shadow Roots as "a Shadow Root hosts another Shadow Root"
WIP. This will be a page which is a follow-up for https://www.w3.org/Bugs/Public/show_bug.cgi?id=23887#c191
Caution: This page is intended for implementors of Shadow DOM, rather than for web-dev.
Have you wondered a reason why a tree of trees tree of trees defines that older shadow tree is the parent tree of the younger tree? Shouldn't they are sibling trees? If you are interested in the reason, please continue reading.
Shadow DOM spec has a concept of Multiple Shadow Roots: One element can host more than one shadow roots.
However, I can explain Multiple Shadow Roots without this Multiple-ness. Welcome to this mirrored world. In this different world, there is no Multiple Shadow Roots concept. An Element can host only one Shadow Root. Instead of that, the difference comes here: "A Shadow Root can host another Shadow Root."
Both worlds have an equivalent power. Let me explain the equivalence by examples.
Suppose an element, host
, hosts three Shadow Roots, [SR1, SR2, SR3], where SR1 is the oldest shadow root, SR3 is the youngest shadow root.
var sr1 = host.createShadowRoot();
var sr2 = host.createShadowRoot();
var sr3 = host.createShadowRoot();
// host ==(hosts)==> [sr1, sr2, sr3]
Remember that Shadow Root also supports createShadowRoot() in this mirrored world. You can write the equivalent as follows:
var sr1 = host.createShadowRoot();
var sr2 = sr1.createShadowRoot();
var sr3 = sr2.createShadowRoot();
Here, there are three shadow hosts, host
, sr1
and sr2
. Each hosts sr1
, sr2
and sr3
, respectively.
// host ==(hosts)==> sr1 ==(hosts)==> sr2 ==(hosts) ==> sr3
Have you noticeed the similality between 1 and 2 in the following?
- The relationship between Shadow Host and it's (oldest) Shadow Root
- The relationship between the older Shadow Root and it's younger Shadow Root
There is no significant difference between A and B. Let's think how browser works for them:
"I'm a Browser. I'm now about to render an element,
A
. Wait. This elmentA
is hosting the (oldest) shadow root,SR1
. Okay, I won't travese the child nodes ofA
. Instead, let me go down toSR1
and continue rendering from there"
"I'm a Browser. I'm now about to render an shadow root,
SR1
. Wait. ThisSR1
hosts the (younger) shadow root,SR2
. Okay, I won't traverse the child nodes ofSR1
. Instead, let me go down toSR2
and continue rendering from there"
"I'm a Browser. I'm now about to render an shadow root,
SR2
. Wait. ThisSR2
hosts the (youngest) shadow root,SR3
. Okay, I won't render the child nodes ofSR2
. Instead, let me go down toSR3
and continue rendering from there"
That means, in any worlds, instead of (child nodes of) an element A, (a subtree of) SR3 is going to be rendered. Nothing has changed from the user's perspective.
Good question. The difference sounds like a kind of an implementation details. Who should care for that?
I care. I've seen a lot of benefits from this different view when I was thinking an algorithm in the Shadow DOM spec. This actually made the spec much simpler, though I guess you didn't think so. :)
For example, thanks to the definition of tree of trees, I don't have to mention about Multiple Shadow Roots at defining Event Retargeting algorithm, Event RelatedTarget Retargeting algorithm and so on. I can use a familiar term like the lowest common ancestor tree.
Now I've found that event path calculation algorithm can get benefits too. See the comment.
There, we don't need to distinguish between content insertion points and shadow insertion points at all. {older shadow roots, younger shadow roots, oldest shadow roots, youngest shadow roots} neither.
The benefits are: Those algorithms don't have to pay attention to the existence of "Multiple Shadow Roots". Those algorithm don't change at all if we were to remove "Multiple Shadow Roots" from the spec. Benefits.
Imagine that we are living in the mirrored world, where ShadowRoot supports ShadowRoot.createShadowRoot() API. No one in this world uses a term of "Multiple Shadow Roots".
In this mirrored world, if you were to support Element.createShadowRoot() which behaves as the current spec defines, we could implement it easily as follows:
function createShadowRootWrapperForMultipleShadowRootsWorld() {
if (this.shadowRoot()) {
return this.shadowRoot().createShadowRootWrapperForMultipleShadowRootsWorld();
} else {
return this.createShadowRoot();
}
}
The similar thing also applies to the current Element.shadowRoot()
API, which returns the youngest shadow root, rather than oldest shadow root. The following is the implementation:
function youngestShadowRootWrapperForMultipleShadowRootsWorld() {
var sr = this.shadowRoot();
if (!sr) return null;
while (sr.shadowRoot()) {
sr = sr.shadowRoot();
}
return sr;
}
}
You can think the current APIs are just wrappers to support pseudo "Multiple Shadow Roots" in the mirrored world.