Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Shadow DOM: Why Do We Only Allow Children in Insertion Points

#Shadow DOM: Why Do We Only Allow Children in Insertion Points

First, a little snippet of code

<some-app>
	...
<my-tabs>
	<section>
		<h2>Foo</h2>
			<p>Paragraph 1</p>
			<p>Paragraph 2</p>
	</section>
	...
</my-tabs>
...
</some-app>

##Glimpse of Excitement It does seem pretty cool that you could reach into your subtree and grab at any descendent as an insertion point candidate. Imagine how neat it would be if <my-tabs> could take <h2> and <p>s inside each <section> and plop them into its insertion points?

<template id="my-tabs-shadow-tree">
	...
	<content select="section>h2"></content>
	<content select="section>p"></content>
	...
</template>

##The Chaos Theory But what happens if <section> also has a shadow tree? How does this impact the ability of <my-tabs> to grab <section>'s children as insertion point candidates?

<template id="section-shadow-tree">
	...
	<content></content>
	...
</template>

What if, at the same time, <some-app> is trying to grab all <section>s for some special insertion point?

<template id="some-app-shadow-tree">
	...
	<content select="section"></content>
	...
	<!-- all other stuff goes here -->
	<content></content>
</template

To provide a full-fidelity solution, we need to define the system that describes the ability of an element in a tree to select its descendants as insertion point candidates in relation to this ability of other elements in the tree.

Intuitively, we need a stable ordering. We could start with a simple tree order: ancestors and preceding siblings always get the first go at the selection.

In the snippet above, <my-tabs> will get to choose from its descendants before <section> does. And <some-app> will get to choose before <my-tabs>.

We also need to decide what happens with an element once it is chosen as an insertion point candidate. We could let this choice be overridden (last one wins) or not (first one wins). For example, let's make it "last one wins".

##It's war For the snippet above, <some-app> will first pick out the <section>, then <my-tab> will grab <title> and <p>s, and then <section>, thus leaving us in the following situation:

  • <some-app> has <section>, which surprisingly renders, because even though all of its children have been borrowed by <my-tabs> at first, the <section> got it all back, since it was given the last go at the selection.
  • <my-tabs> doesn't render anything, because <section> plucked all of its children from <my-tabs>s grubby little hands.

What happens now if <my-tabs> decides to simply remove and re-insert <section> in the same place? Are the <section>'s children now rendered in the <my-tabs> or does it still render nothing?

What's the outcome of moving <section> outside of the <some-app> subtree?

We're starting to see the problem: despite a stable system, the elements appear at war for their descendants. In fact, sticking with the default ordering looks like a bad choice for an element developer: the winning design is the one where an element defensively hunts to have the very last (or very first) go at picking the elements.

This is bad. We've effectively sacrificed composability on the altar of flexibility -- not to mention increased complexity of the overall system.

##Your children are yours Instead, we opted into a much simpler design. Only element's children are available as the candidates for this element's shadow tree. In situations where you need to reach for grandchildren, rely on the behavior of grandchildren's parent.

As a benefit, you as a shadow host, get complete control of your children. There will be no one trying to grab them and hide them in dark places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.