Skip to content

TL;DR; (Too Long; Didn't Read) DOM Lessons

Ra'Shaun Stovall edited this page Apr 18, 2017 · 1 revision

Modern DOM Interfaces

The following are the minimum set of modern DOM APIs you should be familiar with today. Each API has a console example snippet. Just copy / pasta the examples into the developer console and "It just works!" ™

MDN DOM Levels https://developer.mozilla.org/fr/docs/DOM_Levels

Salute🙏 to @domenic for the alley-oop 🏀, and being such a prolific contributor to the community. Also owe ya a matcha tea 🍵 for the JSDOM help.

"What's the difference between the W3C and the WHATWG ?"

https://www.reddit.com/r/javascript/comments/5swe9b/what_is_the_difference_between_the_w3c_and_the

CSS Selectors

https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors

Text

The Text interface represents the textual content of Element or Attr. If an element has no markup within its content, it has a single child implementing Text that contains the element's text. However, if the element contains markup, it is parsed into information items and Text nodes that form its children.

WIP Internet Explorer polyfill PONYFILL - https://github.com/sindresorhus/ponyfill

See also Document.createTextNode https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode

Console example snippet

( new Text ('Yo! 👊') )
  .textContent  // "Yo! 👊"

const empty = new Text ()
empty.textContent // ""
empty.textContent = 'Hey! ✋'
empty.textContent // 'Hey! ✋'

document.body
  .append (empty)

//<body>... "Hey! ✋"</body>

MDN Text () Constructor Documentation - https://developer.mozilla.org/en-US/docs/Web/API/Text/Text

MDN Text Documentation - https://developer.mozilla.org/en-US/docs/Web/API/Text

Template Literals

Template literals are string literals allowing embedded expressions (string interpolation). As you may have noticed the Grave accent (``) is used instead of double/single quotes. You can use multi-line strings and string interpolation features with them. They were called "template strings" in prior editions of the ES2015 specification.

Console example snippet

void `Hello !`

void `Line one of text
  Line two (with preserved \n`

const greeting = 'world'
void `Hello ${greeting} !`

const tag = (...fragments) =>
  ({ parts: fragments.shift (), tokens: fragments })

tag `part before token ${greeting} part after token`

Template Literals

"Tagged" Template Literals

Raw Strings https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Raw_strings

ECMAScript 2015 Specification Standard http://www.ecma-international.org/ecma-262/6.0/#sec-template-literals

ECMAScript 2017 Draft https://tc39.github.io/ecma262/#sec-template-literals

Templates

The HTML <template> element is a mechanism for holding client-side content that is not to be rendered when a page is loaded but may subsequently be instantiated during runtime using JavaScript.

Think of a template as a content fragment that is being stored for subsequent use in the document. While the parser does process the contents of the

Snippet cannot be run in console. Must save as HTML file

<ul id='songs'>
</ul>

<template id='song'>
  <li></li>
</template>

<script defer>

let songs = ['Nothing else matters', 'Battery', 'One']

const template = document.querySelector ('template#song')
    , ul = document.querySelector ('ul#songs')
    , li = template.content.querySelector ('li')

for (let song in songs) {
  li.textContent = song
  songs.append (li)
}

</script>

MDN https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

HTML5 https://www.w3.org/TR/html5/scripting-1.html#the-template-element

Living Standard https://html.spec.whatwg.org/multipage/scripting.html#the-template-element

HTML5 Rocks https://www.html5rocks.com/en/tutorials/webcomponents/template

DocumentFragment

AKA "How did 'front-end frameworks' miss this?"

The DocumentFragment has been proven to be the fastest method of updating DOM Tree. The implementation was spec'd all the way back in DOM Level 1. We should be using this today!

The DocumentFragment interface represents a minimal document object that has no parent. It is used as a light-weight version of Document to store a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn't part of the actual DOM's structure, changes made to the fragment don't affect the document, cause reflow, or incur any performance impact that can occur when changes are made.

DocumentFragments are DOM Nodes. They are never part of the main DOM tree. The usual use case is to create the document fragment, append elements to the document fragment and then append the document fragment to the DOM tree. In the DOM tree, the document fragment is replaced by all its children.

A common use for DocumentFragment is to create one, assemble a DOM subtree within it, then append or insert the fragment into the DOM using Node interface methods such as append(), appendChild() or `insertBefore(). Doing this moves the fragment's nodes into the DOM, leaving behind an empty DocumentFragment. Because all of the nodes are inserted into the document at once, only one reflow and render is triggered instead of potentially one for each node inserted if they were inserted separately.

Since the document fragment is in memory and not part of the main DOM tree, appending children to it does not cause page reflow (computation of element's position and geometry). Consequently, using document fragments often results in better performance.

This interface is also of great use with Web components: <template> elements contain a DocumentFragment in their HTMLTemplateElement.content property.

See also Document.createDocumentFragment https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment

Console example snippet

const browsers = ['Chrome', 'Edge', 'Brave', 'Firefox']
    , fragment = document.createDocumentFragment ()

browsers.forEach (name => {
  const li = document.createElement ('li')
  li.textContent = name
  fragment.append (li)
})

document.querySelector ('ul')
  .append (fragment)

MDN https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment

Living Standard https://dom.spec.whatwg.org/#interface-documentfragment

DOM Level 3 https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-B63ED1A3

DOM Level 2 https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-B63ED1A3

DOM Level 1 https://www.w3.org/TR/DOM-Level-1/level-one-core.html#ID-B63ED1A3

Selectors API Level 2 https://dev.w3.org/2006/webapi/selectors-api2

Document.querySelector

Returns the first Element within the document (using depth-first pre-order traversal of the document's nodes by first element in document markup and iterating through sequential nodes by order of amount of child nodes) that matches the specified group of selectors.

Console example snippet

// Select the <head> element
const head = document.querySelector ('head')
console.log (head)

// Select the <body> element
const body = document.querySelector ('body')
console.log (body)

// Returns the first `<h1>`
const h1 = document.querySelector ('h1')
console.log (h1)

// Returns the *first* HTMLElement with the `id` of `foo`
// (See https://developer.mozilla.org/en-US/docs/Web/API/Element/id)
const foo = document.querySelector ('#foo')
console.log (foo)

// Returns the *first* HTMLElement with `posts` within its `classList`
// (See https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
const posts = document.querySelector ('.posts')
console.log (posts)

MDN https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

Selectors API Level 2 https://dev.w3.org/2006/webapi/selectors-api2/#interface-definitions

Document.querySelectorAll

Returns the first Element within the document (using depth-first pre-order traversal of the document's nodes by first element in document markup and iterating through sequential nodes by order of amount of child nodes) that matches the specified group of selectors.

Console example snippet

// Returns a collection of *all* HTMLElement(s) with `posts` within its `classList`
// (See https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
const posts = document.querySelector ('.posts') // [<a class="posts">,<section class="posts">, ...]
console.log (posts)

// Returns a collection of *all* HTMLParagraphElement(s)
const paragraphs = document.querySelector ('p')
console.log (paragraphs) // [<p>,<p>, ...]

// Returns an collection if not found
const zebras = document.querySelector ('zebras')
console.log (zebras) // []

MDN https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll

Selectors API Level 2 https://dev.w3.org/2006/webapi/selectors-api2/#interface-definitions

Node.textContent

Console example snippet

// Must be on any page with an `<h1>`
const h1 = document.querySelector ('h1')
h1.textContent // "Hello World"

MDN https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent

Living Standard https://dom.spec.whatwg.org/#dom-node-textcontent

DOM Level 1 https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-textContent

MutationObserver

Why? MutationObserver has comparable performance to Proxy.

let target = document.querySelector ('h1')
 
// create an observer instance
let callback = mutations =>
  mutations.forEach
    (mutation => console.log (mutation.type))

let observer = new MutationObserver (callback)
 
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true }
 
// pass in the target node, as well as the observer options
observer
  .observe (target, config)
 
target.textContent = 'Triggering mutation observer'

// later, you can stop observing
observer.disconnect()

(shouts to @ebidel for this one) https://gist.github.com/ebidel/d923001dd7244dbd3fe0d5116050d227

Mutation Observer Tutorials

MDN https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

Living Standard https://dom.spec.whatwg.org/#mutation-observers https://dom.spec.whatwg.org/#mutationobserver

DOM Level 4 https://www.w3.org/TR/dom/#mutationobserver

Rendering (Re-Flow, Re-Layout, Re-Paint)

Minimizing Browser Reflow - https://developers.google.com/speed/articles/reflow https://www.phpied.com/rendering-repaint-reflowrelayout-restyle

Rendering (Layout, Reflow, Repaint, & Redraw)

  • render tree - the visual part of the DOM tree
  • nodes in the render tree are called frames or boxes
  • recalculating parts of the render tree is called reflow (in Mozilla), and called layout in every other browser, it seems
  • Updating the screen with the results of the recalculated render tree is called repaint, or redraw (in IE/DynaTrace)
  • SpeedTracer introduces the notion of "style recalculation" (styles without geometry changes) vs. "layout"

10 ways to minimize reflows & improve performance https://www.sitepoint.com/10-ways-minimize-reflows-improve-performance

MutationEvents (deprecated)

No longer used https://www.w3.org/TR/DOM-Level-3-Events/#legacy-mutationevent-events