原生DOM的Node对象

大家好,我是一碗周,一个不想被喝(内卷)的前端。如果写的文章有幸可以得到你的青睐,万分有幸~

这是【从头学前端】系列文章的第四十二篇-《Node对象》

概述

Node对象是什么

DOM的标准规范中提供了Node对象,该对象主要提供了了用于解析DOM节点树结构的属性和方法。

DOM树结构主要是依靠节点进行解析,称为DOM节点树结构。Node对象是解析DOM节点树结构的主要入口。

Node对象提供的属性和方法,可以实现遍历节点、插入点和替换节点等操作。

Node对象并不像Document对象一样,提供可以直接使用的对象,而且不允许通过new关键字进行创建对象。

我们之前定位的的页面元素其实就是Node对象,称为节点元素;以及自己创建的节点,也是一个Node对象。

示例代码如下:

<body>
  <button id="btn">按钮</button>
  <script>
    var btn = document.getElementById('btn')
    //定位的的页面元素其实就是Node对象,称为节点元素
    console.log(btn instanceof NOde)//true
    //自己创建的节点也是Node对象
    var button = document.createElement('button')
    console.log(button instanceof Node)//true
    //获取其全部属性and方法
    console.log(Node.prototype)
  </script>
</body>

继承链关系

Node对象是继承于EventTarget对象的,EventTarget是一个用于接收事件的对象。

DOM的标准规范中的Document对象和Element对象都是继承于Node对象的。

测试代码如下

<body>
  <script>
    console.log(Node.prototype instanceof EventTarget)//true

    console.log(Document.prototype instanceof Node)//true
    console.log(Element.prototype instanceof Node)//true
  </script>
</body>

判断节点类型

Node 对象中提供了 nodeName(只读), nodeType(只读), nodeValue 分别可以用于获取指定节点的节点名称、节点类型、和节点的值。

DOM 节点树结构中,我们实际开发最常见的节点有:

节点名称含义对应的nodeType
元素节点表示 HTML 页面中的标签(即 HTML 页面的结构)1
文本节点表示 HTML 页面中的标签所包含的文本内容3
属性节点表示 HTML 页面中的开始标签包含的属性2

示例代码如下所示:

HTML结构如下:

<button class="cls" id="btn">按钮</button>

元素节点

var btnElement = document.getElementById('btn')

console.log(btnElement.nodeName)  // BUTTON
console.log(btnElement.nodeType)  // 1
console.log(btnElement.nodeValue) // null

元素节点的 nodeName 属性值为标签名称(大写)

文本节点

由 DOM 结构树得知文本节点属于元素节点的子节点

我们可以使用 Node.firstChild 属性(只读)来获取其子节点,如果节点是无子节点,则返回 null。

var textNode = btnElement.firstChild

console.log(textNode.nodeName)  // #text
console.log(textNode.nodeType)  // 2
console.log(textNode.nodeValue) // 按钮

文本节点的 nodeName 属性值是固定值,为#text

nodeValue 并不是一个只读属性,也就是说我们不仅可以获取,而且可以做修改,示例代码如下

textNode.nodeValue = '新按钮'

属性节点

属性节点并不是一个子级节点,也不是兄弟节点,我们获取属性节点可以使用 Element.getAttributeNode()方法

作用:返回指定元素的指定属性节点。

语法结构如下:

var attrNode = element.getAttributeNode(attrName);
  • attrName:一个包含属性名称的 字符串
var attrNode = btnElement.getAttributeNode('class')

console.log(attrNode.nodeName)  // class
console.log(attrNode.nodeType)  // 2
console.log(attrNode.nodeValue) // cls

属性节点的nodeName为当前元素的属性名称

属性节点的nodeValue属性值为当前属性名称对应的值,当然可以做修改

遍历节点

这里所谓的遍历节点就是指:根据节点树结构来获取节点。由节点树我们得知,节点树中分三层关系,分别是祖先与后代、父级与子级、兄弟关系,祖先与后代可以用父级与子级代替的。所以我们这里学习的是如何获取父节点、子节点、相邻兄弟节点

获取父节点

通过HTML 页面中指定元素查找其父节点,我们可以使用 Node 对象的parentNode属性实现,其语法结构如下

var parentNode = node.parentNode
  • parentNode:指定节点的父节点,一个元素节点的父节点可能是一个元素Element节点,也可能是一个文档Document节点

