Introduction to Shadow DOM
October 29th, 2014
Shadow DOM is an emerging web standard that gives developers access to style and DOM scoping. Learn how to use it on your own website.
What is Shadow DOM?
Here's a video rendered in the browser using the HTML video tag. While the code is as simple as a single tag, the video has built-in controls.
<video src="http://craftymind.com/factory/html5video/BigBuckBunny_640x360.mp4" controls></video>
If you open up the DevTools and turn on 'Show user agent shadow DOM', we can actually look at the source for the video controls.
And you'll see that they're actually made of HTML. This is an example of using the Shadow DOM.
The nice thing about Shadow DOM is that you can actually use this feature in your own components.
Structure of a Shadow DOM
An element that has a shadow root associated with it is called "shadow host". The shadow root can be treated as an ordinary DOM element so you can append arbitrary nodes to it.
With Shadow DOM, all markup and CSS are scoped to the host element. In other words, CSS styles defined inside a Shadow Root won't affect its parent document, CSS styles defined outside the Shadow Root won't affect the main page.
How I build a Shadow DOM
In order to create a Shadow DOM, invoke
.createShadowRoot() on a DOM node and obtain a Shadow Root. By adding elements to the Shadow Root, you can build Shadow DOM.
var host = document.querySelector('#host'); var root = host.createShadowRoot(); // Create a Shadow Root var div = document.createElement('div'); div.textContent = 'This is Shadow DOM'; root.appendChild(div); // Append elements to the Shadow Root
Notice that elements added to the Shadow Root won't be queried. In this case
document.querySelector('#host div') results in
Reflecting the Shadow Host's content to a Shadow DOM
Sometimes you may want to project the child elements of a Shadow Host into a Shadow DOM.
Imagine you want the similar functionality to the combination of
<option>. They are separate tags but make sense as a select menu when used together.
With Shadow DOM, you can do this for example: A name tag that is styled in the Shadow DOM, but needs to pull in the user's name from an external input.
In order to achieve this, you can use
<content> element inside the Shadow DOM.
var host = document.querySelector('#host'); var root = host.createShadowRoot(); var content = document.createElement('content'); content.setAttribute('select', 'h1'); // <content select="h1"></content> root.appendChild(content);
<div id="host"> <h1>This is Shadow DOM</h1> <div>
<content> tag a
select attribute with CSS selector as a value, you can distribute host's content to wherever you want.
select attribute can only take direct children of the host element. For example, you can NOT assign descendant elements to the
<div id="host"> <div class="child"> <h1>This is Shadow DOM</h1> </div> </div> <content select=".child h1"></content> // Not allowed
Combining with Templates
Shadow DOM is fantastic as you have learned so far, but adding contents imperatively isn't that efficient and is not designer friendly. Instead, you may wish to use HTML to define your content.
Here's where the
<template> element comes in. Using the template element, you can define contents of your Shadow DOM declaratively with HTML. To learn about the <template> element, check out the previous post.
<!-- Content of <template> will be appended to the Shadow Root --> <template id="template"> <style> ... </style> <div id="container"> <img src="http://webcomponents.org/img/logo.svg"> <content select="h1"></content> // Insert h1 here </div> </template> <div id="host"> <h1>This is Shadow DOM</h1> </div>
var host = document.querySelector('#host'); // Create a Shadow Root var root = host.createShadowRoot(); var template = document.querySelector('#template'); // Copy the <template> var clone = document.importNode(template.content, true); // Append template to the Shadow Root root.appendChild(clone);
Shadow DOM is supported by Chrome and Opera. Firefox supports it behind a flag as of October 2014. To check availability, go to caniuse.com. For polyfilling other browsers, you can use platform.js (renamed as webcomponents.js in Nov. 2014).
So that is the very basic of the Shadow DOM. But this is only the tip of iceberg. There's tons of interesting things to learn around Shadow DOM such as
- Event handling
- Working with multiple Shadow Roots
If you are interested in learning them, check out following pages:
Head to posts tagged with Shadow DOM to learn even more.