目录

---------

Node 接口

Node 接口是 DOM 节点的一些共同的属性和方法,是 DOM 操作的基础

属性

Node.prototype 提供了许多属性,供我们操作

nodeType

nodeType 属性是一个整数值,可以用来节点的类型:

Javascript
document.nodeType // 9

上述代码表示,文档节点(document)的节点值为 9,为了使得节点值更具语义化,Node 对象还定义了几个常量值:

Javascript
// 修改上述代码
document.nodeType === Node.DOCUMENT_NODE // true

其他 Node 定义的 nodeType 常量值

节点类型节点值对应常量
element(元素节点)1Node.ELEMENT_NODE
attr(属性节点)2Node.ATTRIBUTE_NODE
text(文本节点)3Node.TEXT_NODE
Comment(注释节点)8Node.COMMENT_NODE
document(文档节点)9Node.DOCUMENT_NODE
DocumentType(文档类型节点)10Node.DOCUMENT_TYPE_NODE
DocumentFragment(文档片段节点)11Node.DOCUMENT_FRAGMENT_NODE

示例:

html
PlainText
<ul>
  <li class="lis">A</li>
  <li class="lis"><p>B</p></li>
  <li class="lis"><!-- Comment -->C</li>
</ul>
Javascript
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

nodeName 属性表示节点的名称:

Javascript
document.nodeName // #document

上述代码表示文档节点(document)的节点名称,其他节点的 nodeName 属性值如下:

节点名称属性名
element(元素节点)大写标签名
attr(属性节点)属性的名称
text(文本节点)#text
Comment(注释节点)#comment
document(文档节点)#document
DocumentType(文档类型节点)文档类型
DocumentFragment(文档片段节点)#document-fragment

示例:

html
PlainText
<div id="comment"><!-- comment --></div>
Javascript
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

nodeValue 属性是一个字符串,表示节点本身的文本值,可读写

注:只有**文本节点(text)、注释节点(comment)、属性节点(attr)**的 nodeValue 有文本值,其余节点的 nodeValue 均返回 null

html
PlainText
<div id="a">A</div>
<div id="b"><!--B--></div>
Javascript
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

textContent 属性表示当前节点和它的所有后代节点的文本内容

同时该属性是可读可写的,设置该属性值会替换原来的子节点,同时它还会自动对 HTML 标签转义

html
PlainText
<div id="container">
  这是&nbsp;<span>文本</span>内容
</div>
<script>
	const container = document.getElementById('container')
  // 将 &nbsp; 转移为空格 ' '
  console.log(container.textContent) // 这 是文本内容
</script>

textContent 属性会自动忽略 HTML 标签,返回所有的文本内容。

添加按钮修改 DOM 结构:

html
PlainText
<button id="btn">Handler</button>
<script>
	const btn = document.getElementById('btn')
  btn.addEventListener('click', () => {
    container.text = '<p>这是文本内容</p>'
  })
</script>

上述代码中的 p 标签不会识别为 html 元素,而是简单的字符串,也就是说,页面渲染的内容是 <p>这是文本内容</p>

baseURI

baseURI 是一个字符串,表示当前网页的绝对路径,该属性只读

Javascript
// 浏览器地址 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

ownerDocument 属性表示当前节点所在的顶层文档对象,即 document 对象

html
PlainText
<div id="container"></div>
<script>
  const container = document.getElementById('container')
  console.log(container.ownerDocument === document) // true
</script>

注:document 对象本身的的 ownerDocument 属性为 null

nextSibling

nextSibling 属性表示当前节点后面的第一个同级节点,如果当前节点后面没有同级节点,则返回 null

html
PlainText
<!-- 这两个标签必须紧挨着 -->
<div id="first">1</div><div id="second">2</div>
Javascript
const first = document.getElementById('first')
console.log(first.id) // first
console.log(first.nextSibling.id) // second

previousSibling

nextSibling 类似,不过 previous 属性返回的是当前节点前面的第一个同级节点

nextSiblingpreviousSibling 类似的是,当遇到空格时,会被识别为文本节点,内容也是空格

parentNode

parentNode 属性返回当前节点的父节点,其中父节点会有三种类型:

  1. 元素节点(element)
  2. 文档节点(document)
  3. 文档片段节点(documentfragment)

示例:

html
PlainText
<div id="container">
  <div id="first">AAA</div>
</div>
Javascript
// 文档节点的父节点为 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

parentElement

不同于 parentNodeparentElement 返回的父节点只能是元素节点(element)类型

和上述代码类似,将 parentNode 改为 parentElement

html
PlainText
<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,lastChild

firstChild 属性表示当前节点的第一个子节点,如果没有子节点,则返回 null;同理,lastChild 属性表示当前节点最后一个子节点,如果没有,则返回 null

html
PlainText
<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

childNodes 属性是一个类数组对象,成员包括当前节点的所有子节点,包括:

如果当前节点没有说任何子节点,那么返回一个空的 NodeList 集合,NodeList动态集合,这意味着当前节点发生变化就会立刻返回在结果之中

