Is a programming interface for HTML and XML documents that represents a document composed of multiple layers of nodes through which parts of a page can be added, removed, and modified.

The node hierarchy

Any HTML or XML document can be represented in the DOM as a hierarchy of nodes.

The Node type

DOM Level1 describes the Node interface, which is mandatory for all DOM Node types, is implemented as a Node type in JS, and is directly accessible in all browsers except Internet Explorer. In JS, all Node types inherit from Node types, so all types share the same basic properties and methods.

Each node has the nodeType attribute, which indicates the type of the node. Node types are represented by 12 numeric constants defined on the Node type:

  • Node. ELEMENT_NODE (1)
  • Node. ATTRIBUTE_NODE (2)
  • Node.text_node (3)
  • Node. CDATA_SECTION_NODE (4)
  • Node. ENTITY_REFERENCE_NODE (5)
  • Node. ENTITY_NODE (6)
  • Node. PROCESSING_INSTRUCTION_NODE (7)
  • Node.COM MENT_NODE (8)
  • Node. DOCUMENT_NODE (9)
  • Node. DOCUMENT_TYPE_NODE (10)
  • Node. DOCUMENT_FRAGMENT_NODE (11)
  • Node. NOTATION_NODE (12)

Node types can be determined by these constant comparisons.

  1. NodeName and nodeValue

Holds information about the node. It is a good idea to check the node type before using these two attributes. For elements, nodeName is always equal to the element’s label name, and nodeValue is always null.

  1. Relationship between nodes

Each node has a childNodes property, which contains an instance of NodeList. NodeList is an array-like object, not an instance of Array, but its value can be accessed using brackets, and it also has a Length property, which represents the number of nodes in NodeList at that point in time. A NodeList is usually said to be a live live object.

To convert a NodeList object to an Array, use ES6’s array.from () static method:

let arrayOfNodes = Array.from(someNode.childNodes);

Each node has a parentNode attribute that points to its parent element in the DOM tree.

In ChildNode, the firstChild is firstChild and the lastChild is lastChild;

PreviousSibling; nextSibling;

HasChildNodes () is used to determine whether a node has children. Returning true indicates that the node has one or more children.

The ownerDocument property is a pointer to a document node that represents the entire document.

  1. Manipulation of the node

AppendChild () — Add nodes at the end of the list of childNodes and return the newly added node.

InsertBefore () — Inserts a node into a specific location in childNodes. It takes two arguments: the node to be inserted and the reference node. The node to be inserted becomes the previous sibling of the reference node and is returned.

ReplaceChild () — Replaces a node with an inserted node, taking two arguments: the node to be inserted and the node to be replaced. The node to be replaced is returned and completely removed from the document tree, and the node to be inserted is replaced.

RemoveChild – Removes a node, taking one parameter: the node to be removed, and the removed node is returned.

The above four methods must be obtained before using the parent node.

CloneNode () — returns the exact same node as the one on which it was called, takes a Boolean parameter indicating whether or not the node is deeply copied (that is, the node and its entire DOM tree are copied), and if false, only the node on which the method was called is copied.

Normalize () – Processes text nodes in a document subtree. Calling this method on a node checks all descendants of that node. If an empty text node is found, delete it. If two sibling nodes are adjacent, they are merged into a single text node.

The Document type

Is the type of document node in JS. In the browser, the document object Document is an instance of HTMLDocument (HTMLDocument inherits Document), representing the entire HTML page.

Document is an attribute of the Window object and is therefore a global object. The most common way to get a Document object is through an instance of HTMLDocument.

Child nodes can be of type DocumentType (at most one), Element (at most one), ProcessingInstruction, or Comment.

  1. Document child node

Shortcuts to accessing child nodes:

1) The documentElement attribute, which always points to the/element in the HTML page.

2) The body attribute, which points directly to the/element.

Document. documentElement and document.body are supported by all major browsers.

