What the Heck is Shadow DOM?

If you build Web sites, you probably use Javascript libraries. If so, you are probably grateful to the nameless heroes who make these libraries not suck.

One common problem these brave soldiers of the Web have to face is encapsulation. You know, one of them turtles on which the Object-Oriented Programming foundation sits, upon which stands most of the modern software engineering. How do you create that boundary between the code that you wrote and the code that will consume it?

With the exception of SVG (more on that later), today’s Web platform offers only one built-in mechanism to isolate one chunk of code from another — and it ain’t pretty. Yup, I am talking about iframes. For most encapsulation needs, frames are too heavy and restrictive.

What do you mean I must put each of my custom buttons in a separate iframe? What kind of insane are you?

So we need something better. Turns out, most browsers have been sneakily employing a powerful technique to hide their gory implementation details. This technique is called the shadow DOM.

My name is DOM, Shadow DOM

Shadow DOM refers to the ability of the browser to include a subtree of DOM elements into the rendering of a document, but not into the main document DOM tree. Consider a simple slider:

<input id="foo" type="range">
 

Pop this code into any WebKit-powered browser, and it’ll appear like so:

Typical Slider Control on WebKit

Simple enough. There’s a slider track and there’s a thumb, which you can slide along the track.

Wait, what? There’s a separate movable element inside of the input element? How come I can’t see it from Javascript?

var slider = document.getElementsById("foo");
console.log(slider.firstChild); // returns null
 

Is this some sort of magic?

No magic, my fair person of the Web. Just shadow DOM in action. You see, browser developers realized that coding the appearance and behavior of HTML elements completely by hand is a) hard and b) silly. So they sort of cheated.

They created a boundary between what you, the Web developer can reach and what’s considered implementation details, thus inaccessible to you. The browser however, can traipse across this boundary at will. With this boundary in place, they were able to build all HTML elements using the same good-old Web technologies, out of the divs and spans just like you would.

Some of these are simple, like the slider above. Some get pretty complex. Check out the video element. It’s got trigger buttons, timelines, a hover-appearing volume control, you name it:

WebKit Video Element With Controls

All of this is just HTML and CSS — hidden inside of a shadow DOM subtree.

To borrow a verse from that magnetic meme duo, “how does it work?” To build a better mental model, let’s pretend we have a way to poke at it with Javascript. Given this simple page:

<html>
<head>
<style> p { color: Green; } </style>
</head>
<body>
<p>My Future is so bright</p>
<div id="foo"></div>
<script>
    var foo = document.getElementById('foo');
    // WARNING: Pseudocode, not a real API.
    foo.shadow = document.createElement('p');
    foo.shadow.textContent = 'I gotta wear shades';
</script>
</body>
</html>
 

We get the DOM tree like this:

<p>My Future is so bright</p>
<div id="foo"></div>
 

But it is rendered as if it were this:

<p>My Future is so bright</p>
<div id="foo"> <!-- shadow subtree begins -->
    <p>I gotta wear shades</p>
</div> <!-- shadow subtree ends -->
 

Or visually like so:

Shadow DOM Example

Notice how the second part of the rendered sentence is not green? That’s because the p selector I have in my document can’t reach into the shadow DOM. How cool is that?! What would a framework developer give to have powers like this? The ability to write your widget and not worry about some random selector fiddling with your style seems … downright intoxicating.

Course of Events

To keep things natural, events fired in shadow DOM subtree can be listened to in the document. For instance, if you click on the mute button in the audio element, your event listeners on an enclosing div would hear the click:

<div onclick="alert('who dat?')">
    <audio controls src="test.wav"></audio>
</div>
 

However, if you ask to identify who fired the event, you’ll find out it was the audio element itself, not some button inside of it.

<div onclick="alert('fired by:' + event.target)">
    <audio controls src="test.wav"></audio>
</div>
 

Why? Because when crossing the shadow DOM boundary, the events are re-targeted to avoid exposing things inside of the shadow subtree. This way, you get to hear the events, fired from the shadow DOM, and the implementor gets to keep their details hidden from you.

Reaching into Shadows with CSS

One other trick up the sleeve is the ability to control how and whether CSS reaches into the shadow subtree. Suppose I want to customize the look of my slider. Instead of the standard OS-specific appearance, I want it be stylish, like so:

input[type=range].custom {
    -webkit-appearance: none;
    background-color: Red;
    width: 200px;
}
 

The result I get is:

Slider with a custom-styled track

