DOM 可以将任何 HTML 或 XML 文档描绘成一个由多层节点构成的结构。节点分为几种不同的类
型,每种类型分别表示文档中不同的信息及(或)标记。每个节点都拥有各自的特点、数据和方法,另
外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点
为根节点的树形结构。
DOM树中包含的节点类型
一共有12种Node类型:
- 元素节点 Node.ELEMENT_NODE(1)
- 属性节点 Node.ATTRIBUTE_NODE(2)
- 文本节点 Node.TEXT_NODE(3)
- CDATA节点 Node.CDATA_SECTION_NODE(4)
- 实体引用名称节点 Node.ENTRY_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)
- DTD声明节点 Node.NOTATION_NODE(12)
本文只讨论元素节点的遍历。
在遍历的时候的过滤方式为:
1 | if(node.nodeType === 1){ |
先序遍历DOM节点
以下面DOM结构为例:
1 |
|
使用DOM1中的基础接口递归遍历
DOM1中为基础类型Node提供了一些api,通过这些api可以完成一些基础的DOM操作。使用递归遍历DOM树的代码比较简单,核心思想就是先处理当前节点,然后再从左到右递归遍历子节点:
1 | function traversal(node){ |
使用DOM扩展的Element Traversal API,递归遍历DOM树
新增Element Traversal API:
- firstElementChild:第一个孩子元素
- lastElementChild:最后一个孩子元素
- previousElementSibling:上一个相邻的元素
- nextElementSibing:下一个相邻的元素
- childElementCount:元素子节点的数目
1 | function traversalUsingTraversalAPI(node){ |
使用NodeIterator
NodeIterator 类型是两者中比较简单的一个,可以使用 document.createNodeIterator()方
法创建它的新实例。这个方法接受下列 4 个参数。
- root:想要作为搜索起点的树中的节点。
- whatToShow:表示要访问哪些节点的数字代码。
- filter:是一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
- entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。
NodeIterator 类型的两个主要方法是 nextNode()和 previousNode()。顾名思义,在深度优先的 DOM 子树遍历中, nextNode()方法用于向前前进一步,而 previousNode()用于向后后退一步。
1 | function traversalUsingNodeIterator(node){ |
使用TreeWalker
TreeWalker 是 NodeIterator 的一个更高级的版本。除了包括 nextNode()和 previousNode()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法。
- parentNode():遍历到当前节点的父节点;
- firstChild():遍历到当前节点的第一个子节点;
- lastChild():遍历到当前节点的最后一个子节点;
- nextSibling():遍历到当前节点的下一个同辈节点;
- previousSibling():遍历到当前节点的上一个同辈节点。
创建 TreeWalker 对象要使用 document.createTreeWalker()方法,这个方法接受的 4 个参数与document.createNodeIterator()方法相同:作为遍历起点的根节点、要显示的节点类型、过滤器和一个表示是否扩展实体引用的布尔值。由于这两个创建方法很相似,所以很容易用 TreeWalker来代替 NodeIterator。
1 | function traversalUsingTreeWalker(node){ |