logo logo

The Rise of Shadow DOM – Core Technical Aspects {Part 1}

The Rise of Shadow DOM - Core Technical Aspects

Before taking control over Shadow DOM, let me brief you about the session that I presented at the virtual Selenium Conference 2020. Due to the limited number of seats at the conference, most of the QA Automation engineers missed it, but NO NEED TO WORRY about it! Here’s a blog tutorial for you to explore and very soon the conference session recording will also be shared through YouTube. It’s time to fasten your seat belts and let’s crack together the Shadow DOM 😊

Prior to learning about Shadow DOM, we first have to understand the inner functioning of a web ‘page’ or ‘document’. Let’s understand the DOM and its inner functioning, doing so will enable us to further understand the Shadow DOM.

What is DOM?

Whenever we open up any web application through any web browser {Chrome/ Firefox/ Safari/ Edge}, it always renders a bunch of web components within the DOM, that encapsulates three things – Markup {HTML}, Styling {CSS} and JavaScript {JS}.

DOM {Document Object Model}a structured representation of an HTML document. HTML provides us a way to add structure and content, but machines do need more than that. That is the reason we need DOM. The DOM then has an API that can be used by our applications to manipulate the document’s structure, content, and styling. You can think of the DOM as a JavaScript representation of our HTML structure. Let’s understand the code snippet that demonstrates HTML to DOM structure:

Browser rendered HTML Code >

    <title>Rise of Shadow DOM</title>
     <h1>Let's understand Shadow DOM</h1>

DOM Tree structure >

└─ head
│  └─ title
│     └─ Rise of Shadow DOM
└─ body
   └─ h1
      └─ Let's understand Shadow DOM

DOM elements are visible to the document interface i.e. ‘it is the entry point to DOM’ and we can find DOM elements by using document.querySelector method. 

DOM vs. Shadow Dom: How is Shadow DOM different from DOM?

Shadow DOM is a kind of web standard that developers use to encapsulate their custom HTML code and style components. Shadow DOM elements are NOT visible to document.querySelector method because it renders separately from the DOM. 

Like DOM, the Shadow DOM is also generated by the browser itself, but in a completely isolated DOM tree called the Shadow DOM Tree. The Shadow DOM Tree encapsulates its own custom/standard elements and styling that is added to an element as a child. The element to which the tree is added is called the Shadow Host and the root is called Shadow Root. And Shadow DOM boundary: LoC is where the shadow DOM ends, and the main DOM begins. In the picture below, we can clearly understand the Shadow DOM structure in respect of DOM {aka Light DOM or Main DOM}:

Shadow Dom vs Main DOM

DOM    =   main DOM   =   Light DOM      =     Shadow DOM


If it renders separately then how can we reach out to Shadow DOM custom/standard elements?

We can reach out to Shadow DOM elements through shadowRoot interface i.e. by using shadowRoot.querySelector method. The Shadow Root has two different modes – Open mode allows the shadow root to be accessible from JavaScript outside the shadow root but Closed mode does not.

Use Case: Why is Shadow DOM becoming more popular among developers?

With time, Web-Component-driven architecture is becoming more popular among developers, but there is still one glitch i.e. global CSS applied could create problems across the rest of our web application code. So what to do now? I think Encapsulation {quarantine} is the only solution. Encapsulation will surely help us prevent any unwanted side effects whilst keeping our component code clean, minimal, and easy to extend.

Earlier days, <iFrame> was quite popular among developers for the sake of encapsulation. But there are few serious security glitches i.e. malicious plugins can be injected through <iFrame>. So the rise of Shadow DOM ✌️

Let’s see how Shadow DOM renders on the browser and also see its Shadow tree structure:

Shadow DOM Code Snippet >

const div = document.createElement("div"); // Shadow Host - a bridge between DOM and Shadow DOM
const header = document.createElement("h1"); // Shadow DOM content
header.innerHTML = 'Part of Shadow DOM content';
const shadowRoot = div.attachShadow({ mode: 'open' }); // attaching shadow root to Shadow host and setting mode as open

shadowRoot.appendChild(header); // attaching to shadow DOM

Browser rendered HTML Code {visible on dev tools} >

   #shadow-root (open)
   <h1>Part of Shadow DOM content</h1>

Shadow DOM Tree structure >

└─ shadow host
   └─ shadow root
      └─ h1
         └─ Part of Shadow DOM content

Before moving ahead, we have to understand the concept of Web Components and Custom HTML elements.

Let’s Understand Web Components