Another possible child of the Document type is DocumentType, <! The docType > tag is a separate part of the document, and its information can be obtained through the docType attribute (document.doctype in the browser).

AppendChild (), removeChild (), and replaceChild () are not used on document objects because the document type (if it exists) is read-only and can only have one child node of type Element (that is, < HTML >, which already exists).

  1. Document information

Title attribute — Contains text in the

element. Modifying the title attribute does not change the element.

URL property – Contains the full URL of the current page (URL in the address bar).

Domian property – Contains the domain name of the page.

Referrer property – Contains the URL of the page linked to the current page, or an empty string if there is no current page source.

Only the domain attribute can be set, and the value is limited. You cannot set a value that is not included in the URL. For example, if the URL contains a subdomain name, such as p2p.wrox.com, you can set domain to wrox.com.

When a page contains panes (<frame>) or inline panes (<iframe>) from different subdomains, pages of different subdomains cannot communicate with JS. You can set document.domain to the same value on each page, and these pages can access each other’s JS objects.

Once relaxed, it cannot be tightened again. Document.domain set to “wrox.com” cannot be set back to “p2p.wrox.com”.

  1. Positioning elements

GetElementById () — Gets an element by ID, taking one argument: To get the element’s ID, the ID must exactly match the value of the element’s ID attribute on the page, including case and case. If multiple elements with the same ID are found, the first element that appears in the document is returned.

GetElementsByTagName () — Gets elements by tag name, takes one argument: to get the tag name of the element, returns NodeList containing zero or more elements. In an HTML document, an HTMLCollection object is returned, much like NodeLsit. You can also get specific elements from HTMLCollection using the brackets or item () method. You can also get the number of elements using the Length attribute. There is an additional method, namedItem (), that obtains a reference to an item through the tag’s name attribute. You can pass * to get all the elements in the document.

GetElementsByName () — Returns all elements with the given name attribute. HTMLCollection is also returned. If namedItem () is used, only the first item is retrieved (because all items have the same name attribute).

  1. Special collections

Are instances of HTMLCollection, and like all HTMLCollection objects, their content is updated in real time to match the current document content.

Document. Anchors – Contains all the <a> elements in the document that have the name attribute.

Document. applets — contains all <applet> elements in the document, but <applet> elements are not recommended. This collection is deprecated.

Document. The forms — — contains document all < the from > element (with the document. The getElementsByTagName (” from “) returns the same result).

Document. The images – contains the document all < img > element (with the document. The getElementsByTagName (” img “) returns the same result).

Document.links — Contains all <a> elements in the document with the href attribute.

5. Document writing

Write () — Simply write text.

Writeln () — writes text, appending a newline character (\n) to the end of the string.

The above two methods can be used to dynamically add content to a page during page loading. They are often used to dynamically include external resources such as JS files

Document. write (“<script type =\ “text/javascript” SRC = “file.js”>”+”</script>”); // The last “</script>”.

Open () — Opens the web page output stream.

Close () — Closes the page output stream.

Element type

Represents an XML or HTML element, exposing the ability to access the element’s tag name, child nodes, and attributes.

You can get the tagName of an element using either the nodeName or tagName attribute. In HTML, the tagName of an element is always all uppercase. In XML, including XHTML, tag names are always case – consistent with the source code. If you are not sure which document the script is running in, it is a good idea to convert the tag name to lowercase for comparison:

Element. The tagName. ToLowerCase ()

  1. The HTML element

Id, the unique identifier of the element in the document.

Title, which contains additional information about an element, usually in the form of a prompt bar.

Lang, language code for element content (rarely used).

Dir, the written direction of the language, “LTR” – left to right, “RTL” – right to left, rarely used.

ClassName, equivalent to the class attribute, is used to specify the CSS class of the element.

Can be used to get the corresponding property value, can also be used to modify the corresponding value.

  1. Obtain attribute

GetAttribute (), setAttribute (), and removeAttribute (), used to manipulate attributes, including those defined on the HTMLElement type.