Ok, that’s nice, but how do I style the thumb? We already determined the that our usual selectors don’t go into the shadow DOM tree. Turns out, there’s a handy pseudo attribute capability, which allows shadow DOM subtrees to associate an arbitrary pseudo-element identifier with an element in the subtree. For example, the thumb in the WebKit slider can be reached at:

input[type=range].custom::-webkit-slider-thumb {
    -webkit-appearance: none;
    background-color: Green;
    opacity: 0.5;
    width: 10px;
    height: 40px;
}
 

Which gives us:

Fully custom-styled slider

Ain’t it great? Think about it. You can style elements in the shadow DOM without actually being able to access them. And the builder of the shadow DOM subtree gets to decide which specific parts of their tree can be styled. Don’t you wish you had powers like this when building your UI widget toolkit?

Shadows with Holes, How’s that for a Mind-bender?

Speaking of awesome powers, what happens when you add a child to an element with a shadow DOM subtree? Let’s experiment:

// Create an element with a shadow DOM subtree.
var input = document.body.appendChild(document.createElement('input'));
// Add a child to it.
var test = input.appendChild(document.createElement('p'));
// .. with some text.
test.textContent = 'Team Edward';
 

Displaying as:

Input Element and Nothing Else

Whoa. Welcome to the twilight DOM — a chunk of document that’s accessible by traversal but not rendered on the page. Is it useful? Not very. But it’s there for you, if you need it. Teens seem to like it.

But what if we did have the ability to show element’s children as part of its shadow DOM subtree? Think of the shadow DOM as a template with a hole, through which the element’s children peek:

// WARNING: Pseudocode, not a real API.
var element = document.getElementById('element');
// Create a shadow subtree.
element.shadow = document.createElement('div');
element.shadow.innerHTML = '<h1>Think of the Children</h1>' +
    <div class="children">{{children-go-here}}</div>';
// Now add some children.
var test = element.appendChild(document.createElement('p'));
test.textContent = 'I see the light!';
 

As a result, if you traverse the DOM you will see this:

<div id="element">
    <p>I see the light</p>
</div>
 

But it will render like this:

<div id="element">
    <div> <!-- shadow tree begins -->
        <h1>Think of the Children</h1>
        <div class="children"> <!-- shadow tree hole begins -->
            <p>I see the light</p>
        </div> <!-- shadow tree hole ends -->
    </div> <!-- shadow tree ends --> 
</div>
 

As you add children to element, they act as normal children if you look at them from the DOM, but rendering-wise, they are teleported into a hole in the shadow DOM subtree.

This is the point where you admit that this is pretty cool and start asking:

When can I have it in my browser?

Homework Assignment

Did you think you’d read through all this preaching and get away without homework? As a Javascript library or framework developer, try to think of all the different great things having shadow DOM would allow you to do. Then think of specific use cases (actual/pseudo code a plus) of where shadow DOM could be applied. To help you get your thinking groove going, here is current list of use cases.

Finally. share your use cases on public-webapps mailing list. The discussion about adding these capabilities to the Web platform is under way and your help is needed.

If you aren’t a much a framework writer, you can still participate — by cheering for the shadow DOM and spreading the joy on your favorite social networking site. Because joy is what’s it’s all about.

PS. SVG and Shadow DOM

Almost forgot. Believe it or not, SVG has actually had shadow DOM since the beginning. The trouble is, its shadow DOM is very… shady. No, that’s not it. There’s another qualifier that also begins with “sh” and ends with a “y”. Yeah, that one. I could go on, but trust me on this. Or read the spec.

