🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 1.1 The Document Object Model (aka the DOM) is a hierarchy/tree of JavaScript node objects When you write an HTML document you encapsulate HTML content inside of other HTML content. By doing this you setup a hierarchy that can be expressed as a [tree](http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#trees). Often this hierarchy or encapsulation system is indicated visually by indenting markup in an HTML document. The browser when loading the HTML document interrupts and[parses this hierarchy to create a tree of node objects](http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/#Parsing_general) that simulates how the markup is encapsulated. ~~~ <!DOCTYPE html> <html lang="en"> <head> <title>HTML</title> </head> <body> <!-- Add your content here--> </body> </html> ~~~ The above HTML code when parsed by a browser creates a document that contains nodes structrured in a tree format (i.e. DOM). Below I reveal the tree struture from the above HTML document using Opera's Dragonfly DOM inspector. ![](https://box.kancloud.cn/2015-11-03_563827be07588.png) On the left you see the HTML document in its tree form. And on the right you see the corresponding JavaScript object that represents the selected element on the left. For example, the selected *`<body>`* element highlighted in blue, is an element node and an instance of the *HTMLBodyElement* interface. What you should take away here is that html documents get parsed by a browser and converted into a tree structure of node objects representing a live document. The purpose of the DOM is to provide a programatic interface for scripting (removing, adding, replacing, eventing, modifiying) this live document using JavaScript. > ### Notes > The DOM originally was an application programming interface for XML documents that has been extended for use in HTML documents. ## 1.2 Node object types The most common (I'm not highlighting all of them in the list below) types of nodes (i.e. *nodeType*/node classifications) one encounters when working with HTML documents are listed below. * DOCUMENT_NODE (e.g. window.document) * ELEMENT_NODE (e.g. `<body>`, `<a>`, `<p>`, `<script>`, `<style>`, `<html>`, `<h1>` etc...) * ATTRIBUTE_NODE (e.g. class="funEdges") * TEXT_NODE (e.g. text characters in an html document including carriage returns and white space) * DOCUMENT_FRAGMENT_NODE (e.g. document.createDocumentFragment()) * DOCUMENT_TYPE_NODE (e.g. `<!DOCTYPE html>`) I've listed the node types above formatted (all uppercase with _ separating words) exactly as the constant property is written in the JavaScript browser environment as a property of the *Node* object. These *Node* properties are constant values and are used to store numeric code values which map to a specific type of node object. For example in the following code example, *Node.ELEMENT_NODE* is equal to *1*. And *1* is the code value used to identify element nodes. live code: [http://jsfiddle.net/domenlightenment/BAVrs](http://jsfiddle.net/domenlightenment/BAVrs) ~~~ <!DOCTYPE html> <html lang="en"> <body> <script> console.log(Node.ELEMENT_NODE) //logs 1, one is the numeric code value for element nodes </script> </body> </html> ~~~ In the code below I log all of the node types and there values. live code: [http://jsfiddle.net/domenlightenment/YcXGD](http://jsfiddle.net/domenlightenment/YcXGD) ~~~ <!DOCTYPE html> <html lang="en"> <body> <script> for(var key in Node){     console.log(key,' = '+Node[key]); }; /* the above code logs to the console the following ELEMENT_NODE = 1 ATTRIBUTE_NODE = 2 TEXT_NODE = 3 CDATA_SECTION_NODE = 4 ENTITY_REFERENCE_NODE = 5 ENTITY_NODE = 6 PROCESSING_INSTRUCTION_NODE = 7 COMMENT_NODE = 8 DOCUMENT_NODE = 9 DOCUMENT_TYPE_NODE = 10 DOCUMENT_FRAGMENT_NODE = 11 NOTATION_NODE = 12 DOCUMENT_POSITION_DISCONNECTED = 1 DOCUMENT_POSITION_PRECEDING = 2 DOCUMENT_POSITION_FOLLOWING = 4 DOCUMENT_POSITION_CONTAINS = 8 DOCUMENT_POSITION_CONTAINED_BY = 16 DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 32 */ </script> </body> </html> ~~~ The previous code example gives an exhaustive list of all node types. For the purpose of this book I'll be discussing the shorter list of node types listed at the start of this section. These nodes will most likely be the ones you come in contact with when scripting an HTML page. In the table below I list the name given to the interface/constructor that instantiates the most common node types and their corresponding *nodeType* classification by number and name. What I hope you take away from the table below is the *nodeType* value (i.e. *1*) is just a numeric classificaiton used to describe a certain type of node constructed from a certain JavaScript interface/constructor. For example, the *HTMLBodyElement* interface reprsents a node object that has a node type of *1*, which is a classification for *ELEMENT_NODE*'s. | Interface/Constructor: | nodeType (returned from *.nodeType*): | | --- | --- | | [HTML*Element](http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/html.html#ID-58190037), (e.g. [HTMLBodyElement](http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/html.html#ID-62018039)) | **1** (i.e. *ELEMENT_NODE*) | | [Text](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-1312295772) | **3** (i.e. *TEXT_NODE*) | | [Attr](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-637646024) | **2** (i.e. *ATTRIBUTE_NODE*) | | [HTMLDocument](http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/html.html#ID-26809268) | **9** (i.e. *DOCUMENT_NODE*) | | [DocumentFragment](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-B63ED1A3) | **11** (i.e. *DOCUMENT_FRAGMENT_NODE*) | | [DocumentType](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-412266927) | **10** (i.e. *DOCUMENT_TYPE_NODE*) | > ### Notes > The DOM specification semantically labels nodes like *Node*, *Element*, *Text*, *Attr*, and *HTMLAnchorElement* as an interface, which it is, but keep in mind its also the name given to the JavaScript constructor function that constructs the nodes. As you read this book I will be referring to these interfaces (i.e. *Element*, *Text*, *Attr*, *HTMLAnchorElement*) as objects or constructor functions while the specification refers to them as interfaces. The *ATTRIBUTE_NODE* is not actually part of a tree but listed for historical reasons. In this book I do not provide a chapter on attribute nodes and instead discuss them in the *Element* node chapter given that attributes nodes are sub-like nodes of element nodes with no particiipation in the actual DOM tree structure. Be aware the ATTRIBUTE_NODE is being depreciated in DOM 4. I've not included detail in this book on the *COMMENT_NODE* but you should be aware that comments in an HTML document are*Comment* nodes and similar in nature to *Text* nodes. As I discuss nodes throughout the book I will rarely refer to a specific node using its *nodeType* name (e.g. *ELEMENT_NODE*). This is done to be consistent with verbiage used in the specifications provided by the W3C & WHATWG. ## 1.3 Sub-node objects inherit from the *Node* object Each node object in a typical DOM tree inherits properties and methods from *Node*. Depending upon the type of node in the document there are also additional sub node object/interfaces that extend the *Node* object. Below I detail the inheritance model implemented by browsers for the most common node interfaces (< indicates inherited from). * *Object <* Node <  *Element* < *HTMLElement* <  (e.g. *HTML*Element) * Object <*Node* <  *Attr* (This is deprecated in DOM 4) * Object <*Node* <  *CharacterData* <  *Text* * Object <*Node* <  *Document* <  *HTMLDocument* * Object <*Node* <  *DocumentFragment* Its important not only to remember that all nodes types inherit from *Node* but that the chain of inheritance can be long. For example, all *HTMLAnchorElement* nodes inherit properties and methods from *HTMLElement*, *Element*,*Node*, and *Object* objects. ### Notes *Node* is just a JavaScript constructor function. And so logically *Node* inherits from *Object.prototype* just like all objects in JavaScript To verify that all node types inherit properties & methods from the *Node* object lets loop over an *Element* node object and examine its properties and methods (including inherited). live code: [http://jsfiddle.net/domenlightenment/6ukxe/](http://jsfiddle.net/domenlightenment/6ukxe) ~~~ <!DOCTYPE html> <html lang="en"> <body> <a href="#">Hi</a> <!-- this is a HTMLAnchorElement which inherits from... --> <script> //get reference to element node object var nodeAnchor = document.querySelector('a'); //create props array to store property keys for element node object var props = []; //loop over element node object getting all properties & methods (inherited too) for(var key in nodeAnchor){     props.push(key);   } //log alphabetical list of properties & methods console.log(props.sort()); </script> </body> </html> ~~~ If you run the above code in a web browser you will see a long list of properties that are available to the element node object. The properties & methods inherited from the *Node* object are in this list as well as a great deal of other inherited properties and methods from the *Element*, *HTMLElement*, *HTMLAnchorElement*, *Node*, and*Object* object. Its not my point to examine all of these properties and methods now but simply to point out that all nodes inherit a set of baseline properties and methods from its constructor as well as properties from the prototype chain. If you are more of visual learner consider the inheritance chain denoted from examining the above HTML document with Opera's DOM inspector. ![](https://box.kancloud.cn/2015-11-03_563827be1ae75.png) Notice that the anchor node inherits from *HTMLAnchorElement*, *HTMLElement*, *Element*, *Node*, and *Object* all shown in the list of properties highlighted with a gray background. This inheritance chain provides a great deal of shared methods and properties to all node types. > ### Notes > You can extend the DOM. But its likley not a[ good idea to extend host objects](http://perfectionkills.com/whats-wrong-with-extending-the-dom/). ## 1.4 Properties and methods for working nodes Like we have been discussing all node objects (e.g *Element*, *Attr*, *Text* etc...) inherit properties and methods from a primary *Node* object. These properties and methods are the baseline values and functions for manipulating, inspecting, and traversing the DOM. In addtion to the properties and methods provided by the node interface there are a great deal of other relevant properties and methods that are provided by sub node interfaces such as the *document*, *HTMLElement*, or *HTML*Element* interface. Below I list out the most common *Node* properties and methods inherited by all node objects including the relevant inherited properties for working with nodes from sub-node interfaces. Node Properties: * *childNodes* * *firstChild* * *lastChild* * *nextSibling* * *nodeName* * *nodeType* * *nodeValue* * *parentNode* * *previousSibling* Node Methods: * *appendChild()* * *cloneNode()* * *compareDocumentPosition()* * *contains()* * *hasChildNodes()* * *insertBefore()* * *isEqualNode()* * *removeChild()* * *replaceChild()* Document Methods: * *document.createElement()* * *document.createTextNode()* HTML * Element Properties: * *innerHTML* * *outerHTML* * *textContent* * *innerText* * *outerText* * *firstElementChild* * *lastElementChild* * *nextElementChild* * *previousElementChild* * *children* HTML element Methods: * *insertAdjacentHTML()* ## 1.5 Identifying the type and name of a node Every node has a *nodeType* and *nodeName* property that is inherited from *Node*. For example *Text* nodes have a*nodeType* code of *3* and *nodeName* value of *'#text'*. As previously mentioned the numeric value *3* is a numeric code representing the type of underlying object the node represents (i.e. *Node.TEXT_NODE === 3*).  Below I detail the values returned for *nodeType* and *nodeName* for the node objects discussed in this book. It makes sense to simply memorize these numeric code's for the more common nodes given that we are only dealing with 5 numeric codes. live code: [http://jsfiddle.net/domenlightenment/8EwNu](http://jsfiddle.net/domenlightenment/8EwNu) ~~~ <!DOCTYPE html> <html lang="en"> <body> <a href="#">Hi</a> <script> //This is DOCUMENT_TYPE_NODE or nodeType 10 because Node.DOCUMENT_TYPE_NODE === 10 console.log( document.doctype.nodeName, //logs 'html' also try document.doctype to get <!DOCTYPE html> document.doctype.nodeType //logs 10 which maps to DOCUMENT_TYPE_NODE ); //This is DOCUMENT_NODE or nodeType 9 because Node.DOCUMENT_NODE === 9 console.log( document.nodeName, //logs '#document' document.nodeType //logs 9 which maps to DOCUMENT_NODE ); //This is DOCUMENT_FRAGMENT_NODE or nodeType 11 because Node.DOCUMENT_FRAGMENT_NODE === 11 console.log( document.createDocumentFragment().nodeName, //logs '#document-fragment' document.createDocumentFragment().nodeType //logs 11 which maps to DOCUMENT_FRAGMENT_NODE ); //This is ELEMENT_NODE or nodeType 1 because Node. ELEMENT_NODE === 1 console.log( document.querySelector('a').nodeName, //logs 'A' document.querySelector('a').nodeType //logs 1 which maps to ELEMENT_NODE ); //This is TEXT_NODE or nodeType 3 because Node.TEXT_NODE === 3 console.log( document.querySelector('a').firstChild.nodeName, //logs '#text' document.querySelector('a').firstChild.nodeType //logs 3 which maps to TEXT_NODE ); </script> </body> </html> ~~~ If its not obvious the fastest way to determine if a node is of a certain type is too simply check its *nodeType*property. Below we check to see if the anchor element has a node number of 1\. If it does than we can conclude that its an *Element* node because *Node.ELEMENT_NODE === 1*. live code: [http://jsfiddle.net/domenlightenment/ydzWL](http://jsfiddle.net/domenlightenment/ydzWL) ~~~ <!DOCTYPE html> <html lang="en"> <body> <a href="#">Hi</a> <script> //is <a> a ELEMENT_NODE? console.log(document.querySelector('a').nodeType === 1); //logs true, <a> is an Element node //or use Node.ELEMENT_NODE which is a property containg the numerice value of 1 console.log(document.querySelector('a').nodeType === Node.ELEMENT_NODE); //logs true, <a> is an Element node </script> </body> </html> ~~~ Determining the type of node that you might be scripting becomes very handy so that you might know which properties and methods are available to script the node. ### Notes The values returned by the *nodeName* property vary according to the node type. Have a look at the [DOM 4 specification](http://www.w3.org/TR/dom/#dom-node-nodename) provided for the details. ## 1.6 Getting a nodes value The *nodeValue* property returns *null* for most of the node types (except *Text*, and *Comment*). It's use is centered around extracting actual text strings from *Text* and *Comment* nodes. In the code below I demonstrate its use on all the nodes discussed in this book live code: [http://jsfiddle.net/domenlightenment/LNyA4](http://jsfiddle.net/domenlightenment/LNyA4) ~~~ <!DOCTYPE html> <html lang="en"> <body> <a href="#">Hi</a> <script> //logs null for DOCUMENT_TYPE_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, ELEMENT_NODE below console.log(document.doctype.nodeValue); console.log(document.nodeValue); console.log(document.createDocumentFragment().nodeValue); console.log(document.querySelector('a').nodeVale); //logs string of text console.log(document.querySelector('a').firstChild.nodeValue); //logs 'Hi' </script> </body> </html> ~~~ > ### Notes > *Text* or *Comment* node values can be set by providing new strings values for the *nodeValue* property(i.e.*document.body.firstElementChild.nodeValue = 'hi'*). ## 1.7 Creating element and text nodes using JavaScript methods When a browser parses an HTML document it constructs the nodes and tree based on the contents of the HTML file. The browser deals with the creation of nodes for the intial loading of the HTML document. However its possible to create your own nodes using JavaScript. The following two methods allow us to programatically create *Element*and *Text* nodes using JavaScript. * *createElement()* * *createTextNode()* Other methods are avaliable but are not commonly used (e.g. *createAttribute()* and *createComment()*) . In the code below I show how simple it is to create element and text nodes. live code: [http://jsfiddle.net/domenlightenment/Vj2Tc](http://jsfiddle.net/domenlightenment/Vj2Tc) ~~~ <!DOCTYPE html> <html lang="en"> <body> <script> var elementNode = document.createElement('div'); console.log(elementNode, elementNode.nodeType); //log <div> 1, and 1 indicates an element node var textNode = document.createTextNode('Hi'); console.log(textNode, textNode.nodeType); //logs Text {} 3, and 3 indicates a text node </script> </body> </html> ~~~ ### Notes The *createElement()* method accepts one parameter which is a string specifying the element to be created. The string is the same string that is returned from the *tagName* property of an *Element* object. The *createAttribute()* method is depricated and should not be used for creating attribute nodes. Instead developers typically use *getAttribute()*, *setAttribute()*, and *removeAttribute()* methods. I will discus this in more detail in the*Element* node chapter. The *createDocumentFragment()* will be discussed in the chapter covering this method. You should be aware that a *createComment()* method is available for creating comment nodes. Its not discussed in this book but is very much available to a developer who finds its usage valuable. ## 1.8 Creating and adding element and text nodes to the DOM using JavaScript strings The *innerHTML*, *outerHTML*, *textContent* and *insertAdjacentHTML()* properties and methods provide the functionality to create and add nodes to the DOM using JavaScript strings. In the code below we are using the *innerHTML*, *outerHTML*, and *textContent* properties to create nodes from JavaScript strings that are then immediately added to the DOM. live code: [http://jsfiddle.net/domenlightenment/UrNT3](http://jsfiddle.net/domenlightenment/UrNT3) ~~~ <!DOCTYPE html> <html lang="en"> <body> <div id="A"></div> <span id="B"></span> <div id="C"></div> <div id="D"></div> <div id="E"></div> <script> //create a strong element and text node and add it to the DOM document.getElementById('A').innerHTML = '<strong>Hi</strong>'; //create a div element and text node to replace <span id="B"></div> (notice span#B is replaced) document.getElementById('B').outerHTML = '<div id="B" class="new">Whats Shaking</div>' //create a text node and update the div#C with the text node document.getElementById('C').textContent = 'dude'; //NON standard extensions below i.e. innerText & outerText //create a text node and update the div#D with the text node document.getElementById('D').innerText = 'Keep it'; //create a text node and replace the div#E with the text node (notice div#E is gone)document.getElementById('E').outerText = 'real!'; console.log(document.body.innerHTML); /* logs <div id="A"><strong>Hi</strong></div> <div id="B" class="new">Whats Shaking</div> <span id="C">dude</span> <div id="D">Keep it</div> real! */ </script> </body> </html> ~~~ The *insertAdjacentHTML()* method which only works on *Element* nodes is a good deal more precise than the previously mentioned methods. Using this method its possible to insert nodes before the beginning tag, after the beginning tag, before the end tag, and after the end tag. Below I construct a sentence using the*insertAdjacentHTML()* method. live code: [http://jsfiddle.net/domenlightenment/tvpA6](http://jsfiddle.net/domenlightenment/tvpA6) ~~~ <!DOCTYPE html> <html lang="en"> <body><i id="elm">how</i> <script> var elm = document.getElementById('elm'); elm.insertAdjacentHTML('beforebegin', '<span>Hey-</span>'); elm.insertAdjacentHTML('afterbegin', '<span>dude-</span>');  elm.insertAdjacentHTML('beforeend', '<span>-are</span>');  elm.insertAdjacentHTML('afterend', '<span>-you?</span>');   console.log(document.body.innerHTML); /* logs <span>Hey-</span><i id="A"><span>dude-</span>how<span>-are</span></i><span>-you?</span> */ </script> </body> </html> ~~~ ### Notes The *innerHTML* property will convert html elements found in the string to actual DOM nodes while the *textContent* can only be used to construct text nodes. If you pass *textContent* a string containing html elements it will simply spit it out as text. *document.write()* can also be used to simultaneously create and add nodes to the DOM. However its typically not used anymore unless its usage is required to accomplish 3rd party scripting tasks. Basically the *write()* method will output the values passed to it into the page during page loading/parsing. You should be aware that using the *write()* method will stall/block the parsing of the html document being loaded. *innerHTML* invokes a heavy & expensive HTML parser where as text node generation is trivial thus use the innerHTML & friends sparingly The *insertAdjacentHTML* options "beforebegin" and "afterend" will only work if the node is in the DOM tree and has a parent element.  Support for *outerHTML* was not available natively in Firefox until version 11\. A [polyfill](https://gist.github.com/1044128) is avaliable. *textContent* gets the content of all elements, including *`<script>`* and *`<style>`* elements, *innerText* does not *innerText* is aware of style and will not return the text of hidden elements, whereas *textContent* will Avaliable to all modern browser except Firefox is *insertAdjacentElement()* and *insertAdjacentText()* ## 1.9 Extracting parts of the DOM tree as JavaScript strings The same exact properties (*innerHTML*, *outerHTML*, *textContent*) that we use to create and add nodes to the DOM can also be used to extract parts of the DOM (or really the entire DOM) as a JavaScript string. In the code example below I use these properties to return a string value containing text and html values from the HTML document. live code: [http://jsfiddle.net/domenlightenment/mMYWc](http://jsfiddle.net/domenlightenment/mMYWc) ~~~ <!DOCTYPE html> <html lang="en"> <body> <div id="A"><i>Hi</i></div> <div id="B">Dude<strong> !</strong></div> <script> console.log(document.getElementById('A').innerHTML); //logs '<i>Hi</i>'console.log(document.getElementById('A').outerHTML); //logs <div id="A">Hi</div> //notice that all text is returned even if its in child element nodes (i.e. <strong> !</strong>) console.log(document.getElementById('B').textContent); //logs 'Dude !' //NON standard extensions below i.e. innerText & outerText console.log(document.getElementById('B').innerText); //logs 'Dude !'console.log(document.getElementById('B').outerText); //logs 'Dude !'​​ </script> </body> </html> ~~~ > ### Notes > The *textContent*, *innerText*, *outerText* property when being read will return all of the text nodes contained within the selected node. So for example (not a good idea in practice), *document.body.textContent* will get all the text nodes contained in the body element not just the first text node. ## 1.10 Adding node objects to the DOM using *appendChild()*&*insertBefore()* The *appendChild()* and *insertBefore()* *Node* methods allow us to insert JavaScript node objects into the DOM tree. The *appendChild()* method will append a node(s) to the end of the child node(s) of the node the method is called on. If there are no child node(s) then the node being appended is appended as the first child.  For example in the code below we are creating a element node (*`<strong>`*) and text node (*Dude*). Then the *`<p>`* is selected from the DOM and our *`<strong>`* element is appended using *appendChild()*. Notice that the *`<strong>`*element is encapsulated inside of the *`<p>`* element and added as the last child node. Next the *`<strong>`* element is selected and the text *'Dude'* is appended to the *`<strong>`* element. live code: [http://jsfiddle.net/domenlightenment/HxjFt](http://jsfiddle.net/domenlightenment/HxjFt) ~~~ <!DOCTYPE html> <html lang="en"> <body> <p>Hi</p> <script> //create a blink element node and text node var elementNode = document.createElement('strong'); var textNode = document.createTextNode(' Dude'); //append these nodes to the DOM document.querySelector('p').appendChild(elementNode); document.querySelector('strong').appendChild(textNode); //log's <p>Hi<strong> Dude</strong></p> console.log(document.body.innerHTML); </script> </body> </html> ~~~ When it becomes necessary to control the location of insertion beyond appending nodes to the end of a child list of nodes we can use *insertBefore()*. In the code below I am inserting the `<li>`  element before the first child node of the `<ul>`  element. live code: [http://jsfiddle.net/domenlightenment/UmkME](http://jsfiddle.net/domenlightenment/UmkME) ~~~ <!DOCTYPE html> <html lang="en"> <body> <ul>     <li>2</li>     <li>3</li> </ul> <script> //create a text node and li element node and append the text to the li var text1 = document.createTextNode('1'); var li = document.createElement('li'); li.appendChild(text1); //select the ul in the document var ul = document.querySelector('ul'); /* add the li element we created above to the DOM, notice I call on <ul> and pass reference to <li>2</li> using ul.firstChild */ ul.insertBefore(li,ul.firstChild); console.log(document.body.innerHTML); /*logs <ul> <li>1</li> <li>2</li> <li>3</li> </ul> */ </script> </body> </html> ~~~ The *insertBefore()* requires two parameters, the node to be inserted and the reference node in the document you would like the node inserted before. ### Notes If you do not pass the *insertBefore()* method a second parameter then it functions just like *appendChild()*. We have [more methods](http://www.w3.org/TR/dom/#mutation-methods) (e.g. *prepend()*, *append()*, *before()*, *after()*) to look forward too in DOM 4. ## 1.11 Removing and replacing nodes using *removeChild()* and*replaceChild()* Removing a node from the DOM is a bit of a multi-step process. First you have to select the node you want to remove. Then you need to gain access to its parent element typically using the *parentNode* property. Its on the parent node that you invoke the *removeChild()* method passing it the reference to the node to be removed. Below I demonstrate its use on an element node and text node. live code: [http://jsfiddle.net/domenlightenment/VDZgP](http://jsfiddle.net/domenlightenment/VDZgP) ~~~ <!DOCTYPE html> <html lang="en"> <body> <div id="A">Hi</div> <div id="B">Dude</div> <script> //remove element node var divA = document.getElementById('A'); divA.parentNode.removeChild(divA); //remove text node var divB = document.getElementById('B').firstChild; divB.parentNode.removeChild(divB); //log the new DOM updates, which should only show the remaining empty div#B console.log(document.body.innerHTML); </script> </body> </html> ~~~ Replacing an element or text node is not unlike removing one. In the code below I use the same html structure used in the previous code example except this time I use *replaceChild()* to update the nodes instead of removing them. live code: [http://jsfiddle.net/domenlightenment/zgE8M](http://jsfiddle.net/domenlightenment/zgE8M) ~~~ <!DOCTYPE html> <html lang="en"> <body> <div id="A">Hi</div> <div id="B">Dude</div> <script> //replace element node var divA = document.getElementById('A'); var newSpan = document.createElement('span'); newSpan.textContent = 'Howdy'; divA.parentNode.replaceChild(newSpan,divA); //replace text node var divB = document.getElementById('B').firstChild; var newText = document.createTextNode('buddy'); divB.parentNode.replaceChild(newText, divB); //log the new DOM updates, console.log(document.body.innerHTML); </script> </body> </html> ~~~ ### Notes Depending upon what you are removing or replacing simply providing the *innerHTML*, *outerHTML*, and *textContent*properties with an empty string might be easier and faster. [Careful memory leaks in brwoser might get you however](http://javascript.crockford.com/memory/leak.html). Both *replaceChild()* and *removeChild()* return the replaced or remove node. Basically its not gone just because you replace it or remove. All this does is takes it out of the current live document. You still have a reference to it in memory. We have [more methods](http://www.w3.org/TR/dom/#mutation-methods) (e.g.*replace()*, *remove()*) to look forward too in DOM 4. ## 1.12 Cloning nodes using *cloneNode()* Using the *cloneNode()* method its possible to duplicate a single node or a node and all its children nodes. In the code below I clone only the *`<ul>`* (i.e. *HTMLUListElement)* which once cloned can be treated like any node reference. live code: [http://jsfiddle.net/domenlightenment/6DHgC](http://jsfiddle.net/domenlightenment/6DHgC) ~~~ <!DOCTYPE html> <html lang="en"> <body> <ul> <li>Hi</li> <li>there</li> </ul> <script> var cloneUL = document.querySelector('ul').cloneNode(); console.log(cloneUL.constructor); //logs HTMLUListElement() console.log(cloneUL.innerHTML); //logs (an empty string) as only the ul was cloned </script> </body> </html> ~~~ To clone a node and all of its children nodes you pass the *cloneNode()* method a parameter of of *true*. Below I use the *cloneNode()* method again but this time we clone all of the child nodes as well. live code: [http://jsfiddle.net/domenlightenment/EyFEC](http://jsfiddle.net/domenlightenment/EyFEC) ~~~ <!DOCTYPE html> <html lang="en"> <body> <ul> <li>Hi</li> <li>there</li> </ul> <script> var cloneUL = document.querySelector('ul').cloneNode(true); console.log(cloneUL.constructor); //logs HTMLUListElement() console.log(cloneUL.innerHTML); //logs <li>Hi</li><li>there</li> </script> </body> </html> ~~~ ### Notes When cloning an *Element* node all attributes and values are also cloned. In fact, only attributes are copied! Everything else you can add (e.g. event handlers) to a DOM node is lost when cloning. You might think that cloning a node and its children using *cloneNode(true)* would return a *NodeList* but it in fact does not. *cloneNode()* may lead to duplicate element IDs in a document ## 1.13 Grokking node collections (i.e. *Nodelist* & *HTMLcollection*) When selecting groups of nodes from a tree (cover in chaper 3) or accessing pre-defined sets of nodes, the nodes are either placed in a [*NodeList*](http://www.w3.org/TR/dom/#nodelist) (e.g. *document.querySelectorAll('*')*) or [*HTMLCollection*](http://www.w3.org/TR/dom/#htmlcollection) (e.g.*document.scripts*). These array like (i.e. not a real *Array*) object collections that have the following characteristics. * A collection can either be live or static. Meaning that the nodes contained in the collection are either literally part of the live document or a snapshot of the live document. * By default nodes are sorted inside of the collection by tree order. Meaning the order matches the liner path from tree trunk to branches. * The collections have a *length* property that reflects the number of elements in the list ## 1.14 Gettting a list/collection of all immediate child nodes Using the *childNodes* property produces an array like list (i.e. [NodeList](https://developer.mozilla.org/En/DOM/NodeList)) of the immediate child nodes. Below I select the *`<ul>`* element which I then use to create a list of all of the immediate child nodes contained inside of the*`<ul>`*. live code: [http://jsfiddle.net/domenlightenment/amDev](http://jsfiddle.net/domenlightenment/amDev) ~~~ <!DOCTYPE html> <html lang="en"> <body> <ul> <li>Hi</li> <li>there</li> </ul> <script> var ulElementChildNodes = document.querySelector('ul').childNodes; console.log(ulElementChildNodes); //logs an array like list of all nodes inside of the ul /*Call forEach as if its a method of NodeLists so we can loop over the NodeList. Done because NodeLists are array like, but do not directly inherit from Array*/ Array.prototype.forEach.call(ulElementChildNodes,function(item){    console.log(item); //logs each item in the array });  </script> </body> </html> ~~~ ### Notes The *NodeList* returned by *childNodes* only contains immediate child nodes Be aware *childNodes* contains not only *Element* nodes but also all other node types (e.g. *Text* and *Comment* nodes) *[].forEach* was implemented in ECMAScript 5th edtion ## 1.15 Convert a *NodeList* or *HTMLCollection* to JavaScript *Array* Node lists and html collections are array like but not a true JavaScript array which inherits array methods. In the code below we programtically confirm this using *isArray()*. live code: [http://jsfiddle.net/domenlightenment/n53Xk](http://jsfiddle.net/domenlightenment/n53Xk) ~~~ <!DOCTYPE html> <html lang="en"> <body> <a href="#"></a> <script> console.log(Array.isArray(document.links)); //returns false, its an HTMLCollection not an Array console.log(Array.isArray(document.querySelectorAll('a'))); //returns false, its an NodeList not an Array </script> </body> </html> ~~~ ### Notes *Array.isArray* was implemented in ECMAScript 5th edtion or ES5 Converting a node list and html collection list to a true JavaScript array can provide a good deal of advantages. For one it gives us the ability to create a snapshot of the list that is not tied to the live DOM considering that*NodeList* and *HTMLCollection* are [live](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#td-live) lists. Secondly, converting a list to a JavaScript array gives access to the methods provided by the *Array* object (e.g. *forEach*, *pop*, *map*, *reduce* etc...). To convert an array like list to a true JavaScript array pass the array-like list to *call()* or *apply()*, in which the*call()* or *apply()* is calling a method that returns an un-altered true JavaScript array. In the code below I use the *.slice()* method, which doesn't really slice anything I am just using it to convert the list to a JavaScript *Array*due to the fact the *slice()* returns an array. live code: [http://jsfiddle.net/domenlightenment/jHgTY](http://jsfiddle.net/domenlightenment/jHgTY) ~~~ <!DOCTYPE html> <html lang="en"> <body> <a href="#"></a> <script> console.log(Array.isArray(Array.prototype.slice.call(document.links))); //returns true console.log(Array.isArray(Array.prototype.slice.call(document.querySelectorAll('a')))); //returns true </script> </body> </html> ~~~ > ### Notes > In ECMAScript 6th edtion we have* Array.from* to look forward to which converts a single argument that is an array-like object or list (eg. *arguments*, *NodeList*, *DOMTokenList* (used by *classList*), *NamedNodeMap* (used by *attributes* property)) into a*new Array()* and returns it ## 1.16 Traversing nodes in the DOM From a node reference (i.e. *document.querySelector('ul')*) its possible to get a different node reference by traversing the DOM using the following properties: * *parentNode* * *firstChild* * *lastChild* * *nextSibling* * *previousSibling* In the code example below we examine the *Node* properties providing DOM traversal functionality. live code: [http://jsfiddle.net/domenlightenment/Hvfhv](http://jsfiddle.net/domenlightenment/Hvfhv) ~~~ <!DOCTYPE html> <html lang="en"> <body><ul><!-- comment --> <li id="A"></li> <li id="B"></li> <!-- comment --> </ul> <script> //cache selection of the ul var ul = document.querySelector('ul'); //What is the parentNode of the ul? console.log(ul.parentNode.nodeName); //logs body //What is the first child of the ul? console.log(ul.firstChild.nodeName); //logs comment //What is the last child of the ul? console.log(ul.lastChild.nodeName); //logs text not comment, because there is a line break //What is the nextSibling of the first li? console.log(ul.querySelector('#A').nextSibling.nodeName); //logs text //What is the previousSibling of the last li? console.log(ul.querySelector('#B').previousSibling.nodeName); //logs text </script> </body> </html> ~~~ If you have been around the DOM much then it should be no surprise that traversing the DOM includes not just traversing element nodes but also text and comment nodes. I believe the last code example makes this clear, and this is not exactly ideal. Using the following properties we can traverse the DOM ignoring text and comment nodes. * *firstElementChild* * *lastElementChild* * *nextElementChild* * *previousElementChild* * *children* ### Notes The *childElementCount* is not mentioned but you should be aware of its avaliablity for calculating the number of child elements a node contains. Examine our code example again using only element traversing methods. live code: [http://jsfiddle.net/domenlightenment/Wh7nf](http://jsfiddle.net/domenlightenment/Wh7nf) ~~~ <!DOCTYPE html> <html lang="en"> <body><ul><!-- comment --> <li id="A"></li> <li id="B"></li> <!-- comment --> </ul> <script> //cache selection of the ul var ul = document.querySelector('ul'); //What is the first child of the ul? console.log(ul.firstElementChild.nodeName); //logs li //What is the last child of the ul? console.log(ul.lastElementChild.nodeName); //logs li //What is the nextSibling of the first li? console.log(ul.querySelector('#A').nextElementSibling.nodeName); //logs li //What is the previousSibling of the last li? console.log(ul.querySelector('#B').previousElementSibling.nodeName); //logs li //What are the element only child nodes of the ul? console.log(ul.children); //HTMLCollection, all child nodes including text nodes </script> </body> </html> ~~~ ## 1.17 Verify a node position in the DOM tree with *contains()* &*compareDocumentPosition()* Its possible to know if a node is contained inside of another node by using the *contains()* *Node* method. In the code below I ask if *`<body>`* is contained inside of *`<html lang="en">`*. live code: [http://jsfiddle.net/domenlightenment/ENU4w](http://jsfiddle.net/domenlightenment/) ~~~ <!DOCTYPE html> <html lang="en"> <body> <script> // is <body> inside <html lang="en"> ? var inside = document.querySelector('html').contains(document.querySelector('body')); console.log(inside); //logs true </script> </body> </html> ~~~ If you need more robust information about the position of a node in the DOM tree in regards to the nodes around it you can use the *compareDocumentPosition()* *Node* method. Basically this method gives you the ability to request information about a selected node relative to the node passed in. The information that you get back is a number that corresponds to the following information. | number code returned from*compareDocumentPosition()*: | number code info: | | --- | --- | | 0 | Elements are identical. | | 1 | DOCUMENT_POSITION_DISCONNECTED Set when selected node and passed in node are not in the same document. | | 2 | DOCUMENT_POSITION_PRECEDING Set when passed in node is preceding selected node. | | 3 | DOCUMENT_POSITION_FOLLOWING Set when passed in node is following selected node. | | 8 | DOCUMENT_POSITION_CONTAINS Set when passed in node is an ancestor of selected node. | | 16, 10 | DOCUMENT_POSITION_CONTAINED_BY (16, 10 in hexadecimal) Set when passed in node is a descendant of selected node. | ### Notes *contains()* will return *true* if the node selected and node passed in are identical. *compareDocumentPosition()* can be rather confusing because its possible for a node to have more than one type of relationship with another node. For example when a node both contains (16) and precedes (4) the returned value from*compareDocumentPosition()* will be 20. ## 1.18 How to determine if two nodes are identical [According to the DOM 3 specification](http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isEqualNode) two nodes are equal if and only if the following conditions are satisfied: * The two nodes are of the same type. * The following string attributes are equal: *nodeName*, *localName*, *namespaceURI*, *prefix*, *nodeValue*. That is: they are both null, or they have the same length and are character for character identical. * The *attributes* *NamedNodeMaps* are equal. That is: they are both *null*, or they have the same length and for each node that exists in one map there is a node that exists in the other map and is equal, although not necessarily at the same index. * The *childNodes* *NodeLists* are equal. That is: they are both *null*, or they have the same length and contain equal nodes at the same index. Note that normalization can affect equality; to avoid this, nodes should be normalized before being compared. Calling the *.isEqualNode()* method on a node in the DOM will ask if that node is equal to the node that you pass it as a parameter. Below I exhibt a case of an two equal nodes and two non-identical nodes. live code: [http://jsfiddle.net/domenlightenment/xw68Q](http://jsfiddle.net/domenlightenment/xw68Q) ~~~ <!DOCTYPE html> <html lang="en"> <body> <input type="text"> <input type="text"> <textarea>foo</textarea> <textarea>bar</textarea> <script> //logs true, because they are exactly idential var input = document.querySelectorAll('input'); console.log(input[0].isEqualNode(input[1])); //logs false, because the child text node is not the same var textarea = document.querySelectorAll('textarea'); console.log(textarea[0].isEqualNode(textarea[1])); </script> </body> </html> ~~~ > ### Notes > If you don't care about two nodes being exactly equal but instead want to know if two node references refer to the same node you can simply check it using the *===* opertor (i.e. *document.body === document.body*). This will tell us if they are identical but no equal.