let div = document.getElementById("myDiv"); alert(div.getAttribute("class")); // "bd" // we pass "class" instead of "className" because className is spelled that way as an object property.Copy the code

GetAttribute () can also get the value of a custom attribute that is not an official attribute in HTML:

<div id = "myDiv" my_special = "hello!" ></div> // let value = div.getAttribute("my_special");Copy the code

Attribute names are case insensitive. In addition, according to the REQUIREMENTS of the HTML5 specification, custom attributes should be prefixed with data- to facilitate validation.

Two of the attributes accessed through the DOM object return values that are different from those obtained using getAttribute () : the style attribute and the event handler (the event attribute).

When DOM programming is done, getAttribute () is often abandoned and only object attributes are used. GetAttribute () is used to get the value of a custom attribute.

  1. Set properties

SetAttribute (), which takes two arguments: the name of the attribute to be set and the value of the attribute. If the attribute already exists, the original value is replaced with the specified value. Applies to HTML attributes as well as custom attributes. Property names set using this method are normalized to lowercase.

RemoveAttribute () is used to remove attributes from elements, not much, but it gives you control over which attributes to include when serializing DOM elements.

  1. The attributes property

Contains an instance of NamedNodeMap, which is a “live” collection similar to NodeList. Each attribute of the element is represented as an Attr node and stored in the NamedNodeMap object.

The NameNodeMap object contains the following methods:

  • GetNamedItem (name) – Returns the node whose nodeName attribute is equal to name;

  • SetNamedItem (node) – Adds a node to the list indexed by its nodeName;

  • RemoveNamedItem (name) – Remove the node whose nodeName attribute is equal to name;

  • Item (pos) – Returns the node at the index position pos.

For each node in the Attributes attribute, nodeName is the name of the corresponding attribute, and nodeValue is the value of the attribute.

let id = element.attributes.getNamedItem("id").nodeValue; // Let id = element.attributes["id"].nodeValue;Copy the code

SetNameItem () is rarely used, taking an attribute node and then adding a new attribute to the element.

RemoveNamedItem () is similar to the removeAttribute () method on an element in that it removes the named attribute, except that removeNamedItem () returns the Attr node representing the deleted attribute.

The most useful scenario for the Attributes attribute is when you need to iterate over all the attributes on an element, often serializing DOM structures into XML or HTML strings.

  1. Create the element

You can create a new element using the document.createElement () method, which takes one parameter, the label name of the element to be created. A new element created using this method also sets its ownerDocument property to Document, at which point you can add attributes and more child elements.

The Text type

The Text node is represented by the Text type and contains literal plain Text, or possibly escaped HTML characters, but no HTML code. The Text contained in the Text node can be accessed through the nodeValue property or the data property. Both properties contain the same value, and changing the value of one is reflected in the other property.

Methods for manipulating text:

  • AppendData (text) — Adds the text text to the end of the node.
  • DeleteData (offset, count) — Deletes count characters from position offset.
  • InsertData (offset, text) — Inserts text at position offset.
  • ReplaceData (offset, count, text) — Replace the text from offset to offset+count with text.
  • SplitText (offset) — The offset position splits the current text node into two text nodes.
  • SubstringData (offset, count) — Extracts text from position offset to offset+count.

You can also get the number of characters contained in the text node through the Length attribute. By default, each element containing text content can have at most one text node.

<div>Hello!! </div> // has content, so there is a text node

When modifying a text node, greater-than, less-than, or quotation marks are escaped.

div.firstChild.nodeValue = "some \<strong>other\</strong> message"; // Output "some \&lt; strong\$gt; other\&lt; /strong&\gt; message"Copy the code
  1. Creating a text node

Document.createtextnode (), which takes one argument: the text of the node to be inserted. When a new text node is created, its ownerDocument property is set to Document. In general, an element contains only one text child node.

  1. Normalized text node

