Built-in Shadow DOM: The Magic Behind Familiar Elements
📅
Lesson Objective: Understand what built-in Shadow DOM is using the example of built-in browser elements and what makes it so effective.
Introduction
We use elements like <select>, <input type="range">, <video>, or <audio> every day. They have complex behavior: a dropdown list, a progress slider, play and pause buttons. But have you ever thought about how they work under the hood?
If you take a regular <div> and try to turn it into a styled dropdown, you'll have to add a bunch of HTML markup for the button, list of options, wrappers, and write a lot of CSS and JavaScript. So why does a simple <select> work as a single "black box" despite its complexity?
The answer lies in a powerful mechanism called Shadow DOM.
What is Hidden Inside a Regular <select>?
By default, browsers hide the internal implementation of their complex elements (also called User Agent components). Their DOM structure is encapsulated and not visible in developer tools.
Let's look at a standard <select> element in your HTML:
<select>
<option value="js">JavaScript</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
</select>
In the code, we only see the <select> tag itself and its direct children <option>. But where is the code for the arrow that appears on click and the dropdown list itself?
To see the real "kitchen", you need to check the settings in the developer tools (e.g., Chrome DevTools).
- Open Developer Tools (F12).
- Go to Settings (F1 key or the three-dot icon → Settings).
- In the "Elements" section, check the box next to "Show user agent shadow DOM".
- Now refresh the page and look at the
<select>element in the inspector.
You will see something completely different:
<select>
#shadow-root (user-agent)
<div role="listbox">
<div role="option">JavaScript</div>
<div role="option">HTML</div>
<div role="option">CSS</div>
...
</div>
<div>...</div> <!-- Element for the arrow -->
</select>
That's the magic! What's inside #shadow-root is the Shadow DOM.
What is Shadow DOM?
Shadow DOM is a separate DOM tree attached to a regular DOM element (which in this case is called the shadow host or host element). The key feature of Shadow DOM is that it is protected from external influence; all data is isolated, which ensures the object's integrity and allows users to utilize it without delving into implementation details. This isolation is called encapsulation.
This is precisely why you can't simply style the arrow inside a <select> using CSS from your stylesheet. The browser uses Shadow DOM to protect the internal structure of the element and guarantee that it will work and look predictable, regardless of the page's styles.
Strengths of Shadow DOM
So, using the <select> example, we understood that the Shadow DOM is fully isolated (encapsulation). Elements inside the Shadow DOM are not accessible from the outside through regular JavaScript calls like querySelector('.my-class'). Styles written in the main document do not apply to styles inside the Shadow DOM. Conversely, styles from the Shadow DOM do not "leak" out. This provides the following advantages:
Reusability
- Independent components work in any environment
- Easy integration into different projects
- Ability to create component libraries
Scalability
- Ease of maintaining large applications
- Clear separation of responsibilities
- Possibility for parallel development
Security
- Protection from accidental changes
- Controlled interaction API
- Prevention of conflicts
Conclusion
Built-in Shadow DOM is a mechanism that browsers use to create complex and resilient UI controls. It hides the complex internal implementation, providing us with a simple interface.
We cannot change the built-in trees, but the standard gives us an equally powerful tool—Custom Shadow DOM. Now, understanding this principle, we are ready to use it ourselves to build professional-level web components.