The project I developed in my work some time ago did not use the three frameworks, nor did I introduce jQuery. It was all native JS, which involved some DOM operations. Therefore, I reviewed the API of DOM operations in my spare time these two days.
DOM operations are mainly divided into four parts: add, delete, modify and check. Of course, the division may not be so exact. At the end of the second link inside the elder brother for the type of division is quite good, interested can click to see.
Find operations
getElementById
This method returns an element that matches a specific (case-sensitive) ID, and because an ID is generally unique within a page. So it’s an efficient way to look up specific elements. But if there are multiple ids in the same page, only the first id is returned and the rest is ignored.
<div id="div1">div1 - 1</div>
<div id="div1">div1 - 2</div>
Copy the code
console.log(document.getElementById('div1').textContent) // div1 - 1
Copy the code
getElementsByTagName
HTMLCollection this method returns a dynamic HTML collection containing all the elements of the specified tag name. By dynamic or real-time, I mean that it automatically updates itself as the DOM tree changes. This method can be looked up in any element, that is, it can be given a definite scope to look up, not just the Document object. For example, in the following code, we are looking for the P tag in the div element with id div1. Of course, tag names can be passed in * to represent all elements.
<div id="div1">
<p class="p1">1</p>
<p class="p1">2</p>
</div>
Copy the code
console.log(document.getElementById('div1').getElementsByTagName('p')) // HTMLCollection(2) [p.p1, p.p1]
Copy the code
getElementsByClassName
This method returns a live HTMLCollection containing all the children of the specified class. This method can be looked up in any element, that is, it can be given a definite scope to look up, not just the Document object.
<div id="div0">
<div class="div2">0-2</div>
</div>
<div id="div1">
<div class="div2">1 to 2</div>
</div>
Copy the code
let ele = document.getElementById('div1').getElementsByClassName('div2') // we limit the scope of the query for elements with class div2 to elements with id div1
console.log(ele.length) / / 1
console.log(ele[0].textContent) / / 1-2
Copy the code
This method is not supported in IE9, but can be encapsulated using existing methods. The following is a code for both old and new browsers from the book DOM Programming Art.
function getElementsByClassName(node, className){
if(node.getElementsByClassName){
return node.getElementsByClassName(className)
}else{
var results = new Array(a)var elems = node.getElementsByTagName(The '*')
for(var i = 0; i < elems.length; i++){
if(elems[i].className.indexOf(className) ! =- 1){
results[results.length] = elems[i]
}
}
return results
}
}
Copy the code
querySelector
This method returns Element, the first HTML Element in the document that matches the specified selector or selector group, or null if it cannot be found. That is, it takes a DOM string in the form of a CSS selector.
<div id="div1">
<p class="p1">1</p>
<p class="p1">2</p>
</div>
Copy the code
let div1 = document.querySelector('#div1')
let p1 = document.querySelector('#div1>.p1')
Copy the code
The first line above is to get the element with id div1, where we pass in the parameter #div1, one more # than getElementById. The second line of code gets the element with class P1 in the element with id div1, which is a typical CSS selector. So how does it differ from getElementById, getElementsByClassName? As mentioned earlier, getElementsByClassName returns a live HTMLCollection, that is, it returns a dynamic collection, whereas querySelector returns a static collection.
querySelectorAll
This method returns a NodeList, a list of elements in the document that match the specified selector group. It takes the same argument form as querySelector, except that it returns all of the matched elements. It also returns a static collection, whereas getElementsByClassName returns a dynamic collection. Let’s look at the differences in code.
<ul id="lists">
<li class="item">1</li>
<li class="item">2</li>
</ul>
Copy the code
let lists = document.getElementById('lists')
let items = lists.getElementsByClassName('item')
console.log(items) // HTMLCollection(2) [li.item, li.item]
console.log(items.length) / / 2
for(let i = 3; i < 5; i++){
let li = document.createElement('li')
li.classList.add('item')
li.innerText = i
lists.appendChild(li)
}
console.log(items.length) / / 4
let lists2 = document.querySelector('#lists2')
let items2 = lists2.querySelectorAll('.item')
console.log(items2) // NodeList(2) [li.item, li.item]
console.log(items2.length) / / 2
for(let i = 3; i < 5; i++){
let li = document.createElement('li')
li.classList.add('item')
li.innerText = i
lists.appendChild(li)
}
console.log(items2.length) / / 2
Copy the code
The first large section of code is added again after obtaining the number of Li’s whose class is item. The length will change dynamically, while the number of Li’s in the second large section of code remains unchanged.
parentNode
ParentNode returns the parentNode of the specified node
<div id="parent">
<div id="children"></div>
</div>
Copy the code
let parent = document.querySelector('#parent')
let children = document.querySelector('#children')
console.log(parent === children.parentNode) // true
Copy the code
childNodes
Returns an up-to-date collection of children of the specified node, of type ModeList. Let’s look at the code in parentNode
<div id="parent">
<div id="children"></div>
</div>
Copy the code
let parent = document.querySelector('#parent')
console.log(parent.childNodes) // NodeList(3) [text, div#children, text]
Copy the code
You can see that there are several childNodes in the result returned because childNodes returns text nodes.
PreviousSibling and nextSibling
These methods return the previous and next sibling of the specified node, or null if none exists.
<ul>
<li class="item1">1</li><li class="item2">2</li><li class="item3">3</li>
</ul>
Copy the code
let items1 = document.querySelector('.item1')
let items2 = document.querySelector('.item2')
let items3 = document.querySelector('.item3')
console.log(items2.previousSibling === items1) // true
console.log(items2.nextSibling === items3) // true
Copy the code
Note that there is no gap between the three LI tags in the code above, otherwise you might get a blank text node when using previousSibling or nextSibling.
Create and add operations
createELement
CreateELement creates an element node by passing in a specified tag name, even if it is not a standard tag name.
let ele = document.createElement('div')
let ele2 = document.createElement('div2') // a div2 tag is created
Copy the code
createTextNode
CreateELement creates a text node by passing in a text character
let text = document.createTextNode('created')
Copy the code
However, both types of nodes are created as independent nodes, that is, they are not yet part of an HTML document. We can add it to the HTML document using methods like appendChild or insertBefore.
let ele = document.createElement('div')
let text = document.createTextNode('created')
ele.appendChild(text)
document.body.appendChild(ele)
Copy the code
cloneNode
CloneNode This method returns a copy of the node that called the method, that is, a copy of ‘who’ is returned for whoever called the cloneNode. It takes a Boolean parameter indicating whether to copy the child element of ‘who’. A value of true means that the child node is copied, and a value of false means that the current ‘who’ is copied, regardless of the child node. Note that the copied node is a separate node that does not exist in the HTML document tree. Likewise, we can add it to the document tree using appendChild.
<div id="app">
<p class="p1">1</p>
</div>
Copy the code
let ele = document.querySelector('#app')
let p1 = document.querySelector('.p1')
let newNode1 = p1.cloneNode(true)
let newNode2 = p1.cloneNode(false)
p1.onclick = function(){
console.log('click event')
}
p1.addEventListener('click'.function(){
console.log('addEventListener click')
})
app.appendChild(newNode1)
app.appendChild(newNode2)
Copy the code
In the code above, we generate two nodes, newNode1 and newNode2. We can look at the page structure in the console and see that the node generated by newNode2 has no children, whereas newNode1 does (text nodes are its children). It’s a good idea to fill in the Boolean argument here so that different browsers don’t behave differently. And in the code above, we not only add a click event to the P1 node, but also register a listener for it. The onClick event can only be bound to one object at a time. An addEventListener can register multiple listeners for an event and bind multiple listeners. By clicking on the generated node, we can see that either the onclick or addEventListener binding event is not copied from the replica node. Is there any way to replicate the behavior of events as well? Yes, if we use HTML tag inline binding, then the replica node can also trigger events.
<div id="app">
<p class="p1" onclick="log()">1</p>
</div>
Copy the code
let ele = document.querySelector('#app')
let p1 = document.querySelector('.p1')
let newNode1 = p1.cloneNode(true)
function log(){
console.log('bind html')
}
app.appendChild(newNode1)
Copy the code
createDocumentFragment
CreateDocumentFragment can be used to create a document fragment that stores temporary nodes for later uniform addition to the DOM document tree. This approach is often used to optimize performance when adding a large number of nodes. Let’s start by looking at the following code, which looping renders 10 elements and adds them to the document.
let app = document.querySelector('#app')
for(let i = 0; i < 10; i++){
let ele = document.createElement('div')
ele.innerText = `new node ${i}`
app.appendChild(ele)
}
Copy the code
The code above does what we need, but there are performance issues. Because the browser kernel can be divided into two parts, rendering engine and JS engine. Rendering engine is responsible for rendering pages,JS engine is responsible for the interpretation and execution of JavaScript scripts. And since JS is single threaded, it needs to constantly switch back and forth between the two engines. This process takes a long time, which is why manipulating the DOM is a performance drain. Now each time we loop, we create a new element that causes the browser to backflow when it is stuffed into the DOM document tree. Here’s a quick look at backflow and redraw of the browser. I’m not going to go into the definition of that. Let me give you a simple example of when it’s going to backflow and when it’s going to redraw. When we change the background color of an element, it causes a redraw because the process does not actually change layout information such as the element’s position. When we change the height of an element, the browser needs to recalculate the position of each element, which causes backflow. The inclusion relationship between them is that redrawing does not necessarily cause redrawing, but redrawing does cause redrawing. Now that we know what is causing the performance problem, we optimize it with createDocumentFragment.
let app = document.querySelector('#app')
let tmp = document.createDocumentFragment()
for(let i = 0; i < 10; i++){
let ele = document.createElement('div')
ele.innerText = `new node ${i}`
tmp.appendChild(ele)
}
app.appendChild(tmp)
Copy the code
The object we generate with the createDocumentFragment method is not part of the document tree; it is kept in memory. Therefore, there is no frequent browser backflow when the loop is generated. When the loop ends, all the nodes are added to the DOM once more.
appendChild
The appendChild method actually occurs several times in the previous examples. There are several ways to create nodes, but they just create the node and don’t really stuff it into the DOM tree; the appendChild method does that for us Method appends a node to the last original child of the specified parent node. If the inserted node already exists in the document tree of the document, it is automatically moved from its original location to the new location. So we can say that this is both a method of increase and a method of change.
<div id="app">
<p class="p1">1</p>
<p class="p2">2</p>
<p class="p3">3</p>
</div>
Copy the code
let app = document.querySelector('#app')
let p2 = document.querySelector('.p2')
app.appendChild(p2)
Copy the code
insertBefore
AppendChild inserts the node to the end, and insertBefore inserts the node exactly where we want it to be. parentNode.insertBefore(newNode, referenceNode) . Where the referenceNode is a designated node, indicating that we want to insert a new node before it. If the new node already exists in the document tree, it is automatically moved from its original location to the new location, similar to appendChild.
<div id="app">
<p class="p1">1</p>
<p class="p2">2</p>
<p class="p3">3</p>
</div>
Copy the code
let app = document.querySelector('#app')
let p2 = document.querySelector('.p2')
let el = document.createElement('p')
el.classList.add('p1-2')
el.innerText = '2'
app.insertBefore(el, p2)
Copy the code
Delete operation
removeChild
RemoveChild removes a child node of the specified element or returns null if the child node does not exist
<ul>
<li class="list">1</li>
<li class="list">2</li>
<li class="list">3</li>
</ul>
Copy the code
let lists = document.querySelectorAll('.list')
let list2 = document.querySelectorAll('.list') [1]
let deleteLi = lists[0].parentNode.removeChild(list2)
console.log(deleteLi) // <li class="list">2</li>
lists[0].parentNode.appendChild(deleteLi)
Copy the code
The result page is 1, 3, 2, and combined with the console.log(deleteLi) result above, we can conclude that the deleted node is not in the document tree, but it is still in memory and we can still manipulate it. Uncaught DOMException: Failed to execute ‘removeChild’ on ‘Node’: The node to be removed is not a child of this node. Modify the following to ensure no error:
if(node.parentNode){
node.parentNode.removeChild(node)
}
Copy the code
remove
When removeChild is used to remove an element node, you need to find the parent node of the element and then the child element below the parent node. Here we have a method that can remove its own node directly.
<ul>
<li class="li1">1</li>
<li class="li2">2</li>
</ul>
Copy the code
let li1 = document.querySelector('.li1')
li1.remove() // 1
Copy the code
This method is not very compatible and cannot be used in Internet Explorer, but Polyfill can be used to make Internet Explorer 9 and above compatible. However, Polyfill also uses removeChild to simulate the function of Remove.
Modify the operating
replaceChild
The replaceChild method replaces a child node of the current node with a specified node and returns the replaced node. Parentnode. replaceChild(newChild, oldChild)
<div id="app">
<p class="p1">1</p>
<p class="p2">2</p>
<p class="p3">3</p>
</div>
Copy the code
let app = document.querySelector('#app')
let p2 = document.querySelector('.p2')
let el = document.createElement('p')
el.classList.add('p4')
el.innerText = '4'
let r = app.replaceChild(el, p2)
console.log(r) // <p class="p2">2</p>
Copy the code
Conclusion:
Reference links:
- www.liaoxuefeng.com/wiki/102291…
- Luopq.com/2015/11/30/…
- Developer.mozilla.org/en-US/docs/…