示例代码如下:

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>华为</li>
  </ul>
  <script>
    //作为子节点
    var mi = document.getElementById('mi')
    //通过子节点获取父节点
    var parent1 = mi.parentNode
    console.log(parent1)// ul
  </script>
</body>

parentNode属性类似的还存在一个parentElement属性,这个属性返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个 DOM 元素,则返回null

两者操作<html>以下的节点并没有什么区别,但是如果操作<html>标签,就会出现区别,如下代码展示

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>华为</li>
  </ul>
  <script>
    //<html>作为子节点
    var html = document.documentElement
    //通过子节点获取父节点
    console.log(html.parentNode)//文档节点
    console.log(html.parentElement)// null
  </script>
</body>

获取子节点/空白节点问题

通过 HTML 页面中执行元素查找其子节点,我们可以通过以下 Node 对象的属性实现

属性名描述
Node.childNodes获取指定节点的所有子节点
Node.firstChild获取指定节点的第一个子节点
Node.lastChild获取指定节点的最后一个子节点

示例代码如下

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>锤子</li>
  </ul>
  <script>
    var parent = document.getElementById('parent')
    // childNodes属性-获取指定节点的所有子节点
    var children = parent.childNodes
    console.log(children) // NodeList(7) [text, li, text, li#mi, text, li, text]
  </script>
</body>

如果直接获取的话大多数浏览器都是存在问题的,因为我们只有三个文本节点,获取却得到了7个,而多出来的4个结果,而且这四个结果都是文本节点,这些没有内容的文本节点也就成为空白节点。

那这几个文本节点在什么地方呢,咱们在编写 HTML 代码的时候,为了增加可读性,我们增加了换行和空格,而这些空格和换行都属于文本内容。

空白节点问题图解如下

01_空白节点问题.png

我们得知所有的空白节点都是文本节点,而文本节点的nodeType的值为3,而元素节点的nodeType的值为1;我们可以使用循环加判断的方式来解决这个问题。示例代码如下

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>锤子</li>
  </ul>
  <script>
    var parent = document.getElementById('parent');
    // childNodes属性-获取指定节点的所有子节点
    var children = parent.childNodes;
    console.log(children);

    //用于存取真正的字节集
    var arr = []
    for (var i = 0; i < children.length; i++) {
      var child = children[i]
      if (child.nodeType === 1) {
        // Array.push()方法用于往数组追加数据
        arr.push(child)
      }
    }
    console.log(arr)// Array(3)
  </script>
</body>

获取相邻兄弟节点

通过 HTML 页面中指定元素查找相邻兄弟节点,我们可以通过以下 Node 对象的属性实现:

属性名描述
Node.nextSibling(只读)返回当前节点的后一个兄弟节点,没有则返回null.
Node.previousSibling(只读)返回当前节点的前一个兄弟节点,没有则返回null.

示例代码如下

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>锤子</li>
  </ul>
  <script>
    var parent = document.getElementById('parent');
    // childNodes属性-获取指定节点的所有子节点
    var children = parent.childNodes;
    console.log(children);

    //用于存取真正的字节集
    var arr = []
    for (var i = 0; i < children.length; i++) {
      var child = children[i]
      if (child.nodeType === 1) {
        // Array.push()方法用于往数组追加数据
        arr.push(child)
      }
    }
    console.log(arr)// Array(3)
  </script>
</body>

插入节点

Node.appendChild()方法

Node.appendChild()方法将一个节点附加到指定父节点的子节点列表的末尾处。如果将被插入的节点已经存在于当前文档的文档树中,那么appendChild()只会将它从原先的位置移动到新的位置(不需要事先移除要移动的节点)。

语法结构如下

element.appendChild(aChild)
  • aChild:要追加给父节点的节点。

该方法的返回值就是该子节点。

示例代码如下:

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>锤子</li>
  </ul>
  <script>
    var parent = document.getElementById('parent')

    //创建一个子节点
    var newLi = document.createElement('li')
    //创建文本节点
    var textNode = document.createTextNode('华为')
    //将文本节点追加到新的子节点中
    newLi.appendChild(textNode)
    //将新的子节点追加到父级节点中
    parent.appendChild(newLi)
  </script>
</body>

Node.insertBefore()方法

Node.insertBefore()方法在参考节点之前插入一个拥有指定父节点的子节点。如果给定的子节点是对文档中现有节点的引用,insertBefore()会将其从当前位置移动到新位置。如果引用节点为null,则将指定的节点添加到指定父节点的子节点列表的末尾。

语法结构如下

