Meet Shadow DOM – a New Kid in Town22nd of Jan 2013 - Samuli Hakoniemi

Ever heard of Shadow DOM? If you haven’t then this article is definitely for you (and if you already have, you should still read this ;)).

Despite of the “scary” name it has there’s nothing to be afraid of. Shadow DOM is a friendly little fellow who’s here to make life of web application developers easier.

In this article, I’ll present thoroughly the capabilities of Shadow DOM and how one can easily create independent widgets by encapsulating their code with it.

Table of Contents

Prerequisites

At the moment, Shadow DOM API can only be used and accessed with Chrome 25+ Beta or Chrome Canary. Download either one of them and start inspecting what Shadow DOM has to offer.

To access Shadow DOM via Chrome Console, you have to enable it from Settings -> General -> [x] Show Shadow DOM.

What is it About?

Shadow DOM is described by W3C as “The shadow DOM allows multiple DOM trees to be composed into one larger tree when rendered”. In practice, this means that it’s possible to create shadow roots and include them into document tree nodes, better known as shadow hosts. Shadow roots can contain child nodes, and these nodes aren’t exposed in traditional DOM tree at all.

Let’s have an example: suppose we have an input element which type is date. In modern browsers this type of element contains a date picker to provide some additional functionality and accessibility for the user.

This is where Shadow DOM enters the stage: date picker is constructed as a Shadow DOM subtree where input field acts as an shadow host.

Even though we can view the Shadow DOM of browser components, we can’t directly access to them.

Playing with Shadow DOM

Now that you’ve set up and you understand the basics about Shadow DOM, it’s time to start playing with it.

In our case, we want to create a simple custom widget which displays JSON data structure in a table. I won’t go in to the deepest details of the widget, but I’ve created a live demo of the widget I’m using as an example.

Preparing Content

At first we start by preparing and creating content. There are couple of guidelines on creating content:

  • it may be wise to use HTML templates instead of direct DOM manipulation when creating complex structures,
  • avoid using too generic naming. Although Shadow DOM is secured, this can lead to misunderstandings,
  • using pseudo-attributes is a good practice

With these guidelines we make the life easier both for us and for the developers who are using the widget.

We start by creating a template for our widget:


Notice: if you want to use external template files (like I do in my demo), use valid HTML elements, eg. by switching template element to a section.

Now we have a basic HTML structure set up and we can add some styling:


This is it. We have a HTML structure and CSS styling ready for the widget and now we need to do some JavaScript magic. Basically we want to fetch the template elements and use them as shadow root children elements for displaying the JSON data. In order do to this, we need some attributes for the jsontable element:


What we’ve got here is:

  • jsontable as the custom widget element,
  • data-template refers to an id of the template we want to use,
  • data-source refers to a JS object variable which contains the “JSON data”

There are both static and XHR examples in my demo, check them out for further guidance of using the data-source and .dataSource setter.

Creating a Shadow Root

We will start by accessing our custom HTML element called <jsontable> and creating a shadow root for it by calling document.webkitCreateShadowRoot (notice the webkit prefix):

var jsontable = document.querySelector("jsontable"),
    jsontableRoot = jsontable.webkitCreateShadowRoot();

For the simplicity of this example, we access only one jsontable element at time and create the functionality for it.

Next, we need to refer to our template and append it to the shadow root:

var templateId = jsontable.dataset.template;
var templateNode = document.getElementById(templateId);

jsontableRoot.appendChild(templateNode);

After this, following steps are:

  • setting references for template elements (table, tr, th, td),
  • populating table from the JSON by using these references

Both of these steps are done in my live demo I’ve created for this article.

This is it! What we’ve achieved is an independent widget which doesn’t interfere with the other DOM at all.

Accessing Shadow Root

Sometimes, one may need to access Shadow DOM externally and manipulate it. This is possible both with CSS and JS.

Accessing via CSS

In order to allow CSS access, we need to declare <shadow root>.applyAuthorStyles = true;. In our example, I made a setter for it (see live demo for further details):

var exports = {
    set applyAuthorStyles(x) {
        jsontableRoot.applyAuthorStyles = !!x;
    }
};

This allows accessing the styling whenever we need to, ie. we can enable and disable in on the fly.

Accessing via JavaScript

JavaScript access can’t be done with direct reference (see example), but it can be done by using webkitShadowRoot property, eg.:

var table = document.querySelector("jsontable").webkitShadowRoot.querySelector("table");

This allows manipulating the Shadow DOM whenever it’s needed. At the moment it’s not even possible to protect your Shadow DOM from external access (see Bug 16509 – [Shadow]: Consider isolation).

Conclusions

I’ve to say I’m excited about Shadow DOM. Although different kinds of snippets, plugins and widgets have been created for years, Shadow DOM and Web Components offers a clear path for creating eg. custom form controls, media controls, captchas, etc.

Current status of course is that Shadow DOM can’t yet be used purely because it has landed only on few browsers and the work is still in progress. However, we can play it with (just like we did in this article) and consider the possibilities it offers in the future.

Resources

Here are some of the resources I encountered while exploring the wonderful world of Shadow DOM:

2 thoughts on “Meet Shadow DOM – a New Kid in Town

  1. Pingback: Quora

  2. Timo on said:

    As a casual JS user, I quite don’t understand what Shadow DOM could be used for, and what it shouldn’t be used for (other than the fact that the browser support itsn’t there yet for serious use). Perhaps there should be a “Why use Shadow DOM?” paragraph in the beginning, outlining the “pre-Shadow DOM” situation of doing stuff and how this changes things. Might be obvious for more experienced coders, but in the intro you say that the article is definitely for me (haven’t heard about Shadow DOM before, but sounded cool), and I didn’t necessarily feel like it after skimming through it (since I didn’t understand how it could make my life easier when using JS).

    Thanks!