Chapter 6: Miscellaneous
6.1 Mutation observer
MutationObserver is a built-in object that observes a DOM element and fires a callback in case of changes. It could react on any changes within DOM subtree.
Syntax
let observer = new MutationObserver(callback);
observer.observe(node, config);config is an object with boolean options:
childListβ changes in the direct children of node.subtreeβ in all descendants of node.attributesβ attributes of node.attributeFilterβ an array of attribute names, to observe only selected ones.characterDataβ whether to observenode.data(text content).attributeOldValueβ if true, pass both the old and the new value of attribute to callback.characterDataOldValueβ if true, pass both the old and the new value ofnode.datato callback.
Changes are passed in the first argument as a list of MutationRecord objects, and the observer itself as the second argument.
MutationRecord object:
typeβ mutation typeattributes: attribute modified
characterData: data modified, used for text nodes
childList: child elements added/removed
targetβ where the change occurred: an element forattributes, or text node forcharacterData, or an element for achildListmutationaddedNodes/removedNodesβ nodes that were added/removedpreviousSibling/nextSiblingβ the previous and next sibling to added/removed nodesattributeName/attributeNamespaceβ the name/namespace (for XML) of the changed attributeoldValueβ the previous value, only for attribute or text changes, if the corresponding option is setattributeOldValue/characterDataOldValue
Additional methods
observer.disconnect()β stops the observationobserver.takeRecords()- gets a list of unprocessed mutation records which the callback did not handle.
Observers use weak references to nodes internally. If a node is removed from DOM, it becomes garbage collected.
6.2 Selection and Range
Range
Range is a pair of βboundary pointsβ: range start and range end.
Each point represented as a parent DOM node with the relative offset from its start.
range.setStart(node, offset)range.setEnd(node, offset)(not include the end index)
Selecting parts of text nodes
Range object
properties
startContainer,startOffsetβ node and offset of the startendContainer,endOffsetβ node and offset of the endcollapsed-trueif the range starts and ends on the same pointcommonAncestorContainer- the nearest common ancestor of all nodes within the range
methods
setStart(node, offset)/setEnd(node, offset)setStartBefore(node)/setEndBefore(node)setStartAfter(node)/setEndAfter(node)selectNode(node)selectNodeContents(node)collapse(toStart)cloneRange()deleteContents()extractContents()- remove range content from the document and return asDocumentFragment.cloneContents()- clone range content and return asDocumentFragment.insertNode(node)- insert node into the document at the beginning of therange.surroundContents(node)- wrapnodearound range content.
Selection object
The document selection is represented by Selection object, that can be obtained as window.getSelection() or document.getSelection().
A selection may include zero or more ranges. In practice, only Firefox allows to select multiple ranges in the document.
properties
A selection has a start, called anchor, and the end, called focus.
anchorNode/anchorOffsetfocusNode/focusOffsetisCollapsedβ true if selection selects nothing.rangeCountβ count of ranges in the selection, maximum 1 in all browsers except Firefox.
Selection end may be in the document before start. (Left-to-right direction)
events
elem.onselectstartβ when a selection starts on elemdocument.onselectionchange- whenever a selection changes
methods
getRangeAt(i)addRange(range)removeRange(range)removeAllRanges()empty()β alias toremoveAllRanges
To select, remove the existing selection first.
Selection in form controls
Form elements, such as input and textarea provide special API for selection, without Selection or Range objects.
properties
input.selectionStartβ position of selection start (writeable)input.selectionEndβ position of selection end (writeable),input.selectionDirectionβ selection direction (forward,backward,none)
events
input.onselect
methods
input.select()- selects everything in the text controlinput.setSelectionRange(start, end, [direction])input.setRangeText(replacement, [start], [end], [selectionMode])selectβ selects the newly inserted text
start- collapses before the start
end- collapses after the end
preserve- preserve the selection
Making unselectable
CSS property:
user-select: nonePrevent default action in
onselectstartormousedownevents.document.getSelection().empty()
6.3 Event loop: microtasks and macrotasks
Browser JavaScript execution flow, as well as in Node.js, is based on an event loop.
Event Loop
JavaScript engine does nothing most of the time, only runs if a script/handler/event activates.
Dequeue and run the oldest task from the macrotask queue.
Execute all microtasks:
While the microtask queue is not empty, dequeue and run the oldest microtask.
Render changes if any.
If the macrotask queue is empty, wait till a macrotask appears.
If a task comes while the engine is busy, it's enqueued into the macrotask queue. They are processed on "first come β first served" basis.
Rendering never happens while the engine executes a task.
If a task takes too long, it raises an alert like "Page Unresponsive" suggesting to kill the whole page.
Use-case 1: splitting CPU-hungry tasks
We can split the big task into pieces.
For long heavy calculations, we could use Web Workers in a parallet thread.
Use case 2: progress indication
Another benefit of splitting heavy tasks for browser scripts is that we can show progress indication.
Use case 3: doing something after the event
In an event handler we may decide to postpone some actions until the event bubbled up and was handled on all levels. We can do that by wrapping the code in zero delay setTimeout.
Macrotasks and Microtasks
An execution of .then/catch/finally handler becomes a microtask.
queueMicrotask(func) queues func for execution in the microtask queue.
All microtasks are completed before any other event handling or rendering or any other macrotask takes place.
code- synchronous callpromise- microtasktimeout- macrotask
If weβd like to execute a function asynchronously (after the current code), but before changes are rendered or new events handled, we can schedule it with queueMicrotask.
Last updated