浏览器Node相关API

DOM1 描述了名为 Node 的接口,这个接口是所有 DOM 节点类型都必须实现的。Node 接口在 JavaScript 中被实现为 Node 类型,在除 IE 之外的所有浏览器中都可以直接访问这个类型。在 JavaScript 中,所有节点类型都继承 Node 类型,因此所有类型都共享相同的基本属性和方法。

1. nodeType 节点类型

每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值常量表示:

常量含义
Node.ELEMENT_NODE1元素节点,例如 <p><div>
Node.ATTRIBUTE_NODE2元素的属性
Node.TEXT_NODE3元素的文本内容或属性
Node.CDATA_SECTION_NODE4文档的 CDATA 部分
Node.ENTITY_REFERENCE_NODE5XML 实体引用节点
Node.ENTITY_NODE6XML <!ENTITY ...> 节点
Node.PROCESSING_INSTRUCTION_NODE7处理指令
Node.COMMENT_NODE8注释节点
Node.DOCUMENT_NODE9Document 节点
Node.DOCUMENT_TYPE_NODE10描述文档类型的 DocumentType 节点。例如 <!DOCTYPE html> 就是用于 HTML5 的。
Node.DOCUMENT_FRAGMENT_NODE11DocumentFragment 节点
Node.NOTATION_NODE12一个 XML <!NOTATION ...> 节点

2. nodeName 与 nodeValue

nodeNamenodeValue 保存着有关节点的信息。这两个属性的值完全取决于节点类型。

3. 节点关系

3.1 parentNode 父节点

每一个节点都有 parentNode 属性,指向其 DOM 树中的父元素。如果一个节点没有父节点,则返回 null

比如获取页面中存在一个节点 cNode,那么获取 cNode 的父节点方式如下所示。

const parentNode = cNode.parentNode;

3.2 childNodes 子节点

每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组对象,用于存储可以按位置存取的有序节点。

虽然 NodeList 不是一个 Array 实例,但是我们可以读取它的 length 属性。当需要获取 NodeList 中某一个节点时,可以通过索引下标的方式读取,也可以使用 item 方法读取。

比如,有一个 uList 列表对象,读取它的第二个元素,如下所示

// 使用索引下标读取
const el = uList[1];
// 使用item方法读取
const el = uList.item(1);

由于 NodeList 是一个类数组,我们并不能直接使用数组的一些方法来操作它,但是可以使用 Array.prototype.slice 的方式将其转换为普通数组。

const nodeArray = Array.prototype.slice.call(node.childNodes, 0);

或者使用 ES6 中的 Array.from

const nodeArray = Array.from(node.childNodes);

3.3 previousSibling 上一个节点

获取同胞节点中的上一个节点。如果节点位于第一个位置,那么它的 previousSiblingnull。如果 childNodes 中只有一个节点,那么这个节点的 previousSibling 也是 null

// 获取someNode同级的上一个节点
const previousEl = someNode.previousSibling;

3.4 nextSibling 下一个节点

获取同胞节点中的下一个节点。如果节点位于最后一个位置,那么它的 nextSiblingnull。如果 childNodes 中只有一个节点,那么这个节点的 nextSibling 也是 null

// 获取someNode同级的下一个节点
const nextEl = someNode.nextSibling;

3.5 firstChild 第一个子节点

获取父节点的第一个子节点。它的值等同于 someNode.childNodes[0],是下标为 0 的节点的一种快速访问方式。

如果只有一个子节点,则 firstChild 等于 lastChild。如果没有子节点,则 firstChildlastChild 都是 null

// 获取第一个子节点
const firstEl = someNode.childNodes.firstChild;
const firstEl2 = someNode.childNodes[0];
console.log(firstEl === firstEl2); // true

3.6 lastChild 最后一个子节点

获取父节点的最后一个子节点。它的值等同于 someNode.childNodes[someNode.childNodes.lenght - 1],是下标为 someNode.childNodes.lenght - 1 的节点的一种快速访问方式。