由于空格会被识别为文字节点(text),所以需要考虑空格

html
PlainText
<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

isConnected 为一个布尔值,表示当前节点是否在文档之中:

html
PlainText
<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()

appendChild() 方法接受一个节点对象作为参数,此节点插入到当前节点的最后

这里传入的节点对象不能是页面上已经存在的

html
PlainText
<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>

上述代码等同于:

html
PlainText
<ul id="container">
  <li>111</li>
  <li>222</li>
  <li>333</li>
</ul>

如果参数是 DOM 中已经存在的节点,那么 appendChild 会将其从原来的位置移动到新的位置

html
PlainText
<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>

最终效果是:

html
PlainText
<div id="wrapper">
  <p>BBB</p>
  <p id="a">AAA</p>
</div>

可见节点移动了位置

注:如果 appendChild() 方法的参数是 DocumentFragment 节点,那么插入的是其所有子节点,而不是 DocumentFragment 节点本身,返回值是一个空的 DocumentFragment(Vue3 中的模板可以写多个根标签也是基于这个原理)

hasChildNodes()

hasChildNodes() 方法返回一个布尔值,表示当前节点是否有子节点(包括所有子节点,哪怕是一个空格)

html
PlainText
<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>

另外,判断一个节点还有其他方法,例如:

cloneNode()

cloneNode() 方法用于克隆一个节点,接受的参数是一个布尔值,表示是否同时克隆子节点,返回值是一个克隆出来的新节点

Javascript
const cloneUL = document.querySelector('ul').cloneNode(true)

注意

  1. 克隆节点会克隆该节点的所有属性,但不会克隆事件(addEventListenernode.onclick 等)
  2. 返回的节点不在文档(document)中,没有任何父节点,可以使用 Node.appendChild() 等方法添加到文档中
  3. 克隆一个节点后,注意此时的 DOM 有可能出现相同的 id 属性,如果要插入到文档中,应该修改其中一个元素的 id。如果原节点有 name 属性,也需要修改

insertBefore()

insertBefore() 方法用于将某个节点插入父节点内部指定位置::

Javascript
const insertedNode = parentNode.insertBefore(newNode, referenceNode)
  1. newNode:表明要插入的节点
  2. referenceNode:作为标杆,即 newNode 插入在 referenceNode,如果此项为 null,则插入到 parentNode 最后位置
html
PlainText
<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>

上面代码等同于:

html
PlainText
<ul id="parent">
  <li>AAA</li>
  <li>BBB</li>
  <li id="last">CCC</li>
</ul>

如果插入的节点是 DOM 现有的元素,那么会将该节点移动位置;

如果要插入的节点是 DocumentFragment 类型,那么插入的将是 DocumentFragment 的所有子节点,而不是 DocumentFragment 本身,同时返回值也是一个空的 DocumentFragment 节点

如果要插入在某个节点的后面,可以结合 nextSibling 属性:

Javascript
parentNode.insertBefore(newNode, referenceNode.nextSibling)

removeChild()

removeChild() 用于移除子节点,方法接收一个节点作为参数,返回值是被移出的节点:

html
PlainText
<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> 

以上代码等同于

html
PlainText
<ul id="container">
  <li>BBB</li>
</ul>

注意,如果 removeChild() 的参数是 clone 的元素节点,那么不会被移出

replaceChild()

replaceChild() 用于将一个新的节点,替换成当前节点的某一个子节点:

Javascript
const replaceNode = parentNode.replaceChild(newNode, oldNode)

insertBefore 类似,这里的参数不一一介绍

html
PlainText
<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>

以上代码等同于:

html
PlainText
<ul id="parent">
  <li>hello</li>
  <li>react</li>
</ul>

注意:replaceChild 是将节点全部替换,包括 id 等属性都会被替换成新的

contains()

contains() 方法返回一个布尔值,表示某个节点是否包含某个节点,满足以下三个条件:

html
PlainText
<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()

compareDocumentPosition() 方法返回一个六个比特位的二进制,表示参数节点与当前节点的关系

二进制值十进制值含义
0000000两个节点相同
0000011两个节点不在同一个文档(即有一个节点不在当前文档)
0000102参数节点在当前节点的前面
0001004参数节点在当前节点的后面
0010008参数节点包含当前节点
01000016当前节点包含参数节点
10000032浏览器内部使用

示例:

html
PlainText
<div id="first"></div>
<script>
  const first = document.getElementById('first')
  console.log(document.body.compareDocumentPosition(first).toString(2)) // 10100
</script>

上述代码打印出来的是 10100 = 10000 + 00100,表示:

isEqualNode()、isSameNode()

isEqualNode()isSameNode() 方法均返回一个布尔值,而且听起来名字也很相似

html
PlainText
<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()

getRootNode() 方法返回当前节点所在文档的根节点 document,与 ownerDocument 属性作用相同

html
PlainText
<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()

normalize() 方法回去出节点内部空的文本节点,并将毗邻的文本节点合并成一个,也就是说不存在空的文本节点,以及毗邻的文本节点

html
PlainText
<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>