From Web Component to Lit Elements

43 mins remaining
Back Next

4. Shadow DOM

Why Shadow DOM?

In the previous step, you'll notice that the selectors in the style tag that you inserted select any rating element on the page as well as any button. This may result in the styles leaking out of the element and selecting other nodes that you may not intend to style. Additionally, other styles outside of this custom element may unintentionally style the nodes inside your custom element. For example, try putting a style tag in the head of the main document:

Code Checkpoint

index.html

<!DOCTYPE html>

<head>

  <script src="./index.js" type="module"></script >

  <style>

    span{

      border: 1px solid red;

    }

  </style>

</head>

<body>

  <rating-element></rating-element>

</body>

Your output should have a red border box around the span for the rating. This is a trivial case, but the lack of DOM encapsulation may result in larger problems for more complex applications. This is where Shadow DOM comes in.

Attaching a Shadow Root

Attach a Shadow Root to the element and render the DOM inside of that root:

index.js

class RatingElement extends HTMLElement {

  constructor() {

    super();

    this.rating = 0;

  }

  connectedCallback() {

    const shadowRoot = this.attachShadow({mode: 'open'});

  }

}

 

customElements.define('rating-element', RatingElement);

When you refresh the page, you will notice that the styles in the main document can no longer select the nodes inside the Shadow Root.

You should now see an expandable shadow root that holds the contents. Everything inside that shadow root is called the Shadow DOM. If you were to select the rating element in Chrome Dev Tools and call $0.children, you will notice that it returns no children. This is because Shadow DOM is not considered a part of the same DOM tree as direct children but rather the Shadow Tree.

Light DOM

An experiment: add a node as a direct child of the <rating-element>:

index.html