HTML DOM :: Node :: Document | Element :: CanIUse.com
Document Object has both Model (DOM) and Interfaces.
Interface hierarchy: Node > (Document|Element)
Both have properties and methods, with the descendents inheriting from Node.
Examples
Libraries
UPDATE: Use base.js
library
o.toDOM = (parent, child, at) => {
/** Insert into DOM
* @param {Element} parent
* @param {DOMString} child gets parsed
* @param {any} at optional; prepend on any, else append
*
* USAGE: toDOM(css('#foo .bar'), '<h3>Foo</h3> <p>bar baz</p>', 1)
*/
//o.profStart('toDOM')
// https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
at = at ? 'afterbegin' : 'beforeend'
// ... "? Prepend : Append" (amongst siblings; children of parent node).
if (parent && child) {
parent.insertAdjacentHTML(at, child)
return true
}
return false
}
o.purge = (target) => { // Remove all child nodes
if (!target) return false
while (target.firstChild) {
target.removeChild(target.firstChild)
}
}
o.replaceContent = (node, html) => node.textContent = html
o.create = (name) => {
return document.createElement(name)
}
o.id = (id) => {
return document.getElementById(id)
}
/* root is context node */
o.css = (selector, root) => {
root = root ? root : document
return root.querySelector(selector)
}
o.cssAll = (selector, root) => {
root = root ? root : document
return root.querySelectorAll(selector)
}
o.class = (name, root) => {
root = root ? root : document
return root.getElementsByClassName(name)
}
o.tags = (tag, root) => {
root = root ? root : document
return root.getElementsByTagName(tag)
}
Others
DOM Location
console.log("origin",window.location.origin) // origin http://127.0.0.1:5500
console.log("window",window.location.pathname) // /sub/
console.log("top",top.location.pathname) // /sub/
console.log("parent",parent.location.pathname) // /sub/
// `top` and `parent` useful @ iframe
DOM Position : el.getBoundingClientRect()
| CanIUse @ 99.5%
div {
width: 400px;
height: 200px;
padding: 20px;
margin: 50px auto;
background: #990;
}
<div> </div>
const
getRect = el => {
/*************************************************************
* Convert the unusable return of getBoundingClientRect(),
* which is the morphodite DOMRect, into a USEFUL object.
*
* USEAGE: [...Object.keys(getRect(el))].map(perKey)
************************************************************/
const {
top, right, bottom, left, width, height, x, y
} = el.getBoundingClientRect()
return {top, right, bottom, left, width, height, x, y}
},
src = document.querySelector('DIV'),
got = getRect(src),
put = (key) => {
const tgt = document.createElement('PRE')
tgt.textContent = `${ key } : ${ got[key] }`
document.body.appendChild(tgt)
}
;[...Object.keys(got)].map(put)
HTML DOM :: Element > Node < Document
Test per CSS
Element.matches()
(CanIUse.com)
Test if the CSS selectorString
matches the element; checks if el
is the selector.
var result = el.matches(selectorString) // true | false
Find per CSS
Element.closest()
(CanIUse.com)
Walk up the DOM, from targetEl
, to find the closest element (closestEl) matching the CSS selectors
.
var closestEl = targetEl.closest(selectors) // HTMLElement | null
document.querySelector(CSS-selector)
Examples
document.querySelector('main.group-1')
document.querySelector('#logo>svg')
document.querySelector('div.user-panel.main input[name="login"]')
Returns first element matched, else null.
parentNode.querySelectorAll(CSS-selector)
Returns a NodeList
representing a list of matching elements.
contextNode.querySelectorAll(selector)
Insert | Replace :: HTML as String (parsed as HTML and inserted as nodes)
insertAdjacentHTML()
el.insertAdjacentHTML(position, STRING)
Append (next sibling under parent)
function appendNextSibling(parent, child) { !!(parent) && parent.insertAdjacentHTML('beforeend', child) } var h = {parent: aContainerEl, child: nextSiblingEl}
Better, parameterize the choice of append/prepend:
function toDOM(parent, child, at) { at = at ? 'afterbegin' : 'beforeend' // default appends if (parent && child) parent.insertAdjacentHTML(at, child) }
@ HTML ... aContainerEl existingSiblingEl nextSiblingEl <== inserts this. ...
node.textContent
Replace (text of a node)
const node = document.querySelector('main.group-1') ,txt = 'Foo' node.textContent = txt
Comparisons:
el.insertAdjacentHTML(position, STRING)
- This method of the Element interface parses the string as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position. It does not reparse the element it is being used on, and thus it does not corrupt the existing elements inside that element. This avoids the extra step of serialization, making it much faster than direct
innerHTML
manipulation, usually. (For counter-example, whereinnerHTML
is faster, see dbmon challengemap
case.)
- This method of the Element interface parses the string as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position. It does not reparse the element it is being used on, and thus it does not corrupt the existing elements inside that element. This avoids the extra step of serialization, making it much faster than direct
el.innerHTML
- Parses the string into HTML
node.textContent
- Sans parsing; faster than either (3x
innerHTML
), since it does not parse, though it's therefore limited to text (not even html entities).
- Sans parsing; faster than either (3x
el.innerText
- Do not use
innerText
. It triggers Layout Thrashing.
- Do not use
Insert | Replace :: HTML as Node
append()
(CanIUse.com)
parentNode.append(...nodesToAppend)
append()
/prepend()
do so relative to sibling node(s) of parentNode
; not parentNode
itself. Inserts one or more nodes (nodesToAppend
/nodesToPrepend
) after/before the collection of siblings under parentNode
.
JS
var el = document.getElementById("target") var pre = document.createElement('PRE') el.append(pre) pre.textContent = `Here is AJAX Response: ${xhr.response}`
HTML
<div id="target"> <div> <!-- ... Pre-existing ... --> </div> <pre>Here is ...</pre> </div>
prepend()
(CanIUse.com)
-
parentNode.prepend(...nodesToPrepend)
JS
el = document.createElement('UL') html = '<li>bar</li>' el.insertAdjacentHTML('afterbegin', html) document.getElementById('target').prepend(el)
HTML
<section id=target> <ul> <li>bar</li> <!-- inserts this li node. --> <li>foo</li> ... </ul> ...
JS
parent = document.createElement('DIV') p = document.createElement('P') parent.prepend("Some text", p) console.log(parent.childNodes) // NodeList [ #text "Some text", <p> ]
appendChild()
/ insertBefore()
are Older APIs (IE compatible) of append()
/prepend()
parentNode.appendChild(aChild);
parentNode.insertBefore(newNode, referenceNode)
E.g.,
li = document.createElement('LI') // Create <li> node
txtNode = document.createTextNode(str) // Create text node
li.appendChild(txtNode) // Append it to <li>
ul = document.getElementById('target')
ul.insertBefore(li, ul.childNodes[0]) // prepend this li to siblings under ul
node.childNodes[0]
& node.nodeValue
Use to replace TEXT at one of many siblings; leaves all else unaffected.
node.childNodes[0]
JS
var el = document.getElementById("target").childNodes[0] el.nodeValue = "SUCCESS" // "Target" replaced with "SUCCESS"
- Unlike
nodeValue
, using eitherinnerHTML
ortextContent
methods replaces all the content oftarget
(all its child nodes; html & text).
- Unlike
HTML
<div id="target"> Target <pre>Save</pre> </div>
Insert MANY :: DocumentFragment
| createDocumentFragment()
Node.appendChild()
-
const messages = document.querySelectorAll('#messages div.msg') || [ghost] // Iterate over a node list ;[...messages].map(msg => { const ageEl = messages.querySelector('div.date>span', msg) || ghost ageEl.replaceContent(ageNow(ageEl.dataset.utcTime)) })
const node = document.getElementById('target') ,frag = document.createDocumentFragment() var i = 0 while (i < app.ITEMS) { // Not yet in DOM var li = document.createElement('li') li.textContent = 'Item number ' + i frag.appendChild(li) i++ } // Insert into DOM. node.appendChild(frag)
Remove child elements
node.removeChild(child)
- Purportedly much faster than "
innerHTML = ''
".
Referencing only the target node:
let node = document.getElementById('target')
if (node.parentNode) {
node.parentNode.removeChild(node)
}
Referencing only the parent node:
let parent = document.getElementById('parent')
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
Or
let parent = document.getElementById('parent')
while (parent.firstChild) {
parent.firstChild.remove()
}
var cNode = node.cloneNode(false)
node.parentNode.replaceChild(cNode, node)
Layout Thrashing :: Layout Reflow Culprits
... properties or methods that trigger the browser to synchronously calculate the style and layout; ... a common performance bottleneck.
- Box metrics;
.clientXXX
,.offsetXXX
, ... - Scroll;
.scrollXXX
Focus;
.focus()
.innerText
- "… triggers a reflow to ensure up-to-date computed styles.. Use
textContent
instead.
⋮
- "… triggers a reflow to ensure up-to-date computed styles.. Use