# DOM 规范

# DOM2 和 DOM3

DOM1( DOM Level 1)主要定义了 HTML 和 XML 文档的底层结构。

DOM2( DOM Level 2)和 DOM3( DOM Level 3)在这些结构之上加入更多交互能力,提供了更高级的 XML 特性。

实际上, DOM2和 DOM3 是按照模块化的思路来制定标准的,每个模块之间有一定关联,但分别针对某个 DOM 子集。这些模式如下所示。

  • DOM Core
  • DOM Views
  • DOM Events
  • DOM Style
  • DOM Travesal and Range
  • DOM HTML
  • DOM Mutation Observers
  • Xpath
  • Load and Save

# DOM 的演进

DOM2 和 DOM3 Core 模块的目标是扩展 DOM API,满足 XML 的所有需求并提供更好的错误处理和特性检测。

DOM2 Core 没有新增任何类型,仅仅在 DOM1 Core 基础上增加了一些方法和属性。

DOM3 Core 则除了增强原有类型,也新增了一些新类型。

# 样式

任何时候,只要获得了有效 DOM 元素的引用,就可以通过 JavaScript 来设置样式。来看下面的例子:

let myDiv = document.getElementById("myDiv");
// 设置背景颜色
myDiv.style.backgroundColor = "red";
// 修改大小
myDiv.style.width = "100px";
myDiv.style.height = "200px";
// 设置边框
myDiv.style.border = "1px solid black";

DOM2 Style在 document.defaultView 上增加了 getComputedStyle() 方法。这个方法接收两个参数:要取得计算样式的元素和伪元素字符串(如":after")。

getComputedStyle() 方法返回一个 CSSStyleDeclaration 对象(与 style 属性的类型一样),包含元素的计算样式。

let myDiv = document.getElementById("myDiv");
let computedStyle = document.defaultView.getComputedStyle(myDiv, null);
console.log(computedStyle.backgroundColor); // "red"
console.log(computedStyle.width); // "100px"
console.log(computedStyle.height); // "200px"
console.log(computedStyle.border); // "1px solid black"(在某些浏览器中)

# 遍历

遍历一颗 DOM 树,我们可以通过编写 BFS 或 DFS 算法来实现。 不过现在 DOM 已经为我们提供了一个遍历的工具。

DOM2 Traversal and Range 模块定义了两个类型用于辅助顺序遍历 DOM 结构。

这两个类型—— NodeIteratorTreeWalker——从某个起点开始执行对 DOM 结构的深度优先遍历。

以下代码定义了只接收<p>元素的节点过滤器对象:

let filter = {
acceptNode(node) {
  return node.tagName.toLowerCase() == "p" ?
  NodeFilter.FILTER_ACCEPT :
  NodeFilter.FILTER_SKIP;
  }
};

let iterator = document.createNodeIterator(
  root,
  NodeFilter.SHOW_ELEMENT,
  filter,
  false
);

TreeWalker 是 NodeIterator 的高级版。除了包含同样的 nextNode()、 previousNode()方法,TreeWalker 还添加了如下在 DOM 结构中向不同方向遍历的方法。

  • parentNode()
  • firstChild()
  • lastChild()
  • nextChild()
  • previousSibling()

# 范围

为了支持对页面更细致的控制, DOM2 Traversal and Range 模块定义了范围接口。范围可用于在文档中选择内容,而不用考虑节点之间的界限。(选择在后台发生,用户是看不到的。)范围在常规 DOM操作的粒度不够时可以发挥作用。

DOM2 在 Document 类型上定义了一个 createRange()方法,暴露在 document 对象上。使用这个方法可以创建一个 DOM 范围对象,如下所示:

let range = document.createRange();
  • 简单选择

通过范围选择文档中某个部分最简单的方式,就是使用 selectNode()或 selectNodeContents()方法。这两个方法都接收一个节点作为参数,并将该节点的信息添加到调用它的范围。

  • selectNode() 方法选择整个节点,包括其后代节点
  • selectNodeContents() 只选择节点的后代
let range1 = document.createRange();
let range2 = document.createRange();

p1 = document.getElementById("p1");
range1.selectNode(p1);
range2.selectNodeContents(p1);
  • 复杂选择

要创建复杂的范围,需要使用 setStart()和 setEnd()方法。这两个方法都接收两个参数:参照节点和偏移量。

  • 对 setStart()来说,参照节点会成为 startContainer,而偏移量会赋值给 startOffset。
  • 对 setEnd()而言,参照节点会成为 endContainer,而偏移量会赋值给 endOffset。

TIP

TODO 待续,这一块主要对应富文本相关知识