Node 接口是 DOM 节点的一些共同的属性和方法,是 DOM 操作的基础
Node.prototype 提供了许多属性,供我们操作
nodeType
属性是一个整数值,可以用来节点的类型:
document.nodeType // 9
上述代码表示,文档节点(document)的节点值为 9,为了使得节点值更具语义化,Node 对象还定义了几个常量值:
// 修改上述代码
document.nodeType === Node.DOCUMENT_NODE // true
其他 Node
定义的 nodeType
常量值
节点类型 | 节点值 | 对应常量 |
---|---|---|
element(元素节点) | 1 | Node.ELEMENT_NODE |
attr(属性节点) | 2 | Node.ATTRIBUTE_NODE |
text(文本节点) | 3 | Node.TEXT_NODE |
Comment(注释节点) | 8 | Node.COMMENT_NODE |
document(文档节点) | 9 | Node.DOCUMENT_NODE |
DocumentType(文档类型节点) | 10 | Node.DOCUMENT_TYPE_NODE |
DocumentFragment(文档片段节点) | 11 | Node.DOCUMENT_FRAGMENT_NODE |
示例:
HTML
结构<ul>
<li class="lis">A</li>
<li class="lis"><p>B</p></li>
<li class="lis"><!-- Comment -->C</li>
</ul>
script
标签结构const e = document.createAttribute('id')
console.log(e.nodeType) // 2
console.log(document.nodeType) // 9
const lis = document.getElementsByClassName('lis')
Array.from(lis).forEach(item => {
console.log(item.firstChild.nodeType)
})
// 3
// 1
// 8
nodeName
属性表示节点的名称:
document.nodeName // #document
上述代码表示文档节点(document)的节点名称,其他节点的 nodeName
属性值如下:
节点名称 | 属性名 |
---|---|
element(元素节点) | 大写标签名 |
attr(属性节点) | 属性的名称 |
text(文本节点) | #text |
Comment(注释节点) | #comment |
document(文档节点) | #document |
DocumentType(文档类型节点) | 文档类型 |
DocumentFragment(文档片段节点) | #document-fragment |
示例:
HTML
:<div id="comment"><!-- comment --></div>
script
标签:console.log(document.nodeName) // #document
const div = document.createElement('div')
console.log(div.nodeName) // DIV
div.innerText = 'hello world'
console.log(div.firstChild.nodeName) // #text
const attr = document.createAttribute('id')
console.log(attr.nodeName) // id
const comment = document.getElementById('comment')
console.log(comment.firstChild.nodeName) // #comment
const fragment = document.createDocumentFragment()
console.log(fragment.nodeName) // #document-fragment
nodeValue
属性是一个字符串,表示节点本身的文本值,可读写
注:只有**文本节点(text)、注释节点(comment)、属性节点(attr)**的
nodeValue
有文本值,其余节点的nodeValue
均返回null
HTML
:<div id="a">A</div>
<div id="b"><!--B--></div>
script
标签console.log(document.nodeValue) // null
const a = document.getElementById('a')
console.log(a.firstChild.nodeValue) // A
const attr = document.createAttribute('id')
attr.value = 'hello'
console.log(attr.nodeValue) // hello
// 可写操作
attr.nodeValue = 'world'
console.log(attr.nodeValue) // world
const b = docuemnt.getElementById('b')
console.log(b.firstChild.nodeValue) // B
textContent
属性表示当前节点和它的所有后代节点的文本内容
同时该属性是可读可写的,设置该属性值会替换原来的子节点,同时它还会自动对 HTML 标签转义:
<div id="container">
这是 <span>文本</span>内容
</div>
<script>
const container = document.getElementById('container')
// 将 转移为空格 ' '
console.log(container.textContent) // 这 是文本内容
</script>
textContent
属性会自动忽略 HTML 标签,返回所有的文本内容。
添加按钮修改 DOM 结构:
<button id="btn">Handler</button>
<script>
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
container.text = '<p>这是文本内容</p>'
})
</script>
上述代码中的 p
标签不会识别为 html
元素,而是简单的字符串,也就是说,页面渲染的内容是 <p>这是文本内容</p>
baseURI
是一个字符串,表示当前网页的绝对路径,该属性只读:
// 浏览器地址 http://127.0.0.1:5000/属性/05-baseURI.html
// 这里使用 decodeURI 解析网页地址 utf-8 内容
console.log(decodeURI(document.baseURI.toString()))
// http://127.0.0.1:5000/属性/05-baseURI.html
ownerDocument
属性表示当前节点所在的顶层文档对象,即 document
对象
<div id="container"></div>
<script>
const container = document.getElementById('container')
console.log(container.ownerDocument === document) // true
</script>
注:
document
对象本身的的ownerDocument
属性为null
nextSibling
属性表示当前节点后面的第一个同级节点,如果当前节点后面没有同级节点,则返回 null
HTML
<!-- 这两个标签必须紧挨着 -->
<div id="first">1</div><div id="second">2</div>
script
标签const first = document.getElementById('first')
console.log(first.id) // first
console.log(first.nextSibling.id) // second
与 nextSibling
类似,不过 previous
属性返回的是当前节点前面的第一个同级节点
nextSibling
和previousSibling
类似的是,当遇到空格时,会被识别为文本节点,内容也是空格
parentNode
属性返回当前节点的父节点,其中父节点会有三种类型:
示例:
HTML
<div id="container">
<div id="first">AAA</div>
</div>
script
标签// 文档节点的父节点为 null
console.log(document.parentNode) // null
// html 节点的父节点为 document
console.log(document.documentElement.parentNode) // #document
const container = document.getElementById('container')
const first = document.getElementById('first')
console.log(first.parentNode === container) // true
不同于 parentNode
,parentElement
返回的父节点只能是元素节点(element)类型
和上述代码类似,将 parentNode
改为 parentElement
:
<div id="container">
<div id="first">AAA</div>
</div>
<script>
console.log(document.parentElement) // null
// html 是元素根节点,所以没有 父元素节点
console.log(document.documentElement.parentElement) // null
const container = document.getElementById('container')
const first = document.getElementById('first')
console.log(first.parentElement === container) // true
</script>
firstChild
属性表示当前节点的第一个子节点,如果没有子节点,则返回 null
;同理,lastChild
属性表示当前节点最后一个子节点,如果没有,则返回 null
<ul id="container"><li>AAA</li><li>BBB</li><li>CCC</li></ul>
<script>
const container = document.getElementById('container')
console.log(container.firstChild.textContent) // AAA
console.log(container.lastChild.textContent) // BBB
</script>
当
html
标签之间有空格的时候,firstChild
也会认为这是当前节点的一个子节点(文本节点 text),所以上述html
标签写在了一行
childNodes
属性是一个类数组对象,成员包括当前节点的所有子节点,包括:
如果当前节点没有说任何子节点,那么返回一个空的 NodeList
集合,NodeList
为动态集合,这意味着当前节点发生变化就会立刻返回在结果之中
由于空格会被识别为文字节点(text),所以需要考虑空格
<ul id="container">
<li>AAA</li>
<li>BBB</li>
<li>CCC</li>
</ul>
<script>
const container = document.getElementById('container')
const childNodes = Array.from(container.childNodes)
// 长度包括了空格
console.log(childNodes.length) // 7
childNodes.forEach(item => {
console.log(item.textContent)
})
//
// AAA
//
// BBB
//
// CCC
//
</script>
isConnected
为一个布尔值,表示当前节点是否在文档之中:
<div id="container"></div>
<script>
const p = document.createElement('p')
console.log(p.isConnected) // false
const container = document.getElementById('container')
console.log(container.isConnected) // true
</script>
appendChild()
方法接受一个节点对象作为参数,此节点插入到当前节点的最后
这里传入的节点对象不能是页面上已经存在的
<ul id="container">
<li>111</li>
<li>222</li>
</ul>
<script>
const container = document.getElementById('container')
const li = document.createElement('li')
li.innerText = 333
container.appendChild(li)
</script>
上述代码等同于:
<ul id="container">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
如果参数是 DOM 中已经存在的节点,那么
appendChild
会将其从原来的位置移动到新的位置
<div id="wrapper">
<p id="a">AAA</p>
<p>BBB</p>
</div>
<script>
const wrapper = document.getElementById('container')
const a = document.getElementById('a')
wrapper.appendChild(a)
</script>
最终效果是:
<div id="wrapper">
<p>BBB</p>
<p id="a">AAA</p>
</div>
可见节点移动了位置
注:如果
appendChild()
方法的参数是DocumentFragment
节点,那么插入的是其所有子节点,而不是DocumentFragment
节点本身,返回值是一个空的DocumentFragment
(Vue3 中的模板可以写多个根标签也是基于这个原理)
hasChildNodes()
方法返回一个布尔值,表示当前节点是否有子节点(包括所有子节点,哪怕是一个空格)
<div>
<p id="first"></p>
<p id="second">1</p>
</div>
<script>
const first = document.getElementById('first')
const second = document.getElementById('second')
console.log(first.hasChildNodes()) // false
console.log(second.hasChildNodes()) // true
</script>
另外,判断一个节点还有其他方法,例如:
node.firstChild !== null
node.childNodes && node.childNodes.length > 0
cloneNode()
方法用于克隆一个节点,接受的参数是一个布尔值,表示是否同时克隆子节点,返回值是一个克隆出来的新节点
const cloneUL = document.querySelector('ul').cloneNode(true)
注意:
addEventListener
,node.onclick
等)Node.appendChild()
等方法添加到文档中id
属性,如果要插入到文档中,应该修改其中一个元素的 id
。如果原节点有 name
属性,也需要修改insertBefore()
方法用于将某个节点插入父节点内部指定位置::
const insertedNode = parentNode.insertBefore(newNode, referenceNode)
parentNode
表明要插入节点的容器,对插入节点来说是父节点
insertBefore()
方法接受两个参数:
newNode
:表明要插入的节点referenceNode
:作为标杆,即 newNode
插入在 referenceNode
,如果此项为 null
,则插入到 parentNode
最后位置insertedNode
为插入的节点,即 newNode
<ul id="parent">
<li>AAA</li>
<li id="last">CCC</li>
</ul>
<script>
const parent = document.getElementById('parent')
const li = document.createElement('li')
li.innerHTML = 'BBB'
const last = document.getElementById('last')
parent.insertBefore(li, last)
</script>
上面代码等同于:
<ul id="parent">
<li>AAA</li>
<li>BBB</li>
<li id="last">CCC</li>
</ul>
如果插入的节点是 DOM 现有的元素,那么会将该节点移动位置;
如果要插入的节点是
DocumentFragment
类型,那么插入的将是DocumentFragment
的所有子节点,而不是DocumentFragment
本身,同时返回值也是一个空的DocumentFragment
节点
如果要插入在某个节点的后面,可以结合 nextSibling
属性:
parentNode.insertBefore(newNode, referenceNode.nextSibling)
removeChild()
用于移除子节点,方法接收一个节点作为参数,返回值是被移出的节点:
<ul id="container">
<li id="first">AAA</li>
<li>BBB</li>
</ul>
<script>
const container = document.getElementById('container')
const first = document.getElementById('first')
const removeChild = container.removeChild(first)
console.log(removeChild.textContent) // AAA
</script>
以上代码等同于
<ul id="container">
<li>BBB</li>
</ul>
注意,如果
removeChild()
的参数是 clone 的元素节点,那么不会被移出
replaceChild()
用于将一个新的节点,替换成当前节点的某一个子节点:
const replaceNode = parentNode.replaceChild(newNode, oldNode)
和 insertBefore
类似,这里的参数不一一介绍
<ul id="parent">
<li>hello</li>
<li id="last">world</li>
</ul>
<script>
const parent = document.getElementById('parent')
const last = document.getElementById('last')
const li = document.createElement('li')
li.innerText = 'react'
parent.replaceChild(li, last)
</script>
以上代码等同于:
<ul id="parent">
<li>hello</li>
<li>react</li>
</ul>
注意:
replaceChild
是将节点全部替换,包括id
等属性都会被替换成新的
contains()
方法返回一个布尔值,表示某个节点是否包含某个节点,满足以下三个条件:
<ul id="container">
<li id="first">AAA</li>
</ul>
<div id="brother"></div>
<script>
const container = document.getElementById('container')
const first = document.getElementById('first')
const brother = document.getElementById('brother')
console.log(container.contains(container)) // true
console.log(container.contains(first)) // true
console.log(container.contains(brother)) // false
</script>
compareDocumentPosition()
方法返回一个六个比特位的二进制,表示参数节点与当前节点的关系
二进制值 | 十进制值 | 含义 |
---|---|---|
000000 | 0 | 两个节点相同 |
000001 | 1 | 两个节点不在同一个文档(即有一个节点不在当前文档) |
000010 | 2 | 参数节点在当前节点的前面 |
000100 | 4 | 参数节点在当前节点的后面 |
001000 | 8 | 参数节点包含当前节点 |
010000 | 16 | 当前节点包含参数节点 |
100000 | 32 | 浏览器内部使用 |
示例:
<div id="first"></div>
<script>
const first = document.getElementById('first')
console.log(document.body.compareDocumentPosition(first).toString(2)) // 10100
</script>
上述代码打印出来的是 10100
= 10000
+ 00100
,表示:
10000
:first
DOM 节点在 body
节点中00100
:first
DOM 节点在 body
节点的后面isEqualNode()
、isSameNode()
方法均返回一个布尔值,而且听起来名字也很相似
Equal
:表示**"等价"**,isEqualNode()
用于检查两个节点的类型、属性、子节点是否相同Same
:表示**"相等",即 isSameNode()
只有两个节点为同一个节点**才会返回 true
<p id="text">Helo</p>
<div>
<li>hello</li>
<li>hello</li>
</div>
<script>
const text = document.getElementById('text')
const p = document.getElementsByTagName('p')
console.log(text.isSameNode(p[0])) // true
const lis = document.getElementsByTagName('li')
console.log(lis[0].isSameNode(lis[1])) // false
console.log(lis[0].isEqualNode(lis[1])) // true
</script>
getRootNode()
方法返回当前节点所在文档的根节点 document
,与 ownerDocument
属性作用相同
<p id="text">AAA</p>
<script>
const text = document.getElementById('text')
console.log(text.getRootNode() === document) // true
const p = document.createElement('p')
console.log(p.getRootNode() === document) // false
console.log(p.getRootNode()) // <p></p>
</script>
normalize()
方法回去出节点内部空的文本节点,并将毗邻的文本节点合并成一个,也就是说不存在空的文本节点,以及毗邻的文本节点
<ul id="container"> </ul>
<script>
const container = document.getElementById('container')
container.appendChild(document.createTextNode('fff'))
console.log(container.childNodes.length) // 2
container.normalize()
console.log(container.childNodes.length) // 1
</script>