var insertedNode = parentNode.insertBefore(newNode, referenceNode);
  • parentNode:新插入节点的父节点
  • newNode:用于插入的节点
  • referenceNodenewNode将要插在这个节点之前

返回值insertedNode表示被插入节点(newNode)

示例代码如下

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>锤子</li>
  </ul>
  <script>
    var parent = document.getElementById('parent')

    //创建一个子节点
    var newLi = document.createElement('li')
    //创建文本节点
    var textNode = document.createTextNode('华为')
    //将文本节点追加到新的子节点中
    newLi.appendChild(textNode)
    //获取目标节点
    var mi = document.getElementById('mi')
    //将新建的子节点插入到小米之前
    parent.insertBefore(newLi, mi)
    //如果要插入的节点之前的那个节点为 null ,则默认插入到最后
    var nli = document.createElement('li')
    var tNode = document.createTextNode('三星')
    nli.appendChild(tNode)
    parent.insertBefore(nli, null)
  </script>
</body>

删除节点

Node对象提供了Node.removeChild()方法从DOM中删除一个子节点。返回删除的节点。

语法结构如下:

var oldChild = node.removeChild(child);
//OR
element.removeChild(child);
  • child:要移除的那个子节点.
  • nodechild的父节点.

返回值oldChild保存对删除的子节点的引用,所以说oldChildchild是相等的。

值得注意的是,我们删除之后子节点仍然存在于内存中,只是没有添加到当前文档的DOM树中,因此我们还可以把这个节点重新插入到文档中,但前提是用一个变量来存储被删除的返回值,才可以实现。如果不存储这个返回值,则认为被移除的节点已经是无用的,在短时间内将会被内存管理回收。

如果child节点不是node节点的子节点,则该方法会抛出异常。

示例代码如下

<body>
  <ul id="parent">
    <li>苹果</li>
    <li id="mi">小米</li>
    <li>锤子</li>
  </ul>
  <script>
    var parent = document.getElementById('parent')
    var mi = document.getElementById('mi')
    var oldChild = parent.removeChild(mi)
  </script>
</body>

替换节点

Node对象提供了replaceChild()方法可以实现 HTML 页面中的节点替换功能。

语法结构如下

var replaceNode = parentNode.replaceChild(newChild, oldChild);

参数说明

  • parentNode:表示oldChild节点的父级节点。
  • newChild:用来替换oldChild的新节点。如果该节点已经存在于DOM树中,则它首先会被从原始位置删除。
  • oldChild:被替换掉的原始节点。

返回值是被替换的节点,即 replaceNode == oldChild

示例代码如下所示:

<body>
  <ul id="phone">
    <li>华为</li>
    <li id="mi">小米</li>
    <li>中兴</li>
  </ul>
  <script>
    // 1. 获取目标节点的父节点
    var parent = document.getElementById('phone')
    // 2. 获取目标节点
    var mi = document.getElementById('mi')
    // 3. 创建节点
    var newLi = document.createElement('li')
    var nodeText = document.createTextNode('黑鲨')
    newLi.appendChild(nodeText)
    // 4..进行替换
    parent.replaceChild(newLi, mi)
  </script>
</body>

执行结果如下所示:

02_替换节点.png

这里还存在着另外一种情况,就是当前节点已经存在,这里进行替换的话,新节点就会从原来的位置被删除了。

复制节点

Node对象提供了cloneNode()方法实现HTML页面中节点复制的功能。

语法结构如下

var dupNode = node.cloneNode(deep);
  • node:将要被克隆的节点
  • dupNode:克隆生成的副本节点
  • deep (可选):是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false(默认值),则只克隆该节点本身.

示例代码如下

<body>
  <button>按钮</button>
  <script>
    // 1. 获取需要复制的节点
    var btn = document.getElementsByTagName('button')[0]
    // 2. 复制节点操作
    var newbtn = btn.cloneNode(true)  // 如果为 false 则不复制子节点
    // 3. 将子节点添加到页面中
    var body = document.body
    body.appendChild(newbtn)

  </script>
</body>

值得注意的是,复制操作时,如果其节点有id属性,则会一起复制,但是id的属性是唯一的属性,这里就会出现问题的。

写在最后

你如果看到这里,我感到很荣幸,如果你喜欢这篇文章,你可以为这篇文章点上一个小赞;你如果喜欢这个专栏,我会一直更新到百篇以上,可以点一下后面的链接从头学前端 - 一碗周的专栏进入之后给个关注。

最后也可以给我点个关注,万分荣庆。

往期推荐

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一碗周.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值