Hello and Welcome to the Web Platform course!

In this first lesson we’ll explore the main features of the DOM, the most fundamental API offered by browsers.

DOM stands for Document Object Model, a representation of an HTML document in nodes and objects.

Browsers expose this model as an API that you can use to interact with the DOM, to

  • explore the page contents
  • interact with elements in the page
  • modify elements in the page

Let’s start!

The DOM is the browser internal representation of a web page.

When the browser retrieves your HTML from your server, the parser analyzes the structure of your code, and creates a model of it.

Based on this model, the browser then renders the page on the screen.

Browsers expose an API that you can use to interact with the DOM. Modern JavaScript frameworks internally use the DOM API to tell the browser what to display on the page.

In Single Page Applications, the DOM continuously changes to reflect what appears on the screen, and as a developer you can inspect it using the browser Developer Tools.

Tip: I don’t cover the use of DevTool here, but you can check my post at https://flaviocopes.com/browser-dev-tools/ to learn more.

The DOM is language-agnostic, and the de-facto standard to access the DOM is by using JavaScript, since it’s the only language that browsers can (currently) run. We can now imagine a (near) future where a language compiled to WebAssembly can interact with the DOM, but this is still all experimental at this point.

The DOM is standardized by WHATWG in the DOM Living Standard Spec.

What is WHATWG? The acronym stands for Web Hypertext Application Technology Working Group. WHATWG was founded by individuals of Apple, the Mozilla Foundation, and Opera Software in 2004, after a W3C workshop. Apple, Mozilla and Opera were becoming increasingly concerned about the W3C’s direction with XHTML, lack of interest in HTML, and apparent disregard for the needs of real-world web developers. So, in response, these organisations set out with a mission to address these concerns and the Web Hypertext Application Technology Working Group was born – https://whatwg.org/faq

With JavaScript you can interact with the DOM to:

  • inspect the page structure
  • access the page metadata and headers
  • edit the CSS styling
  • attach or remove event listeners
  • edit any node in the page
  • change any node attribute

and much more.

The main 2 objects provided by the DOM API, the ones you will interact the most with, are document and window.

Wait: we first need to wait for the DOM ready event

It’s easy to fall in the trap of not waiting that the DOM is ready. We want to go fast, right? But we must access the DOM as soon as we can, but not sooner.

You can do so by adding an event listener to the document object for the DOMContentLoaded event, using the document.addEventListener() method:

document.addEventListener('DOMContentLoaded', (event) => {
  //the DOM is ready, we can do what we want!
})

See the Pen DOMContentLoaded by Flavio Copes (@flaviocopes) on CodePen.

The Window object

The window object represents the window that contains the DOM document.

window.document points to the document object loaded in the window.

Properties and methods of this object can be called without referencing window explicitly, because it represents the global object. So, the previous property window.document is usually called just document.

Properties

Here is a list of useful properties you will likely reference a lot:

  • console points to the browser debugging console. Useful to print error messages or logging, using console.log, console.error and other tools
  • document as already said, points to the document object, key to the DOM interactions you will perform
  • history gives access to the History API
  • location gives access to the Location interface, from which you can determine the URL, the protocol, the hash and other useful information.
  • localStorage is a reference to the Web Storage API localStorage object
  • sessionStorage is a reference to the Web Storage API sessionStorage object

Methods

The window object also exposes useful methods:

  • alert(): which you can use to display alert dialogs
  • postMessage(): used by the Channel Messaging API
  • requestAnimationFrame(): used to perform animations in a way that’s both performant and easy on the CPU
  • setInterval(): call a function every n milliseconds, until the interval is cleared with clearInterval()
  • clearInterval(): clears an interval created with setInterval()
  • setTimeout(): execute a function after n milliseconds
  • setImmediate(): execute a function as soon as the browser is ready
  • addEventListener(): add an event listener to the document
  • removeEventListener(): remove an event listener from the document

Those are just a few of the things window gives you. Here’s a screenshot of typing window in the DevTools console:

See the full reference of all the properties and methods of the window object at https://developer.mozilla.org/en-US/docs/Web/API/Window

The Document object

The document object represents the DOM tree loaded in a window.

Here is a representation of a portion of the DOM pointing to the head and body tags:

DOM, the body and head tags

Here is a representation of a portion of the DOM showing the head tag, containing a title tag with its value:

DOM, the head tag with the title

Here is a representation of a portion of the DOM showing the body tag, containing a link, with a value and the href attribute with its value:

DOM, the body tag with a link

The Document object can be accessed from window.document, and since window is the global object, you can use the shortcut document object directly from the browser console, or in your JavaScript code.

You can get the document title using document.title, and the URL using document.URL. The referrer is available in document.referrer, the domain in document.domain.

From the document object you can get the body and head Element nodes:

  • document.documentElement: the Document node
  • document.body: the body Element node
  • document.head: the head Element node

The DOM nodes

In the preview shown here, you see HTML tags, but in that’s how the DevTools represent the object to you. In reality they are objects.

