Today we take a leap into the unknown: the mysterious and dark shadow DOM, and its partner in crime, HTML5 Templates, both of which are part of the wider web component specification. What exactly are these things and how do they help us as web developers? Continue onward to find out more about the future of HTML.
The Shadow DOM
What is the shadow DOM? Well to answer that, it might help to know a little about how browsers style UI. Basically there is a hidden layer of HTML that we web developers cannot alter. This is how Google and Mozilla build their UI elements. From HTML5 videos to sliders and annoying-to-style drop down form inputs, all of them are built using regular HTML elements that are in the Shadow DOM.
Effectively the shadow DOM is hidden, inaccessible to an outside entity. It’s sealed off and can’t be styled or affected by any general Javascript. The big thing here is that the Web Components Specification which consists of the Shadow DOM and a few other pieces of the puzzle is allowing us to create and alter the shadow DOM.
It might also be useful to know that Shadow DOM can be enabled in Dev Tools. To enable the Shadow DOM in Chrome just right click, go to inspect element, and in the settings (gear in the bottom right corner) you can activate ‘Show Shadow DOM‘. Then you can see for yourself exactly how elements like the range input are styled using HTML elements.
How it’s useful
First off, shadow DOM is useful because it creates encapsulation. All that means is anything in the Shadow DOM is unaffected by styling in regular stylesheets and javascript. This is one of the reasons that UI elements are found in the shadow DOM and not directly editable. Imagine if a beginner in Javascript applied some code to all divs on a page. Without encapsulation sliders and other UI elements would be ruined.
Secondly, this encapsulation allows for easy building of website modules. When you create a plugin or some sort of content to be shared across numerous websites external styling can affect how it is displayed. Perhaps you used a reset stylesheet on one website and didn’t on another. Maybe someone applied 5 pixels of padding to all spans. Regardless of what the reason is, encapsulation means you can create easy and effective reusable web components.
Thirdly, using custom elements (which I will cover separately in the future) we can create our own shadow DOMs and our own custom elements, like a custom drop down or a custom slider.
How it Works
First off, as usual with Javascript it’s good to check that the DOM is loaded. If you’re using jQuery that can be as easy as writing this:
$(document).ready(function() { });
Next up, we need to select the object we want to create a Shadow DOM in. We can do this with a little Javascript query selector, or jQuery if you really wanted to. Then we make our shadow DOM using a function called webkitCreateShadowRoot
. Webkit because currently this only works in webkit browsers. Below I’m just doing a general selection for any divs. Obviously in real life I would hope you’d be a little more specific.
var select = document.querySelector('div'); var shd = select.webkitCreateShadowRoot();
Now we just have to insert stuff into our shadow DOM. That’s easy too, just use innerHTML on the shd variable.
shd.innerHTML = '<div>'+ '<h2>Hello there!</h2>'+ '<p>This is my first shadow DOM. I hope you enjoy it.</p>'+ '</div>';
And there we have it, our own shadow DOM, totally encapsulated from the world. Now that’s all well and good, but it’s a bit simple, isn’t it? Probably too simple to do anything meaningful. What if I want CSS and a little bit of Javascript applied to my shadow DOM? Well to do that, we need to learn about the template tag.
Templates
Templates are introduced by way of the <template>
tag in HTML5. That’s just a regular tag that you can call in your HTML file:
<template id="atemplate"> </template>
The template tag is ignored by the browser, it effectively has the CSS property display: none
as well as preventing any script from executing. This means we can put all our component stuff in here and put it in a shadow DOM object with Javascript. For example, we can do something like this:
<div id="mydiv"> </div> <template id="atemplate"> <style> div { background: blue; } </style> <script> alert('hey there!'); </script> <div>My div</div> </template>
Then we can append our template to a shadow DOM in #mydiv
. It’s important to note that we use template.content
to access the content of the template when appending it to the div.
// Is the DOM ready? document.addEventListener('DOMContentLoaded', function() { // Select #mydiv var select = document.querySelector('#mydiv'); // Create the shadow DOM var shadow = select.webkitCreateShadowRoot(); // Select the template var template = document.querySelector('#mytemplate'); // Append the template to the shadow DOM. shadow.appendChild(template.content); });
Pretty neat right? That’s great but it’s hardly reusable. We have to implement a template tag into every page we want to use it on. Then if an update comes we’d have to update every page the template was on. That sounds awfully long and monotonous. Not to fret though, another key part of web components is HTML imports
HTML Imports
An HTML import is exactly what you think it is. It’s a way to import an HTML document into an HTML document using a link tag. It makes you wonder why this never existed before, but I suppose there wasn’t a good enough usage case until now.
<link rel="import" href="import.html">
Final Words
The web components specification is providing a powerful method for encapsulation and the creation of future web applications. There is no doubt that 3rd party libraries and plugins will adjust to heavily depend on web components when they become standard. Chrome has already implemented the shadow DOM and templates, so it seems like it wont be that long before we see full support in Chrome and hopefully other browsers will follow its lead.
Support
Feature | Chrome | Firefox | Safari | Opera | IE |
Shadow DOM | With Prefix | None | None | Yes | None |
Templates | Yes | Yes | None | Yes | None |
HTML Imports | None | None | None | None | None |