Using normalize () as defined in the Node type, this method is available on all types of nodes. When normalize () is called on a parent node containing two or more adjacent text nodes, all sibling text nodes are merged into a single text node.

  1. Split text nodes

The Text type defines the opposite of normalize (), splitText (), which splits the nodeValue at the specified offset, splitting one Text node into two Text nodes. Split text nodes THE DOM parsing technique most commonly used to extract data from text nodes.

The Comment type

Comments in the DOM are represented by the Comment type.

It inherits from the same base class (CharacterData) as the Text type, so it has all the string manipulation methods of the Text node except splitText ().

Similar to the Textl type, annotation content can be obtained via the nodeValue or data attribute.

Comment nodes can be accessed as children of the parent node.

You can create a comment node using the document.createcomment () method, which takes the comment text.

CDATASection type

Represents CDATA blocks that are unique to XML. This type inherits from the Text type and therefore has all string manipulation methods on the Text node except splitText ().

CDATA blocks are only valid in XML documents. In real XML document, you can use the document. The createCDataSection () and incoming node content to create the CDATA block.

DocumentType type

Nodes of this type contain document type information for the document. The DocumentType object is stored in the document.docType property.

There are three properties of the DocumentType object: name, entities and notations. Name is the name of the DocumentType, and entities is the NamedNodeMap of the entity described by this DocumentType.

And this is a NamedNodeMap of notations for this document type description. Because the document in the browser is usually an HTML or XHTML document type, lists entities and notations are empty. Only the name attribute is useful.

DocumentFragment type

Is the only type that has no corresponding representation in tags and can contain and manipulate nodes without the added expense of full documentation.

Can not directly add document fragments into the document, you can use the document. The createDocumentFragment () to create a document fragments.

Attr type

Element data is represented in the DOM by the Attr type. The Attr type constructors and stereotypes are directly accessible in all browsers.

Attributes are nodes that exist in the attributes attribute of an element, and attribute nodes, although nodes, are not considered part of the DOM document tree.

Attr nodes are rarely referred to directly, but are usually manipulated using getAttribute (), setAttribute (), and removeAttribute ().

The Attr object has three properties: name, value, and specified. Name and value are the same as nodeName and nodeValue respectively. Specified is a Boolean value that indicates whether the property is using the default value or the specified value. You can use the document.createAttribute () method to create a new Attr node with the attribute name.

DOM programming

Dynamic script

The <script> element is used to insert JavaScript code into the web page, either as an external file contained in the SRC attribute or as the source code for the element’s content. A dynamic script is a script that does not exist when the page loads and is later included through the DOM.

There are two ways to dynamically add scripts to a web page via <script> :

  1. Importing external files

<script src = “foo.js”></script>

This node can be created programmatically using DOM to dynamically load external files:

let script = document.createElement("script");
script.src = "foo.js";
document.body.appendChild(script);	
Copy the code

In the last line, until the <script> element is added to the page, you do not start downloading external files. <script> can also be added to <head> elements

  1. Insert source code directly
 <script>
	function sayHi(){
		alert("hi");
	}
</script>
Copy the code

The following logic can be implemented through DOM programming:

let script = document.createElement("script"); script.appendChild(document.createTextNode("function sayHi(){ alert("hi"); } ")); document.body.appendChild(script);Copy the code

The above code runs in Firefox, Safari, Chrome, and Opera.

Dynamic style

CSS styles are loaded in HTML with two elements:

<link> — For containing CSS external files.

<style> — used to add embedded styles.

Like dynamic scripting, dynamic styles don’t exist when the page loads and are added to the page later.

<link rel = “stylesheet” type = “text/css” href = “style.css”>

Create this element using DOM programming:

let link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "style.css";
let head = document.getElementByTagName("head")[0];
head.appendChild(link);
Copy the code

The above code runs in Firefox, Safari, Chrome, and Opera. <style> DOM programming, like <link>

