DOM, DOM, DOM…
Many concepts in web developement are omnipresent, yet it is hard to define them precisely. DOM (Document Object Model) is one of them. Once I’ve heard people talking about virtual DOM, shadow DOM, DOM level 1, 2, 3, I knew I have to do something about it. This story is my attempt to shed some linght onto this notion and to structure it a bit.
[TL;DR: DOM enhances cross-browser compatibility, DOM Levels are no longer relevant, Shadow DOM offers better element encapsulation, virtual /incremental DOM allows to frequently update the DOM in a more performant way]
Before we move on to the definiton, I always find it helpful to understand, why people would introduce the concept in the first place.
Imagine, you are a web developer back in 1997. You would like to create web pages with client-side interactivity. Cross-browser compatibility was never an easy task, but suddently Internet Explorer 4 and Netscape Navigator 4 started taking different approaches in how to communicate to the document (i.e. HTML) elements or tags. To prevent such a development of Internet, the World Wide Web Consortium (W3C) began working on DOM. Hence, DOM helps us to interact with our web pages (more or less) the same way in all browsers.
The DOM interface is built using multiple APIs (or modules) that work together. The core DOM defines the objects that fundamentally describe the document and the objects within it (attributes, elements etc.). Further modules include event support, style sheet handling, XPath usage etc.
You don’t have to do anything special to begin using the DOM. When a web page is loaded, the layout engine of the browser (e.g. Blink, WebKit, and Gecko) creates the DOM of the page in memory. What does it look like?
The model represents a document with a logical tree. Each branch of the tree ends in a node (a HTML tag), and each node contains objects. Nodes can also have event handlers attached to them; once an event is triggered, the event handlers get executed. Thus, the DOM ist the object-based representation of the HTML document and the API to manipulating that object.
Once you’ve started to think that you’ve got it, things get more complicated. Ever heard about DOM level 4? Shadow DOM? Virtual DOM? Incremental DOM? Let’s get it sorted, too.
Dom Level: 0, 1, 2, 3, 4…
Back to 1995, Nescape 2 introduced Level 0 DOM (well, just the first DOM implementation). With Level 1 it became W3C recommendation for all browsers. Basically, the DOM level simply represents something like a release version. It can be associated with its evolution: next levels (specifications) built up on the previous ones, adding some new modules, methods and behaviors… untill it stopped with Level 4 DOM. The standard is no longer described as a set of layers, but as a snapshot of the living specification. Continous delivery, so to say.
It used to be important to know, which level the feature belongs to, because DOM Level 2 was more widely supported than DOM Level 3. As of now, it is no longer relevant, though you might still find these version numbers on the web. For eager-minded, here is a nice historical overview over respective features. Yet we should turn to the notion which came to stay in web development — the shadow DOM.
Again, let’s start with the question why do we need it.
Think about <input type=”range”>. It obviously consists of more than just one element, yet the “insides” are hidden from web developers. Instead, just some CSS properties are exposed for customization. The browser however, can access the “insides”and build all HTML elements using the same good-old Web technologies, out of the
spans just like you would. Oh, speaking about browsers… don’t forget to check out caniuse for Shadow DOM support.
Shadow DOM is basically a “DOM within a DOM” or a scoped DOM tree that’s attached to the element, but separate from the rest. This scoped subtree is called a shadow tree. The element it’s attached to is its shadow host. Anything you add in the shadows becomes local to the hosting element, including
<style>. This is how shadow DOM achieves CSS style scoping.
Further reading on the topic:
- What is the Shadow DOM?
- What the Heck is Shadow DOM?
- Shadow DOM v1: Self-Contained Web Components | Web Fundamentals
- Using shadow DOM
However, we are not done yet. Another important DOM concept is…
To understand the virtue of the virtual DOM (VDOM), let’s start with a simple use case. The user clicks a button and you want to display a menu (re-render()), color the background differently (re-render-again()), highlight the focused stuff (uff-re-render-again()), hide irrelevant information(really-re-render-again?!()). Considering, that the state of the art algorithms have a complexity in the order of O(n3), this is a hell lot of work. It gets even worse in the context of Single Page Applications (SPAs), since even more stuff has to be moved around. Thus, the virtual DOM comes into play.
Unlike the DOM or the shadow DOM, the virtual DOM isn’t an official specification, but rather a new method of interfacing with the DOM that allows to frequently update the DOM in a more performant way.
Yet virtual DOM is not an ultimate weapon. E.g. React has to create a new VDOM on each and every render() operation and to rerun (almost) the whole app for diffing. If this involves expesive computations, the performance win trough VDOM-abstraction is no longer worth it. More considerations can be found here and here. Thus, alternative solutions have been suggested, among them the incremental DOM…
Further reading on the topic:
To overcome the pitfalls of the virtual DOM, we should know what exactly has changed and where it happened to be able to update just this very affected spot. We can do this by introducing some template with change slots, so that we know where to look for changes instead of scanning the whole document (doing model diff instead of VDOM diff). The trade-offs of this approach are described here.
The next step in this direction is the introduction of the incremental DOM. Its basic idea is: Every element gets compiled into a series of instructions for rendering and updating the DOM, no intermediary abstraction is required. Note that the instructions aren’t interpreted by the framework’s rendering engine. They are the rendering engine.
These instructions create DOM trees, allow to iterate through them and update them in-place when the data changes (since now we know what and where has changed!). The incremental nature allows for significantly reduced memory allocation during render passes, allowing for more predictable performance. This is especially relevant for smartphones / other devices with constrained resources.
The incremental DOM is not a new notion, it has been introduced in 2015, yet it might get more attention after it has become part of Angular Ivy.
More information on the topic:
Thinking outside of the box
- DOM can also represent XML documents.
- DOM as a standard is just a recommendation. Different layout engines implement it to varying degrees of compliance. Moreover, DOM can be extended by proprietary extensions such as Element.scrollIntoViewIfNeeded(). It allows yout to scroll the current element into the visible area of the window if it’s not already within it. Yet, this feature is not supported by some browsers (surprise, surprise!).
- There is not only DOM, but also CSSOM (or maybe soon CSS Typed OM) — but this is a different story.
[Disclaimer: you think the author misses the point or provides incorrect information? Blame the author AND provide missing / relevant / correct information in your comments — help other readers (and the author) to get it straight! a.k.a. #learningbysharing]