Node类型

  • DOMLevel 1描述了名为Node的接口,这个接口时所有DOM节点类型都必须实现的。
  • 每个节点都有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)

1、nodeName与nodeValue

  • nodeName 与nodeValue 保存着有关节点的信息。
  • 这两个属性的值完全取决于节点类型,在使用这两个属性前,最好先检测节点类型。
1
2
3
4
5
6
if (someNode.nodeType == 1){ 
value = someNode.nodeName; // 会显示元素的标签名
}
/*
在这个例子中,先检查了节点是不是元素。如果是,则将其nodeName 的值赋给一个变量。对元素而言,nodeName始终等于元素的标签名,而nodevalue 则始终为null。
*/

2、节点关系

  • 文档中的所有节点都与其他节点有关系,这些关系可以形容为家族关系,相当于把文档树比作家谱。
  • 每个节点都有一个childNodes属性,其中包含一个NodeList的实例。
  • NodeList是一个类数组对象,用于存储可以按位置存取的有序节点。
  • NodeList并不是Array的实例,但可以使用中括号访问它的值,而且它也有length属性。
  • NodeList是一个对DOM结构的查询,DOM结构的变化会自动地在NodeList 中反映出来。
  • 我们通常说NodeList 是实时的活动对象,而不是第一次访问时所获得内容的快照。
1
2
3
let firstChild = someNode.childNodes[0]; 
let secondChild = someNode.childNodes.item(1);
let count = someNode.childNodes.length;
  • 使用Array.prototype.slice()可以把NodeList对象转换为数组。
  • ES6的Array.from()静态方法同样也能实现。
1
2
let arrayOfNodes1 = Array.prototype.slice.call(someNode.childNodes,0);
let arrayOfNodes2 = Array.from(someNode.childNodes);
  • 每个节点都有一个parentNode属性,指向其DOM树中的父元素。

  • childNodes中的所有节点都有同一个父元素,因此它们的parentNode属性都指向同一个节点。

  • childNodes列表中的每个节点都是同一列表中其他节点的同胞节点。

  • 使用previoussibling 和nextsibling可以在这个列表的节点间导航。

  • 这个列表中第一个节点的previoussibling属性是 null,最后一个节点的nextSibling属性也是null。

    1
    2
    3
    4
    5
    if (someNode.nextSibling === null){ 
    alert("Last node in the parent's childNodes list.");
    } else if (someNode.previousSibling === null){
    alert("First node in the parent's childNodes list.");
    }
  • 如果childlNodes中只有一个节点,则它的previoussibling和 nextsibling 属性都是null。

  • 父节点和它的第一个及最后一个子节点也有专门属性:firstchild 和 lastchild 分别指向childNodes 中的第一个和最后一个子节点。

  • someNode.firstchild的值始终等于someNode.childNodes[0],而someNode.lastChild 的值始终等于someNode.childNodes[someNode.childNodes.length-1]。

  • 如果只有一个子节点,则firstchild和 lastchild指向同一个节点。

  • 如果没有子节点,则firstchild和 lastchild都是 null。

3、操纵节点

  • 由于所有的关系指针都是只读的,DOM提供了一些操纵节点的方法。
  • appendChild():用于在childNodes列表末尾添加节点
    • 添加新节点会更新相关的关系指针,包括父节点和之前一个子节点。
    • appendChild()会返回新添加的节点。
1
2
3
let returnedNode = someNode.appendChild(newNode); 
alert(returnedNode == newNode); // true
alert(someNode.lastChild == newNode); // true
  • 如果把文档中已经存在的节点传给appendChild() ,则这个节点会从之前的位置被转移到新位置。
  • 即使DOM树通过各种关系指针维系,一个节点也不会在文档中同时出现在两个或更多个地方。
  • 如果调用appendChild()传入父元素的第一个子节点,则这个节点会成为父元素的最后一个子节点。
1
2
3
4
// 假设 someNode 有多个子节点
let returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); // false
alert(returnedNode == someNode.lastChild); // true
  • 如果想把节点放到childNodes中的特定位置而不是末尾,则可以使用**insertBefore()**方法。
    • 方法接收两个参数:要插入的节点参照节点
    • 调用这个方法后,要插入的节点会变成参照节点的前一个同胞节点,并被返回。
    • 如果参照节点是 null,则insertBefore()与 appendchild()效果相同。
1
2
3
4
5
6
7
8
9
10
// 作为最后一个子节点插入
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()方法接收两个参数:要插入的节点和要替换的节点。
  • 要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。
  • 使用replacechild()插入一个节点后,所有关系指针都会从被替换的节点复制过来。
1
2
3
4
// 替换第一个子节点
let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
// 替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
  • 移除节点可以使用removeChild()方法
    • 这个方法接收一个参数,即要移除的节点。
    • 被移除的节点会被返回,
1
2
3
4
// 删除第一个子节点
let formerFirstChild = someNode.removeChild(someNode.firstChild);
// 删除最后一个子节点
let formerLastChild = someNode.removeChild(someNode.lastChild);
  • 上面介绍的4个方法都用于操纵某个节点的子元素,也就是说使用它们之前必须先取得父节点(使用前面介绍的parentNode属性)。
  • 并非所有节点类型都有子节点,如果在不支持子节点的节点上调用这些方法,则会导致抛出错误。

4、其他方法

  • cloneNode()会返回与调用它的节点一模一样的节点。

    • 方法接收一个布尔值参数,表示是否深复制。即复制节点及其整个子DOM树。
    • 如果传入 false,则只会复制调用该方法的节点。
    • 复制返回的节点属于文档所有,但尚未指定父节点,所以可称为孤儿节点 ( orphan )。
    • 可以通过appendchild(),insertBefore()或replacechild()方法把孤儿节点添加到文档中。
  • normalize()用于处理文档子树中的文本节点。

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