JavaScript中的DOM之Node类型 – MasterH杂货铺

JavaScript中的DOM之Node类型

节点层级

任何 HTML 或 XML 文档都可以用 DOM 表示为一个由节点构成的层级结构。节点分很多类型,每 种类型对应着文档中不同的信息和(或)标记,也都有自己不同的特性、数据和方法,而且与其他类型 有某种关系。这些关系构成了层级,让标记可以表示为一个以特定节点为根的树形结构。以下面的 HTML 为例:

<html>
<head>
    <title>Sample Page</title>
</head>
<body>
    <p>Hello World!</p>
</body>
</html>

其中,document 节点表示每个文档的根节点。在这里,根节点的唯一子节点是<html>元素,我们称之为文档元素(documentElement)。文档元素是文档最外层的元素,所有其他元素都存在于这个元素之 内。每个文档只能有一个文档元素。在 HTML 页面中,文档元素始终是<html>元素。在 XML 文档中, 则没有这样预定义的元素,任何元素都可能成为文档元素。

Node类型

DOM Level 1 描述了名为 Node 的接口,这个接口是所有 DOM 节点类型都必须实现的。Node接口在 JavaScript 中被实现为 Node 类型,在除 IE 之外的所有浏览器中都可以直接访问这个类型。在 JavaScript 中,所有节点类型都继承 Node 类型,因此所有类型都共享相同的基本属性和方法。
每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值 常量表示:

  • Node.ELEMENT_NODE(1)
  • Node.ATTRIBUTE_NODE(2)
  • Node.TEXT_NODE(3)
  • Node.CDATA_SECTION_NODE(4)
  • Node.ENTITY_REFERENCE_NODE(5)
  • Node.ENTITY_NODE(6)
  • Node.PROCESSING_INSTRUCTION_NODE(7)
  • Node.COMMENT_NODE(8)
  • Node.DOCUMENT_NODE(9)
  • Node.DOCUMENT_TYPE_NODE(10)
  • Node.DOCUMENT_FRAGMENT_NODE(11)
  • Node.NOTATION_NODE(12)

nodeName 与 nodeValue

nodeName 与 nodeValue 保存着有关节点的信息。使用这两个属性之前,最好进行比较

if (someNode.nodeType == 1) {
    value = someNode.nodeName;
}

对元素而言,nodeName 始终等于元素的标签名,而 nodeValue 则始终为 null。

节点关系

文档中的所有节点都与其他节点有关系。这些关系可以形容为家族关系,相当于把文档树比作家谱。
每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组 对象,用于存储可以按位置存取的有序节点。注意,NodeList 并不是 Array 的实例,但可以使用中括 号访问它的值,而且它也有 length 属性。NodeList 对象独特的地方在于,它其实是一个对 DOM 结 构的查询,因此 DOM 结构的变化会自动地在 NodeList 中反映出来。我们通常说 NodeList 是实时的 活动对象,而不是第一次访问时所获得内容的快照。
访问NodeList中的元素:

let firstChild = someNode.childNodes[0]; // 通过中括号访问
let secondChild = someNode.childNodes.item(1); // 通过item()方法访问
let count = someNode.childNodes.length; // 子节点个数

将其转换为数组:

let arrayOfNodes = Array.prototype.slice.call(someNode.childNodes, 0); // 使用Array.prototype.slice()方法
let arrayOfNodes = Array.from(someNode.childNodes); // 使用ES6 Array.from方法,推荐

每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素。此外,childNodes 列表中的每个节点都是同一列表中其他节点的同胞节点。而使用 previousSibling 和 nextSibling 可以在这个列表的节点间导航。注意,如果childNodes中只有一个节点,则它的previousSibling和nextSibling属性都是 null。父节点和它的第一个及最后一个子节点也有专门属性:firstChild 和 lastChild 分别指向 childNodes 中的第一个和最后一个子节点。
file
ownerDocument 属性是一个指向代表整个文档的文档节点的指针。

操纵节点

因为所有关系指针都是只读的,所以 DOM 又提供了一些操纵节点的方法。最常用的方法是 appendChild(),用于在 childNodes 列表末尾添加节点。添加新节点会更新相关的关系指针,包括 父节点和之前的最后一个子节点。appendChild()方法返回新添加的节点,如下所示:

let returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode);        // true
alert(someNode.lastChild == newNode);  // true

如果把文档中已经存在的节点传给 appendChild(),则这个节点会从之前的位置被转移到新位置。 5 即使 DOM 树通过各种关系指针维系,一个节点也不会在文档中同时出现在两个或更多个地方。因此, 如果调用 appendChild()传入父元素的第一个子节点,则这个节点会成为父元素的最后一个子节点, 如下所示:

// 假设 someNode 有多个子节点
let returnedNode = someNode.appendChild(someNode.firstChild); alert(returnedNode == someNode.firstChild); // false
alert(returnedNode == someNode.lastChild); // true

如果想把节点放到 childNodes 中的特定位置而不是末尾,则可以使用 insertBefore()方法。 这个方法接收两个参数:要插入的节点和参照节点。调用这个方法后,要插入的节点会变成参照节点的 前一个同胞节点,并被返回。如果参照节点是 null,则 insertBefore()与 appendChild()效果相 同,如下面的例子所示:

// 作为最后一个子节点插入
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); // true

// 作为新的第一个子节点插入
returnedNode = someNode.insertBefore(newNode, someNode.firstChild); 
alert(returnedNode == newNode); // true
alert(newNode == someNode.firstChild); // true

// 插入最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild); 
alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true

appendChild()和 insertBefore()在插入节点时不会删除任何已有节点。相对地, replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档 树中完全移除,要插入的节点会取而代之。

// 替换第一个子节点
let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);

// 替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);

要移除节点而不是替换节点,可以使用 removeChild()方法。

// 删除第一个子节点
let formerFirstChild = someNode.removeChild(someNode.firstChild);

// 删除最后一个子节点
let formerLastChild = someNode.removeChild(someNode.lastChild);

其他方法

cloneNode():返回与调用它的节点一模一样的节 点。cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制, 即复制节点及其整个子 DOM 树。如果传入 false,则只会复制调用该方法的节点。复制返回的节点属 于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。
normalize():处理文档子树中的文本节点。在节点上调用 normalize()方法会检测这个节点的所有后代,从中搜索上述两种 情形。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。

Related Posts

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注