如果只有一个子节点,则 firstChild 等于 lastChild。如果没有子节点,则 firstChildlastChild 都是 null

// 获取最后一个子节点
const lastEl = someNode.childNodes.lastChild;
const lastEl2 = someNode.childNodes[someNode.childNodes.length - 1];
console.log(lastEl === lastEl2); // true

3.7 hasChildNodes 是否有子节点

判断节点是否有一个或多个子节点。相比查询 childNodeslength 属性,这个方法更加便利。

// 判断节点是否有子节点
const isExistEl = someNode.hasChildNodes();

3.8 ownerDocument 文档指针

ownerDocument 属性是一个指向代表整个文档的文档节点的指针。所有节点都被创建它们(或自己所在)的文档所拥有,因为一个节点不可能同时存在于两个或者多个文档中。这个属性为迅速访问文档节点提供了便利,因为无需在文档结构中逐层上溯了。

4. 操纵节点

因为所有关系指针都是只读的,所以 DOM 又提供了一些操纵节点的方法。

4.1 appendChild 添加和移动节点

用于在 childNodes 列表末尾添加节点。添加新节点会更新相关的关系指针,包括父节点和之前的最后一个子节点。appendChild()方法返回新添加的节点。

由于一个节点不会出现在文档中的两个或两个以上的位置,所以如果把文档中已经存在的节点传给 appendChild(),则这个节点会从之前的位置被转移到新位置。

// 在文档中添加newNode节点
someNode.appendChild(newNode);

// 移动文档中的第一个节点到最后位置
someNode.appendChild(someNode.firstChild);

4.2 insertBefore 在某个节点之前添加新节点

如果想把节点放到 childNodes 中的特定位置而不是末尾,则可以使用 insertBefore()方法。这个方法接收两个参数:要插入的节点和参照节点。它的返回值为要插入的节点。

insertBefore(要插入的节点, 参照节点)

同样的,移动节点也适用于 insertBefore

// 在最后一个节点的前面插入一个新节点
someNode.insertBefore(newNode, someNode.lastChild);
// 将第一个节点移动到最后一个节点之前的位置
someNode.insertBefore(someNode.firstChild, someNode.lastChild);

4.3 replaceChild 替换节点

replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。

replaceChild(要插入的节点, 要替换的节点)

// 替换第一个子节点
const firstChild = someNode.firstChild;
const replacedNode = someNode.replaceChild(newNode, firstChild);
console.log(replacedNode === firstChild); // true

4.4 removeChild 删除节点

要移除节点而不是替换节点,可以使用 removeChild()方法。这个方法接收一个参数,即要移除的节点,被移除的节点会被返回。

// 移除最后一个节点
const lastChild = someNode.lastChild;
const el = someNode.removeChild(lastChild);
console.log(el === lastChild); // true

5. 其它方法

5.1 cloneNode

cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制,即复制节点及其整个子 DOM 树。如果传入 false,则只会复制调用该方法的节点。

比如存在如下 html 结构。

<ul>
  <li>Lorem ipsum dolor sit.</li>
  <li>Animi corporis ipsam suscipit.</li>
  <li>Ipsum maiores mollitia sed.</li>
</ul>

假如 uList 为 ul 的引用,那么两种克隆方式如下。

const cloned1 = uList.cloneNode(false);
console.log(cloned1.length); // cloned1不包含ul的子节点,所以输出为0

const cloned2 = uList.cloneNode(true);
console.log(cloned3.length); // cloned2包含ul的所有子节点,所以输出为3

5.2 normalize

在节点上调用 normalize()方法会检测这个节点的所有后代,从中搜索上述两种情形。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。

参考资料:Javascript 高级程序设计(第四版)

如果您觉得本文对您有用,欢迎捐赠或留言~
微信支付
支付宝

发表评论

您的电子邮箱地址不会被公开。