Jason Pan

潘忠显 / 2021-04-17


“JavaScript 工作原理”系列文章是翻译和整理自 SessionStack 网站的 How JavaScript works。因为博文发表于2017年,部分技术或信息可能已经过时。本文英文原文链接,作者 Alexander Zlatkov,翻译 潘忠显

How JavaScript works: the internals of Shadow DOM + how to build self-contained components

Alexander Zlatkov

Alexander ZlatkovFollow

Jun 21, 2018 · 11 min read

img

This is post # 17 of the series dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript application that needs to be robust and highly-performant to help users see and reproduce their web app defects real-time.

If you missed the previous chapters, you can find them here:

Overview

Web Components is a suite of different technologies that allows you to create reusable custom elements.Their functionality is encapsulated away from the rest of your code, and you can utilize them in your web apps.

There are 4 Web Component standards:

In this article, we’ll focus on the Shadow DOM.

Shadow DOM is designed as a tool for building component-based apps. It offers solutions to common problems in web development you’ve probably experienced:

Shadow DOM

This article assumes that you’re already familiar with the concept of the DOM and its APIs. If not, you can read a detailed article about it here — https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction.

Shadow DOM is just a normal DOM except for two differences:

In general, you create DOM nodes and append them as children to another element. In the case of shadow DOM, you create a scoped DOM tree that’s attached to the element yet it’s separate from its actual children. This scoped subtree is called a shadow tree. The element it’s attached to is its shadow host. Anything you add to the shadow tree becomes local to the hosting element, including <style>. This is how shadow DOM achieves CSS style scoping.

Creating Shadow DOM

A shadow root is a document fragment that gets attached to a “host” element. The moment you attach a shadow root is when the element gains its shadow DOM. To create shadow DOM for an element, call element.attachShadow():

var header = document.createElement('header');
var shadowRoot = header.attachShadow({mode: 'open'});
var paragraphElement = document.createElement('p');

paragraphElement.innerText = 'Shadow DOM';
shadowRoot.appendChild(paragraphElement);

The specification defines a list of elements that can’t host a shadow tree.

Composition in Shadow DOM

Composition is one the most important features of Shadow DOM.

When writing HTML, composition is the way you construct your web app. You combine and nest different building blocks (elements) such as <div>, <header>, <form> and others in order to build the UI you need for your web app. Some of these tags even work with each other.

Composition defines why elements such as <select>, <form>, <video> and others are flexible and accept specific HTML elements as children in order to do something special with them.

For example, <select> knows how to render <option> elements into a dropdown widget with pre-defined items.

Shadow DOM introduces the following features which can be used to achieve composition.

Light DOM

This is the markup a user of your component writes. This DOM lives outside the component’s shadow DOM. It is the element’s actual children. Imagine that you’ve created a custom component called <better-button> which extends the native HTML button and you want to add an image and some text inside it. This is how it looks like:

The “extended-button” is the custom component that you have defined, while the HTML inside it is called Light DOM and is added by the user of your component.

The Shadow DOM here is the component you have created (“extended-button”). Shadow DOM is local to the component and defines its internal structure, scoped CSS, and encapsulates your implementation details.

Flattened DOM tree

The result of the browser distributing the light DOM, that’s been created by the user into your shadow DOM and which has defined your custom component, renders the final product. The flattened tree is what you ultimately see in the DevTools and what’s rendered on the page.

Templates

When you have to repeatedly reuse the same markup structures on a web page, it’s better to use some kind of a template rather than repeat the same structure over and over again. This was possible before, but it is made a lot easier by the HTML