You can also get a list of all the element nodes of a particular type, like an HTMLCollection of all the links using document.links, all the images using document.images, all the forms using document.forms.

The document cookies are accessible in document.cookie. The last modified date in document.lastModified.

You can do much more, even get old school and fill your scripts with document.write(), a method that was used a lot back in the early days of JavaScript to interact with the pages.

See the full reference of all the properties and methods of the document object at https://developer.mozilla.org/en-US/docs/Web/API/Document

Selectors API


One big part of the DOM API is about working with the Selectors API (https://www.w3.org/TR/selectors-api/), a set of methods that allow you to select a specific element in the DOM:

  • document.getElementById() accepts an id attribute value and returns the element (only one, as id cannot be repeated in the page according to the HTML standard)
  • document.getElementsByTagName() accepts a tag element name and returns all the elements that match it
  • document.getElementsByClassName() accepts a class attribute name and returns all the elements that match it
  • document.querySelector()
  • document.querySelectorAll()

querySelectorAll() and querySelector() are two relatively recent methods, standardized in 2013, which are now available on any modern browser.

They can be safely used, as caniuse.com tells us, and they are even fully supported on IE9 in addition to all the other modern browsers, so there is no reason to avoid them, unless you need to support IE8 (which has partial support) and below.

You pass a CSS selector name, which can be also complex, and the API will return you what it was matched:

  • document.querySelector() returns a single element, the first found
  • document.querySelectorAll() returns all the elements, wrapped in a NodeList object.

These are examples of their use

  • document.querySelector('#test')
  • document.querySelectorAll('.my-class')
  • document.querySelector('#test .my-class')
  • document.querySelector('a:hover')

Tip: a NodeList object is not an array, but you can iterate it with forEach or for..of, or you can transform it to an array with Array.from() if you want.

Types of Nodes

There are different types of nodes, some of which you already saw in the example images above. The main ones you will see are:

  • Document: the document Node, the start of the tree
  • Element: an HTML tag
  • Attr: an attribute of a tag
  • Text: the text content of an Element or Attr Node
  • Comment: an HTML comment
  • DocumentType: the Doctype declaration

Traversing the DOM

The DOM is a tree of elements, with the Document node at the root, which points to the html Element node, which in turn points to its child element nodes head and body, and so on.

From each of those elements, you can navigate the DOM structure and move to different nodes.

Getting the parent

Every element has one and one single parent.

To get it, you can use Node.parentNode or Node.parentElement (where Node means a node in the DOM).

They are almost the same, except when ran on the html element: parentNode returns the parent of the specified node in the DOM tree, while parentElement returns the DOM node’s parent Element, or null if the node either has no parent, or its parent isn’t a DOM Element.

People most usually use parentNode.

Getting the children

To check if a Node has child nodes, use Node.hasChildNodes() which returns a boolean value.

To access all the Element Nodes children of a node, use Node.childNodes.

The DOM also exposes a Node.children method, but it will not just include Element nodes, but it includes also the white space between elements as Text nodes, which is not something you generally want.

Get the children of a node

To get the first child Element Node, use Node.firstElementChild, and to get the last child Element Node, use Node.lastElementChild:

To get the first or last child Element Node

The DOM also exposes Node.firstChild and Node.lastChild, with the difference that they do not “filter” the tree for Element nodes only, and they will also show empty Text nodes that indicate white space.

In short, to navigate children Element Nodes use

  • Node.childNodes
  • Node.firstElementChild
  • Node.lastElementChild

Getting the siblings

In addition to getting the parent and the children, since the DOM is a tree you can also get the siblings of any Element Node.

You can do so using

  • Node.previousElementSibling
  • Node.nextElementSibling

The DOM also exposes previousSibling and nextSibling, but as their counterparts described above, they include white spaces as Text nodes, so you generally avoid them.

Editing the DOM

The DOM offers various methods to edit the nodes of the page and alter the document tree.

With

  • document.createElement(): creates a new Element Node
  • document.createTextNode(): creates a new Text Node

you can create new elements, and add them the the DOM elements you want, as children, by using document.appendChild():

const div = document.createElement('div')
div.appendChild(document.createTextNode('Hello world!'))
  • first.removeChild(second) removes the child node “second” from the node “first”.
  • document.insertBefore(newNode, existingNode) inserts “newNode” as a sibling of “existingNode”, placing it before that in the DOM tree structure.
  • element.appendChild(newChild) alters the tree under “element”, adding a new child Node “newChild” to it, after all the other children.
  • element.prepend(newChild) alters the tree under “element”, adding a new child Node “newChild” to it, before other child elements. You can pass one or more child Nodes, or even a string which will be interpreted as a Text node.
  • element.replaceChild(newChild, existingChild) alters the tree under “element”, replacing “existingChild” with a new Node “newChild”.
  • element.insertAdjacentElement(position, newElement) inserts “newElement” in the DOM, positioned relatively to “element” depending on “position” parameter value. See the possible values.
  • element.textContent = 'something' changes the content of a Text node to “something”.