Shadow DOM

<div id="host-section" class="host">
  <h1 class="title">Title</h1>
  <p class="paragraph">Paragraph</p>
</div>

Problems

A component/webview

.host { display: flex; }
.title { font-size: 32px; }
.paragraph { color: red; }
<div id="host-section" class="host">
  <AWebView />
  <h1 class="title">My Title</h1>
</div>

B component/webview

.host { display: block; }
.title { font-size: 48px !important; }

CSS module can help ?

<div
  id="host-section"
  class="member_host__8vfa7">
  <h1
    class="member_title__a783f">
    Title
  </h1>
  <p
    class="member_paragraph__913fg">
    Paragraph
  </p>
</div>

Problems

Member component

.member_host__8vfa7 { display: flex; }
.member_title__a783f { font-size: 32px; }
.member_paragraph__913fg { color: red; }
<div
  id="host-section"
  class="member_host__3gh8l">
  <AWebView />
  <h1
    class="member_title__a783f">
    My Title
  </h1>
</div>

Another Member component

.member_host__3gh8l { display: block; }
.member_title__a783f { font-size: 48px !important; }

CSS module can help ?

Problems is that we need an isolated environment for our components

Shadow DOM

  • Isolated DOM
  • Scoped CSS
  • Composition
  • Simplified CSS
  • Productivity
<body>
  <header>
    <h1>Hello DOM</h1>
  </header>
</body>

Background on DOM

const header = document.createElement('header');
const h1 = document.createElement('h1');
h1.textContent = 'Hello DOM';
header.appendChild(h1);
document.body.appendChild(header);

=

Document

body

header

h1

head

Hello DOM

(極簡化的 DOM tree)

Create a shadow DOM

const header = document.createElement('header');
const shadowRoot = header.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>';

Document

body

header

h1

head

Hello DOM

(極簡化的 DOM tree)

Document

body

header

h1

head

Hello Shadow DOM

(極簡化的 DOM tree)

Shadow host (DOM tree 中)

Shadow Root

Shadow Tree

Valid elements we can attachShadow

<img /> ?

Given children is non-sense

<a /> ?

Security reasons

Create a shadow DOM for custom element

// Use custom elements API v1 to register a new HTML tag and define its JS behavior
// using an ES6 class. Every instance of <fancy-tab> will have this same prototype.
customElements.define('fancy-tabs', class extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to <fancy-tabs>.
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
      <div id="tabs">...</div>
      <div id="panels">...</div>
    `;
  }
  ...
});
<fancy-tabs>
  #shadow-root(open)
    <style>#tabs { ... }</style>
    <div id="tabs">...</div>
    <div id="panels">...</div>
</fancy-tabs>
<fancy-tabs></fancy-tabs>

inside your html, render <fancy-tabs>

Slot

Slots are placeholders inside your component that users can fill with their own markup.

 

By defining one or more slots, you invite outside markup to render in your component's shadow DOM

CSS :host

This has no effect when used outside a shadow DOM.

<my-component centered>
  #shadow-root(open)
    <style>
      :host { display: flex; }
      :host([centered]) { align-items: center; }
    </style>
</my-component>

<template>

Holding HTML that is not to be rendered immediately when a page is loaded but may be instantiated subsequently during runtime using JavaScript.

<template id="element-template">
  <style>
    :host { display: grid; }
    .title { font-size: 24px; }
  </style>
  <h1 class="title">
    My Title
  </h1>
</template>
<h1 class="title">My Title</h1>

In your .html

In your browser

References

Shadow DOM

By Travor Lee

Shadow DOM

  • 148