Web component is a W3C standard feature that is being incorporated in HTML5, Google’s Polymer, and Google’s LitElement libraries. Web components are a set of APIs that facilitate the creation of new custom, reusable HTML elements that can be used in web pages and web apps with their functionality and also isolates them from the rest of your application code.

  • Reusable: Web Components are the same as other HTML tags, they can be reused n number of times without conflicting each other’s functionalities. In the picture below, we can clearly see Header/Footer web components used twice.
  • Encapsulation: Web Components wrap their business logic, it’s styling within the component. By attaching shadow DOM to that web component we can completely encapsulate its styling and business logic.
  • All encapsulated web components can interact with each other but can not access each other’s properties. But still global Inheritable properties are easily applicable on all web components e.g. color is an inheritable global property that always impacts all of the web component elements but we can override it within the web component by writing inline HTML code {‘background-color’ is also global property but NOT inheritable to encapsulated web components}.
  • One component can integrate another component e.g. Header inside Article.

Web Components

Web components have FOUR Building blocksCustom HTML Elements , Shadow DOM, HTML Templates and HTML Imports. But we will cover only two {Custom HTML Elements and Shadow DOM [already covered above]}, which are required to understand our test automation problem. 

Let’s Understand and Create Custom HTML Elements

By working on HTML5, we can create our own custom elements, those would be encapsulated by web components. Let’s understand and create custom elements:

  • customElements – are HTML elements with custom templates, behaviors, and tag names made with a set of JavaScript APIs (e.g. <my–element>).
  • To define a custom element, create a class that says ‘MyElement’ that extends HTMLElement class and passes the class to the customElements.define method.
  • HTMLElement class on HTML – is used for defining a custom element and teaching the browser about a new tag.
  • customElements.define method – to register a custom element on the page.

Custom Element Code Snippet >

    	class MyElement extends HTMLElement {
      	connectedCallback() {
        	const button = document.createElement("button");
        	button.onclick = () => alert("Button clicked");
        	button.innerText = "Button in Custom Element";
    	customElements.define("my-element", MyElement);

Let’s open this code snippet in the browser and observe it under the dev tools console, it clearly shows the custom HTML element and that element contains inside another standard web element i.e. button:

Dev tools console view >

   <button>Button in Custom Element</button>

Let’s Attach the Shadow DOM to the above Custom HTML Element Code Snippet

    	class MyElement extends HTMLElement {
      	connectedCallback() {
        	const shadow = this.attachShadow({ mode: "open" });
        	const button = document.createElement("button");
        	button.onclick = () => alert("Button clicked");
        	button.innerText = "Shadow DOM Button";
    	customElements.define("my-element", MyElement);

Let’s open this code snippet in the browser and observe it under dev tools console, it clearly shows the custom HTML element appended with Shadow Root:

  #shadow-root (open)
   <button>Button in Custom Element</button>

Shadow DOM Browser Support

Almost all new versions of majorly used browsers (Chrome, Firefox, Edge, Safari, and mobile browsers) support Shadow DOM V1 {fully supported browsers highlighted in green boxes – reference}. 

Browser Support of Shadow DOM

Key Takeaways

  • Understand DOM vs Shadow DOM
  • Shadow DOM structure and its two modes
  • Shadow DOM use case for developers
  • Understand the DOM, Web components, and Custom Elements
  • DOM and Shadow DOM entry points
  • Shadow DOM attachment with Custom elements
  • Shadow DOM browser support

So far we have seen developers using Shadow DOM gracefully and succeeded in the separate rendering of DOM and Shadow DOM. Now the problem has been passed on to QA Automation Engineers. A lot of questions arise, such as:

  • Can a QA Automation engineer interact with custom elements WITHOUT appending Shadow DOM?
  • Can a QA Automation engineer interact with custom elements AFTER appending Shadow DOM?
  • What is the role of the Open/Closed mode of Shadow Root, while finding the elements?
  • Do regular selectors {Xpath, CSS} work with Shadow DOM elements?
  • How to interact with multi-level Shadow Root elements?
  • Does TestProject OpenSDK support Shadow DOM elements?
  • Does TestProject Test Recorder support Shadow DOM elements?

Want to learn about these open questions? The crucial conversation has already started between Shadow DOM and QA Automation engineers. So go ahead and explore part #2 of this Shadow DOM series here! 😉👨‍💻

About the author

Virender Singh

@VirenderSingh has a test automation mindset that contains the Developer’s logic and Tester’s domain expertise. He is always aggressive to share his knowledge through different mediums say Selenium global conference, organizational meetups, writing blogs for any problem statement, and also through organizational internal technical sessions. Frankly, he got into the testing field not really by choice. But once he started, there is no fall-back. Even after 14 years in this field, there is still a lot to learn and contribute to. And this learning curve is expanding day by day, that seems endless…..!

Approachable anytime at – @Linkedin   @Twitter   @Github


17 4 comments

Leave a Reply