But IE restricts access to <style> nodes to their children, just as it restricts access to <script> elements. Solution: Access the element’s styleSheet property, which has a cssText property, and add CSS code to that property.

style.styleSheet.cssText = “body{background-color : red}”

Be careful with stylesheet.csstext. Reusing the same <style> element and setting the attribute more than once can cause the browser to crash.

Using a NodeList

Understand that NodeList objects and their associated NamedNodeMap and HTMLCollection types are “live”. NodeList is real-time queries based on DOM documents.

Any time you iterate over NodeList, it’s a good idea to initialize a variable that holds the length of the query at that time:

let divs = document.getElementsByTagName("div");
for(let i = 0, len = divs.length; i < len; ++i){
	let div = document.createElement("div");
	document.body.appendChild(div);
}
Copy the code

If you do not want to initialize a variable, you can also iterate through the collection in reverse. It is best to limit the number of NodeList operations because each query searches the entire document, so it is best to cache the NodeList queries.

MutationObserver interface

Callbacks can be executed asynchronously when the DOM is modified. MutationObserver allows you to view an entire document, a portion of the DOM tree, or an element. You can also observe changes to element attributes, child nodes, text, or any combination of the first three.

Basic usage

The instance is created by calling the MutationObserver constructor and passing in a callback function:

let observer = new MutationObserver(() => console.log(‘DOM was mutated! ‘));

  1. observer()

Use this method to associate an Observer instance with the DOM. Take two required parameters: the DOM node to watch change, and a MutationObserverInit object.

The MutationObserverInit object, which controls what changes are observed, is a dictionary in the form of key/value pairs.

let observer = new MutationObserver(() => console.log('<body> attributes changed! ')); observer.observe(document.body, { attributes : true }); document.body.className = 'foo'; console.log('change body class'); // change body class // <body> attributes changed!Copy the code

The instance asynchronously executes the registered callback function when it finds that any attributes on the body element have changed.

  1. The callback and MutationRecord

Each callback receives an array of MutationRecord instances, which contain information about what has changed and which parts of the DOM are affected.

let observer = new MutationObserver ((mutationRecords) => console.log (mutationRecords)) ; observer.observe (document.body, { attributes: true }) ; document.body.setAttribute(' foo', 'bar') ; // [ // { // addedNodes: NodeList [] , // attributeName: "foo", // attributeNamespace: null, // nextsibling: null, // nextSibling : null, // previoussibling: null, // removedNodes: NodeList [] , // target: body // type: "Attributes" //} //Copy the code

Properties of the MutationRecord instance

The second argument passed to the callback function is an instance of MutationObserver to observe the change:

let observer = new MutationObserver ((mutationRecords, mutationObserver) => console.log (mutationRecords, mutationObserver)) ;
observer.observe (document.body, { attributes: true }) ;
document.body.className = 'foo';
// [MutationRecord], MutationObserver
Copy the code
  1. Disconnect () method

Used to abort the callback prematurely. By default, a MutationObserver callback is executed in response to a DOM change event as long as the observed element is not garbage collected. You can call this method if you want to abort callback execution early.

A synchronous call to Disconnect () not only stops callbacks to events that have changed since, but also discards callbacks that have been queued up for asynchronous execution.

let observer = new MutationObserver (() => console.log ('<body> attributes changed! ')); observer.observe (document.body, { attributes: true }) ; document.body.className = 'foo'; observer.disconnect(); document.body.className = 'bar'; // (no log output)Copy the code

To enable the queued callback to execute, use setTimeout () to complete the queued callback before calling Disconnect ().

document.body.className = 'foo'; setTimeout(() => { observer.disconnect(); document.body.className = 'bar'; }, 0); // <body> attributes changed!Copy the code
  1. Reuse MutationObserver

By calling the Observer () method multiple times, you can reuse a MutationObserver object to observe multiple different target nodes.

At this point, the target property of the MutationRecord can identify the target node of the changed event.