220 thoughts on “What the Heck is Shadow DOM?”

  1. There is one major flaw with cross-browser compatibility for the CSS pseudo-element approach, which is that one invalid selector will cause the entire rule to be dropped. Suppose Mozilla implements the slider-thumb pseudo as well, CSS like this would be impossible:

    input[type=range].custom::-webkit-slider-thumb, input[type=range].custom::-moz-slider-thumb
    {
    properties
    }

    This is behavior which has been in CSS since forever, used for hacks and therefore not likely to change. I addressed the problem a couple of months ago on www-style myself.

    The Shadow DOM itself is great, of course. I’ve seen the patches come by 🙂

    1. I came across this and hung my head at realising it lead towards duplication.
      But between CSS autoprefixer and SASS mixins it’s actually really easy to solve.

  2. This is exactly how IE’s HTML components (aka behaviors) implementation worked. This is also how Microsoft’s WPF works. They refer to them as the Visual Tree and the Logical Tree. Had the HTML components proposal from 1998[1] ever been adopted and carried forward we’d all be living in a better world. Amazed that we still have now standard control model to this day.

    [1] http://www.w3.org/TR/NOTE-HTMLComponents

      1. Okay, Dmitri, here’s your homework. Tell the SVG WG exactly what you don’t like about the SVG shadow DOM, and how you would want it to work instead. We’ll fix it in SVG 2.

        For historical perspective, SVG defined the shadow DOM over a decade ago, so it’s not surprising that some tweaks are needed.

        Nice article, btw… I’ve always liked the shadow DOM, and even just a few years ago, it was utterly reviled by browser developers. So, I’m glad to see it get some props (like the rainbow it is).

  3. Dimitri, did you hear about Ample SDK http://www.amplesdk.com/ ? It does nicely pretty much what you are discussing here on top of the existing technologies (HTML4+CSS1+DOM0). Basically it creates and maintains its own DOM (with standard L2 APIs) out of XUL, SVG or any other markup document included (or referenced) in an HTML webpage in . Indeed the events propagation works in this DOM. For example in case of SVG the host tree will only contain SVG nodes, but the shadow in case of IE, for example, will contain VML nodes (see http://www.amplesdk.com/examples/markup/svg/tiger/ in IE6, for exmple). With XUL everything is rendered in shadow tree using HTML4 (see http://www.amplesdk.com/examples/markup/xul/views/). The developer interacts with Ample SDK DOM through “ample” object which is the “document” object of Ample SDK.

    Next to the benefit of browser-independent DOM re-implemented, developer can also use many “new” features of CSS, such as pseudo-elements classes to reach out shadow DOM if needed and so on. See tutorial for details http://www.amplesdk.com/tutorials/adg/

    1. I am familiar with Ample — and kudos! You are definitely on my “framework hero” list. I should’ve linked to it in my post. But what Ample does is a hack, approaching horrifying proportions. You are basically building a rendering engine inside of a rendering engine. Yes, you are enabling new capabilities, but they come at the expense of performance, latency, and just plain living in a detached layer that has little to do with what is really going in the renderer. And yes, I totally understand why you’re doing it and even support it, because supplying an even ground in today’s browser landscape is just tough.

      Finally, with your skills, you should stop worrying about the legacy browsers and start hacking on the actual rendering engines instead. Like WebKit. Or Gecko. Let me know if you’re interested 🙂

  4. As a developer, I hate them because I can’t style them to my will…
    For example, I want to skin the arrow in select-element, but I am not allowed to do. In order to do it, I have to emulate same behaviour of select, using div’s and other elements.

  5. Just out of curiosity, is there any way to find out which elements that lies within the Shadow DOM? For example, I haven’t been able to find a way to access the up & down buttons of an input type=”number” in JS. Could this be because they lie in the Shadow DOM?

    1. The way to do this is by studying the code. For example, in WebKit the magic is currently in TextControlInnerElements.cpp (http://www.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/third_party/WebKit/WebCore/rendering/TextControlInnerElements.cpp&q=SpinButtonElement&exact_package=chromium&l=260) and RenderTextControlSingleLine.cpp (http://www.google.com/codesearch/p?vert=chromium#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/rendering/RenderTextControlSingleLine.cpp). But don’t expect it to be here for long — we’re planning a rather large refactoring of this code soonish.

  6. But if you can’t access this “Shadow DOM” stuff then how does the Shadow DOM really matter? Forgive me if I missed something. Can people actually make re-usable custom components that are tucked into the shadows of the Shadow DOM?

    1. Use case: you are creating a reusable custom control out of conventional HTML elements. (For example, a fancy select box, or a calendar control.)

      Problem 1: the elements that comprise your clever control are affected by external stylesheets, perhaps in ways that will break them horribly.

      Problem 2: other JavaScripts that do clever stuff might accidentally interact with your controls in ways that you would prefer they couldn’t.

      Solution: the Shadow DOM will let you build a DOM subtree that cannot be touched by CSS and JavaScript through conventional means.

  7. Very cool, and pretty mind-boggling when it gets to ‘holes’ – I’ll have to read that again!

    Could we please have a working link to the ‘current list of use cases’ – or have they actually disappeared?

  8. Pingback: The Shadow DOM
  9. Interesting idea, allowing component creators to have the ability to create “shadow DOM” elements, but, I don’t see any compelling argument for it. The ability to encapsulate a “component” is indeed important, but, to that end, you only mention JavaScript events and CSS. As far as JavaScript event encapsulation, it’s already possible to trap events at a specific DOM element and to have events originate from a given element, so, that is not an issue. CSS encapsulation? That is a valid issue, but, it is an issue with CSS (being addressed by CSS3 no less), not a reason to add a component model to browsers.

  10. Thats awesome!
    I am just a little web-developer who is interested in those things.

    My current challenge style the html5 progress tag in opera. This Shadow DOM seems like “42” to my question.
    Where is the green coming from?

    Is there a way of finding the super crazy pseudo selector to figure out where the color in the browser is controlled?

  11. It occurs to me that content added via pseudo-elements :before and :after can be considered Shadow DOM.

    1. At least in WebKit, the content property is implemented by injecting rendering objects directly, without DOM. So in this particular case, there is no shadow DOM involved.

  12. Mozilla’s XBL system exposes a mechanism like this to developers, which is used to implement the XUL widgets that make up the UI of Mozilla’s apps.

    It would be awesome to see a facility like this standardized across browsers for use in web pages.

    Mozilla tried to write a specification for such a thing:
    http://www-archive.mozilla.org/projects/xbl/xbl2.html

    1. XBL 2.0 is also W3C Candidate Recommendation:
      http://www.w3.org/TR/xbl/

      Just no one implemented it yet. At least in the Browsers. There is a JS implementation from Sergey (above):
      http://code.google.com/p/xbl/

      Although XBL was designed with other intention than those of the WebApps WG, this concept of templating reusable components and bind them to implementations is very nice. Through the interface it provide well-defined access to the shadow tree, as well as events. And it’s based on the experiences made with XBL1.0 in XUL (or even remade from scratch?). Thus if there are shadow trees in the rendering engine already, why not expose them to all users to create reusable components?

  13. A shadow dom would hide complexity from the programmer. Complexity is basically the worst enemy you can have. Picture for yourself how hard something would get to debug when it also has many property’s that are inaccessible once set (a shadow dom would be that).

    The way to writing more functional and bugfree apps is not abstracting complexity, it’s removing complexity. I think the added complexity of a shadow dom would not be a good idea. I do think standardizing the look and customisability of the elements we have now would be a good idea (like what happened to buttons, sort of). HTML + CSS + Javascript is about dynamic interactive applications, not about structured data anymore. If you ask me there should be a single standardized browser css template, and a collection of public domain fonts that are enforced be included.

    Bah!

  14. I think that Shadow DOM can be used for hiding DOMs from the container page. However, malicious host pages can redefine WebKitShadowRoot object. For instance, page A at Host A has script B from Host B. Script B can use ShadowDOM to hide some DOMs from PageA. However, page A can redefine WebKitShowRoot objects to reveal information. I claim that WebKitShadowRoot must be non-over-writable like window and document objects. However, the current chrome browser does not support this. Please enlighten me if I am incorrect.

      1. Thank you Glazkov for the immediate reply. Though, Shadow DOM is “NOT” meant for security assurance, obviously Shadow DOM can be used for hiding DOMs from the hosting page by making ShadowRoot inaccessible from the hosting page. There is no way for the hosting page to access Shadow DOMs unless going through ShadowRoot. I think that it is correct. Moreover, What goods can come from allowing redefinition of ShadowRoot object for the purpose of functional encapsulation ?

  15. Hi,

    The Document Object Model (DOM) is an API for HTML and XML documents. It provides a structural representation of the

    document, enabling you to modify its content and visual presentation by using a scripting language such as JavaScript.

    Which information given by you on DOM, very informative. So thanks for sharing your knowledge. There are few other link

    that’s also helpful for developers.

    http://www.mindstick.com/Articles/d4d10ebd-58c8-45a1-88f4-724497f7a36e/?JavaScript%20Document%20Object%20Model
    http://msdn.microsoft.com/en-us/library/dd282900(v=vs.85).aspx

  16. Delete my prior comment since it won’t show HTML code. It was just saying that you seem to have an unnecessary DIV in the “gotta wear shades” example. Can’t it just be a P element with an ID of “foo” without needing a DIV around it?

  17. There is a small mistake in the first JavaScript (var slider = document.getElementsById(“foo”);)

    It should say “getElementById” without an “s”

  18. Dimitri Glazkov your page here was very informative. But better yet it was HILARIOUS. I like your writing style. I had to try not to laugh. It would have been interesting explaining my cackling to my non-IT nextdoor colleagues. 🙂

Leave a Reply to KarthikeyanCancel reply

Discover more from Dimitri Glazkov

Subscribe now to keep reading and get access to the full archive.

Continue reading