What is shadow root and how to use it ?

In today’s world full of component-driven architecture is still the case that CSS applied globally could create problems in the rest of our code. This is because of the way and how CSS was created. It is not necessarily bad, but it is good to be aware that we could use styling more effectively through the idea of encapsulation in the component architecture world. This helps us avoid unwanted side effects while maintaining our component code clean, minimal, easy to extend and rebuild.

An important feature of web components is encapsulation—the ability to hide and differentiate the behavior, style, and markup structure from the other code on the page such that the various parts do not overlap and keep the code nice and clean. The core component of this is the Shadow DOM API, which offers a way to add a separate hidden DOM to an element. This article addresses the basics of using Shadow DOM and Shadow root concepts.

Before the introduction of Shadow DOM, Let’s understand some concepts:

What is DOM?

The basis of the web page is HTML, a markup language for people to write and understand. HTML gives us a way to add content and structure, but machines need a little more than this. For this reason, the Document Object Model (aka the DOM) is being created. The DOM then has an API that our applications can use to manipulate the structure, content, and styling of a document.



What is DOM tree?

When a browser loads a web page, the HTML code is converted to a data model consisting of “Objects” and “Nodes.” In addition, a “DOM Tree” is generated in which the web page structure is stored. 

A DOM tree look like this : 

DOM Tree

Web Components is a set of various technology that enable you to build reusable custom elements. Their functionality is encapsulated away from the rest of the program and can be included with the web apps.

There are 4 Web Component standards, we’ll focus on the Shadow DOM :

  1. Shadow DOM
  2. HTML Templates
  3. Custom elements
  4. HTML Imports

Shadow DOM eliminates the fragility of creating web apps. The fragility comes from the global nature of HTML, CSS, and JavaScript. Over the years, we have invented an exorbitant number of tools to solve the problems. For example, when you add a new HTML id / class, it doesn’t say whether it clashes with the current name used on the website. Subtle glitches are creeping up, the CSS specificity is becoming a big problem! (important all things!), style selectors are rising out of balance, and output can suffer. Shadow DOM fixes both the HTML and the DOM. This adds scoped styles to the web apps.  

Note: Please always bear in mind that this concept is not being applied in all browsers.

Shadow DOM is used for encapsulation. The Shadow DOM allows the author of a component to build its own DOM, which is separate from a regular DOM. This ensures that all JavaScript and CSS written against this new DOM can be fully encapsulated, and its results are shielded against CSS declared in a global field unless the component allows it to do so.  



Built the Shadow DOM: The structure of the DOM is normally hidden from us, but we can see it in the developer tools. In Chrome, for example, we need to allow “Show user agent shadow DOM” option in Dev Tools.

What you can see below #shadow-root is called “shadow DOM”.

We can’t get built-in shadow DOM elements with regular JavaScript calls or selectors. These are not regular children but a strong technique for encapsulation.

Shadow Root: A shadow tree is a node tree whose root called as a shadow root. A shadow root is always attached through its host to another node tree. Therefore, the shadow tree is never alone. The node tree of the host of the shadow root is sometimes referred to as the light tree.

Shadow DOM allows hidden DOM trees to be attached to the elements of a regular DOM tree.

Here, this shadow DOM tree starts with a shadow root underneath which can be attached to any element you select, in the same manner as the normal DOM.

<my-header>
  #shadow-root
    <header>
      <h1>
      <button>

The top of the shadow tree is Shadow root. The element that the tree is attached to (<my-header>) is called the shadow host and the host has a property called shadowRoot that refers to the shadow root. The shadow root is a document fragment that is attached to the host element and it has a host property that identifies its host element. The act of attaching a shadow root is how the element gets its shadow DOM.

There are a few bits of shadow DOM terminology that you need to be aware of:

  1. Shadow host: The element you choose to start a new Shadow DOM.
  2. Shadow tree: The node tree i.e. DOM tree inside the shadow DOM.
  3. Shadow boundary: This is the place where the shadow DOM ends, and the regular DOM begins.
  4. Shadow root: It is the root node of the shadow tree.

Creating shadow DOM: Once you have chosen the element that you want to use to host your Shadow DOM, you have to attach the Shadow DOM to it. Please see the snippet below.



<p class="highlight">Welcome to GeeksForGeeks</p> 

<div id="Firstcomponent"></div> 

<script> 
  const shadowRootOne = 
     document.getElementById('Firstcomponent')
            .attachShadow({mode: 'open'}); 
</script>

So in this script, we grab a div element with an id Firstcomponent, and then we call a special method on this element called attachShadow() where this method passes in an object with a property mode.

There are two limitations:

  1. Only one shadow root can be created per element.
  2. The Element must be either a custom element, or one of: “blockquote”, “body”, “div”, “article”, “aside”, “footer”, “h1…h6”, “header”, “main” “nav”, “p”, “section”, or “span” and Other elements, like <img>, can’t host shadow tree.

The mode option is used to set the encapsulation level. It must follow any of two values:

  • “open” – open means that you can access the shadow DOM using JavaScript.
For example using the Element.shadowRoot property:
let myShadowDom = myCustomElem.shadowRoot;
  •  “closed” – myCustomElem.shadowRoot returns null.

You won’t be able to access the shadow DOM from the outside, If you attach a shadow root to a custom element with mode: closed set. 

We can only access the shadow DOM by the reference returned by attachShadow and it is probably hidden inside a class. Browser-native shadow trees are closed. There’s no way to access them. The shadow root, returned by attachShadow() method. This object represents the root node of your newly created Shadow DOM and you will append your other elements to.

Let’s see the example given below which shows adding a paragraph element against the Shadow Root that is hosted by the component.

const paragraphElement = document.createElement('p'); 
paragraphElement.setAttribute('class', 'highlight'); 
paragraphElement.innerText = 
'This is a Shadow DOM paragraph'; 
    
shadowRootOne.appendChild(paragraphElement);

The above code will attach a new paragraph element with the highlight class to the Shadow Root which becomes the part of new Shadow DOM. This paragraph element is encapsulated separately from the main DOM so that it can not be identified by performing both JavaScript which CSS selections. It will also not inherit any styles from the CSS author page, but at this stage it will have the default browser styles, since we have not yet specified styles for this Shadow DOM.

Adding Styles to the Shadow DOM: The styles within the Shadow Tree are scoped to the Shadow Tree, and do not impact the elements outside the Shadow Tree. Often, styles outside the shadow tree do not match selectors inside the shadow tree.

There are 2 ways to add style to a shadow DOM. First way to include the styles into the Shadow DOM is the same as adding any other element to the Shadow DOM. With a simple appendChild call on the Shadow Root. The other method is use the <template> element for add styles, but this is the part of another topic, so we are just going to look at one in this article.

const styleElement = document.createElement('style'); 
styleElement.innerText = ` 
  .highlight { 
      background: #FFA500; 
      font-size: 6rem; 
  } `; 

shadowRootOne.appendChild(styleElement);

The paragraph is encapsulated inside a separate Shadow DOM therefore if you changed the global style to have a higher specificity, it does not change the styling of new component.

full-stack-img




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :
Practice Tags :


Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.