let observer = new MutationObserver ((mutationRecords) => console.log (mutationRecords.map((x) => x.target) ;

Observe. observe(childA, {attributes: true}); observer.observe(childB, { attributes : true }); childA.setAttributes('foo', 'bar'); childB.setAttributes('foo', 'bar'); // [<div>,<span>]Copy the code

The disconnect () method is a one-size-fits-all scheme, calling it stops observing all targets.

  1. Reuse MutationObserver

Calling Disconnect () does not end the life of the MutationObserver, and you can reuse the observer and associate it with a new target node.

let observer = new MutationObserver (() => console.log ('<body> attributes changed! ')); observer.observe (document.body, { attributes: true }) ; document.body.setAttributes('foo', 'bar'); SetTimeout (() => {observer.disconnect(); / / the interrupt associated document. The body. The setAttributes (' bar ', 'baz'); }, 0); setTimeout(() => { observer.observe(document.body, { attributes : true }); / / to associated document. The body. The setAttributes (' baz ', 'qux'); // Trigger change event}, 0); // <body> attributes changed! // <body> attributes changed!Copy the code

MutationObserverInit with observation scope

The MutationObserverInit object is used to control the scope of observation of the target node, and the events that can be observed include property changes, text changes, and child node changes.

Property of the MutationObserverInit object

When you call observe (), the attribute, characterData, and childList properties in the MutationObserverInit object,At least one of the entries must be true(Either setting these attributes directly or setting attributes such as attributeOldValue causes their values to be converted to true indirectly). Otherwise, an error is thrown because there are no change events that might trigger a callback.

  1. Observe the properties

The MutationObserver can observe the addition, removal, and modification of node attributes.

To register callbacks for property changes, set the Attributes property to true in the MutationObserverInit object.

observer.observe(document.body, { attributes : true });

If you want to observe one or more attributes, you can use the attributeFilter attribute to set the whitelist, which is an array of attribute names:

observer.observe(document.body, { attributeFilter : ['foo'] }); / / add the white list properties document. The body. The setAttributes (' foo ', 'bar'); / / add was excluded from the properties of the document. The body. The setAttributes (' baz ', 'qux'); // [MutationRecord]Copy the code

If you want to save the original value of the attribute in the change record, you can set the attributeOldValue attribute to true.

  1. Observing character data

MutationObserver can observe the addition, deletion, and modification of characters in Text nodes such as Text, Comment, or ProcessingInstruction nodes.

To register callbacks for characterData, set the characterData property to true in the MutationObserverInit object.

If you want to save the original character data in the change record, you can set the characterDataOldValue property to true.

  1. Observe child nodes

The MutationObserver can observe the addition and removal of target nodes.

To observe child nodes, set the childList property to true in the MutationObserverInit object.

Reordering the child nodes (although it can be done by calling a method) reports two change events, because it technically involves removing and then adding

  1. Observe the subtree

By default, the MutationObserver limits observations to changes in an element and its children.

You can extend the scope of observation to the subtree of this element (all descendant nodes) by setting the subtree property to true in the MutationObserverInit object.

Nodes in the observed subtree can still trigger change events after being removed from the subtree.

Asynchronous callback and record queue

In order not to affect performance when a large number of change events occur, information about each change (determined by the observer instance) is stored in the MutationRecord instance and then added to the record queue. This queue is unique to each MutationObserver instance and is an ordered list of all DOM change events.

Calling the takeRecords () method of a MutationObserver instance emptens the record queue, fetching and returning all MutationRecord instances in it. Applies to a MutationRecord instance in a record queue that you want to disconnect from the observed target, but you want to handle if the record queue is abandoned due to the call disconnect ().

performance

MutationObserver references

The MutationObserver has a weak reference to the observed target node and does not prevent the garbage collector from reclaiming the target node.

However, the target node has a strong reference to the MutationObserver, and if the target node is removed from the DOM and subsequently garbage collected, the associated MutationObserver will also be garbage collected.