Different Types of DOM

After years of using JavaScript frameworks (React, Vue, Angular, etc.) extensively you might end up in following states:

  • You can’t explain what DOM is because you never touch it directly
  • You heard that jQuery was hugely popular but never actually use it
  • You know React and Vue using virtual DOM but can’t explain why
  • You heard of shadow DOM but don’t know where it has been utilized
  • You heard that Svelte is fast and don’t use virtual DOM. How is that possible?

Modern frameworks allow you to develop a complete complicated website without decent knowledge of how different types of DOM work. There is no shame about that, more abstract layers emerge everyday in an attempt to increase your productivity.

This post will help you clear the confusion on regular DOM, shadow DOM, and virtual DOM just in case you’re interested, want to contribute to above frameworks, enhance performance, prepare for interviews, or just bring your web dev expertise to a new level.

Regular DOM

Document Object Model (DOM) is a platform and language-neutral interface that allows programs and scripts to dynamically access and update the content, structure, and style of a document (HTML/XML/SVG).

Web browsers handle the DOM implementation details, so we can interact with it using JavaScript and CSS. We can search for nodes and change their details, remove one and insert a new one.

// create a couple of elements in an empty HTML page
var heading = document.createElement('h1')
var heading_text = document.createTextNode('Big Head!')
heading.appendChild(heading_text)
document.body.appendChild(heading)

Every time the DOM changes, the browser has to do two intensive operations: repaint (visual or content changes to an element that do not affect the layout and positioning relative to other elements) and reflow (recalculate the layout of a portion of the page - or the whole page layout).

DOM manipulation is the heart of the modern interactive web but unfortunately frequent manipulations are expensive (it has to re-render everything to change one thing) and hard to manage (too many small APIs).

DOM manipulation is messy and keeping track of the previous DOM state is hard. A solution to this problem is to write your code as if you were recreating the entire DOM whenever state changes. Of course, if you actually recreated the entire DOM every time your application state changed, your app would be very slow and your input fields would lose focus.

jQuery is a fast, small, and feature-rich JavaScript library to make things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. But jQuery does little to solve performance issues.

// hide all <p> elements on button click
$(document).ready(function () {
  $('button').click(function () {
    $('p').hide()
  })
})

Shadow DOM

Shadow DOM is one of the three Web Component standards: HTML Templates, Shadow DOM and Custom elements.

An important aspect of web components is encapsulation — being able to keep the markup structure, style, and behavior hidden and separate from other code on the page so that different parts do not clash, and the code can be kept nice and clean.

Shadow DOM is a set of JavaScript APIs for attaching an encapsulated shadow DOM tree to an element — which is rendered separately from the main document DOM — and controlling associated functionality. In this way, you can keep an element’s features private, so they can be scripted and styled without the fear of collision with other parts of the document.

const header = document.createElement('header')
const shadowRoot = header.attachShadow({mode: 'open'})
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>'

Browsers have used shadow DOM for a long time to encapsulate the inner structure of an element. Think for example of a video element, with the default browser controls exposed. All you see in the DOM is the video element, but it contains a series of buttons and other controls inside its shadow DOM.

This creates a barrier between what the developer and the browser can reach; the developer cannot access the Shadow DOM in the same way they would with nested elements, while the browser can render and modify that code the same way it would with nested elements.

Shadow DOM must always be connected to an existing element, either through attaching it as a literal element or through scripting. In JavaScript, you attach Shadow DOM to an element using Element.attachShadow().

You can think of the shadow DOM as a DOM within a DOM. It is its own isolated DOM tree with its own elements and styles, completely isolated from the regular DOM.

There are some bits of shadow DOM terminology to be aware of:

  • Shadow host: The regular DOM node that the shadow DOM is attached to.
  • Shadow tree: The DOM tree inside the shadow DOM.
  • Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
  • Shadow root: The root node of the shadow tree.

Shadow DOM fixes the global nature of HTML, CSS. It introduces scoped styles to the web platform. Without tools or naming conventions, you can bundle CSS with markup, hide implementation details, and author self-contained components in vanilla JavaScript.

You don’t have to author web components that use shadow DOM. But when you do, you take advantage of its benefits (CSS scoping, DOM encapsulation, composition) and build reusable custom elements, which are resilient, highly configurable, and extremely reusable.

If custom elements are the way to create a new HTML (with a JS API), shadow DOM is the way you provide its HTML and CSS. The two APIs combine to make a component with self-contained HTML, CSS, and JavaScript.

Virtual DOM

If you’ve used JavaScript frameworks in the last few years, you’ve probably heard the phrase ‘the virtual DOM is fast’, often said to mean that it’s faster than the regular DOM.

Virtual DOM is an in-memory framework-specific internally representation of regular DOM, manipulating the virtual DOM is much faster because framework batches much of the changes and performs a unique update to the regular DOM, by changing all the elements that need to be changed at the same time, so the repaint and reflow the browser must perform to render the changes are executed just once.

What make virtual DOM really fast is: Efficient diff algorithms, batching DOM read/write operations, and efficient update of sub-tree only.

You can think of the virtual DOM like a blueprint. It contains all the details needed to construct the DOM, but because it doesn’t require all the heavyweight parts that go into a real DOM, it can be created and changed much more easily.

This approach enables the declarative API of JavaScript frameworks: You tell them what state you want the UI to be in, and they makes sure the DOM matches that state. This abstracts out the attribute manipulation, event handling, and manual DOM updating that you would otherwise have to use to build your app.

Essentially, JavaScript frameworks take out the tedious traversing of the DOM tree and does this lower level stuff to make everything easier for developers, and run way more efficiently and smoothly.

It’s important to understand that virtual DOM isn’t a feature. It’s a means to an end, the end being declarative, state-driven UI development. Virtual DOM is valuable because it allows you to build apps without thinking about state transitions, with performance that is generally good enough. That means less buggy code, and more time spent on creative tasks instead of tedious ones.