-
-
Notifications
You must be signed in to change notification settings - Fork 17
TL;DR; (Too Long; Didn't Read) DOM Lessons
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
https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors
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 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
-
2Ality - http://www.2ality.com/2015/01/template-strings-html.html
-
https://developers.google.com/web/updates/2015/01/ES6-Template-Strings
-
https://developer.mozillajorg/en-US/docs/Web/JavaScript/Reference/Template_literals
"Tagged" Template Literals
- https://developers.google.com/web/updates/2015/01/ES6-Template-Strings#tagged_templates
- https://leanpub.com/understandinges6/read#leanpub-auto-multiline-strings#leanpub-auto-tagged-templates
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals
- http://exploringjs.com/es6/ch_template-literals.html#_implementing-tag-functions
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
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
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)
- DOM Fragments - http://ejohn.org/blog/dom-documentfragments
- JavascriptDocumentFragment - https://davidwalsh.name/documentfragment
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
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
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
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
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
- https://dev.opera.com/articles/mutation-observers-tutorial
- https://tiffanybbrown.com/2013/09/mutation-observer
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
Minimizing Browser Reflow - https://developers.google.com/speed/articles/reflow https://www.phpied.com/rendering-repaint-reflowrelayout-restyle
- 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
No longer used https://www.w3.org/TR/DOM-Level-3-Events/#legacy-mutationevent-events