Skip to content

Instantly share code, notes, and snippets.

@justinfagnani
Last active August 1, 2017 02:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinfagnani/936791248120749ff1f8188f1f4064d9 to your computer and use it in GitHub Desktop.
Save justinfagnani/936791248120749ff1f8188f1f4064d9 to your computer and use it in GitHub Desktop.
<!doctype html>
<html>
<head>
<script src="shadow-root.js"></script>
</head>
<div>
<div slot="main">
I'm some projected content.
</div>
<shadow-root>
<template>
I'm some template content in a shadow-root.
<slot name="main"></slot>
<div>
<div>
Nested project content
</div>
<shadow-root>
<template>
<slot></slot>
</template>
</shadow-root>
</div>
</template>
</shadow-root>
</div>
</html>
class ShadowRootElement extends HTMLElement {
connectedCallback() {
const template = this.children[0];
if (template && template.tagName === 'TEMPLATE') {
const parent = this.parentElement;
let shadow = parent.shadowRoot;
if (!shadow) {
shadow = parent.attachShadow({mode: 'open'});
}
const content = document.importNode(template.content, true);
shadow.appendChild(content);
}
}
}
customElements.define('shadow-root', ShadowRootElement);
@treshugart
Copy link

treshugart commented Dec 6, 2016

As a result of rendering, I'd much rather have something closer to what the composed tree would look like. For example:

<x-app>
  <shadow-root style="display: contents">
    I'm some template content in a shadow-root.
    <slot name="main">
      <div slot="main" slotted>I'm some projected content.</div>
    </slot>
    <div>
      <x-descendant>
        <shadow-root style="display: contents">
          <slot>
            <div slotted>Nested project content</div>
            <div>Default content</div>
          </slot>
        </shadow-root>
      </x-descendant>
    </div>
  </shadow-root>
</x-app>

In this example, the <shadow-root /> element is similar to yours but in this case it's simply replaced by a shadowRoot. The display: contents style is added to not mess up styling. The <slot /> elements contain nodes that have a slotted attribute which indicate they should be moved to light DOM, thus distributed. Any content in slots that don't have that attribute are left as default content.

The idea behind this is so it looks like it would if it were hydrated without executing any JS. If you have to execute JS sync, I think that would negate some of the [arguable] benefits of server rendering. One may as well just stamp out the custom element with light DOM. Making this look good before hydration would be where zeit/styled-jsx comes in. After it doesn't really matter, but it works in both cases.

Terms

  • hydrated initialised with a shadow root

DISCLAIMER

This isn't a solid proposal and I'm still fleshing it out in my head.

@justinfagnani
Copy link
Author

The problem with trying to send over the distributed tree is that you lose information, specifically the light DOM of elements, that you'd have to reconstruct somehow. This includes undistributed nodes, ordering of nodes distributed to different slots. On top of that you have to implement style scoping.

What I'm trying to show here with <shadow-root> is polyfill a frequent request for declarative shadow DOM. The JS that executes here to build the tree of trees is much, much simpler than any real framework + element definitions would be, and the JS payload is very small. Of course there'd have to be some measurement, but I bet this type of tree is constructed extremely efficiently.

@justinfagnani
Copy link
Author

I think where this idea would go from here is:

  • Add a template attribute to <shadow-root> so that multiple shadow roots can share the same template. It would reference a template by id.
  • Have <shadow-root> remove itself when it stamps.
  • Build a serializer that takes a complex Shadow DOM based page, like YouTube, and outputs a page using <shadow-root>, de-duplicating shadow root contents to use the same template instance.
  • Measure payload size and rendering time of the complex page.

@treshugart
Copy link

treshugart commented Dec 7, 2016

The problem with trying to send over the distributed tree is that you lose information, specifically the light DOM of elements, that you'd have to reconstruct somehow. This includes undistributed nodes, ordering of nodes distributed to different slots.

All very good points. Where I was trying to take it is be able to render a tree that looks the same before the shadow root is stamped out so that the initialisation work can be done separately. From what I can gather this is the biggest argument for SSR: perceived performance. Overall performance and TTI don't seem to be affected much by it, but it's an argument that comes up a lot. Whether or not it's valid in the grand scheme of things is somewhat irrelevant to the fact that the industry is flocking to it.

On top of that you have to implement style scoping.

If styled-jsx is used (not a general solution, though), it would be able to scope styles prior to stamping out and distribution.

I like your solution here for declarative shadow roots, but I don't see how it can help the problem I'm trying to solve. Though, the more I try and solve it, I don't think it's solvable with shadow DOM.

@treshugart
Copy link

treshugart commented May 26, 2017

@justinfagnani I was just doing some work on the serialisation aspect of this and mini-filling shadow DOM (to work with Undom) on the server and was wondering if you knew if crawlers will index content. I know querySelector calls won't go into them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment