浏览器在前端开发中无时无刻不在接触,但是浏览器里面的DOM对象及其DOM API真正的了解吗?
DOM API 是最早被设计出来的一批 API,也是用途最广的一批 API,而这里要学习的 DOM,指的是狭义的文档对象模型。
DOM API 介绍
在学习 DOM API 前先了解一下文档对象模型,顾名思义,文档对象模型是用来描述文档,这里的文档特指的是 HTML 文档(也用于 XML 文档);同时,它又是一个“对象模型”,这意味着它使用的是对象这样的概念来描述 HTML 文档。
而 HTML 文档是一个由标签嵌套而成的树形结构,因此,DOM 也是使用树形的对象模型来描述一个 HTML 文档。
DOM API 大致包含四个部分:
- 节点:DOM 树形结构中的节点相关 API
- 事件:触发和监听事件相关 API
- Range:操作文字范围相关 API
- 遍历:遍历 DOM 需要的相关 API
下面对节点进行学习。
节点
DOM 的所有树形结构所有的节点有统一的接口 Node,按照继承关系如下图(来自winter老师):
在这些节点中,除了 Document 和 DocumentFragment 都有与之对应的 HTML 写法。
1 | Element: <tagname>...</tagname> |
我们在编写 HTML 代码并运行后就会在内存中得到这样一棵 DOM 树,HTML 的写法会被转换成对应的文档模型,这样我们就能够通过 JavaScript 这样的语法去访问这个对象模型。这里要掌握的是 Document、Element、Text 节点。
DocumentFragment 也比较重要,它常常被用来高性能的批量添加节点。而这几个 Comment、DocumentType和ProcessingInstruction 了解即可,因为很少需要运行时去修改和操作。
Node
Node 是 DOM 树继承关系的根节点,它定义了 DOM 节点在 DOM 树上的操作;首先,Node 提供了一组属性,来表示它在 DOM 树中的关系,如下:
- parentNode
- childNodes
- firstChild
- lastChild
- nextSibling
- previousSibling
有了这几个属性,我们可以很方便的根据相对位置获取元素。当然,Node 中也提供了操作 DOM 树的 API,主要有如下几个:
- appendChild
- insertBefore
- removeChild
- replaceChild
这几个 API 也是见名知意,但它们受到的最主要批评是不对称 —— 只有 before,没有 after,而 jQuery 框架对其进行了补充。
实际上,appendChild 和 insertBefore 的这个设计是一个“最小原则”的设计,这两个 API 是满足插入任意位置的必要 API,如果要实现 insertAfter 这样的 API,则可以由这两个 API 来实现。
还有一点,这几个修改型的 API,全都是在父元素上操作的;除此之外,Node 还提供了一些高级的 API,如下:
- compareDocumentPosition 是一个用于比较两个节点中关系的函数
- contains 判断一个节点是否包含另一个节点
- isEqualNode 检查两个节点是否相等
- isSameNode 检查两个节点是否是同一个节点,实际在 JavaScript 中可以用 “===”
- cloneNode 复制一个节点,如果传入 true,则会连同子元素一起做深拷贝
DOM 标准规定了节点必须从 create 方法创建出来,不能够使用原生的 JavaScript 的 new 运算,于是 document 对象又这些方法:
- createElement
- createTextNode
- createCDATASection
- createComment
- createProcessingInstruction
- createDocumentFragment
- createDocumentType
这些方法都是用于创建节点类型。下面继续
Element 与 Attribute
Node 提供了树形结构上节点相关的操作,而大部分时候比较关心的是元素 Element,它是 Node 的子类。
元素对应了 HTML 中的标签,它既有子节点又有属性,所以 Element 子类中,有一系列操作属性的方法。
首先,把元素的 Attribute 当做字符串看待,有如下 API:
- getAttribute
- setAttribute
- removeAttribute
- hasAttribute
如果追求性能,还可以吧 Attribute 当做节点:
- getAttributeNode
- setAttributeNode
此外,还可以使用 attributes 对象,如:
document.body.attributes.class = ‘class名称’ 等效于 document.body.setAttribute(‘class’, ‘class名称’)。
查找元素
document 节点提供了查找元素的能力,有如下 API:
- querySelector
- querySelectorAll
- getElementById
- getELementsByName
- getELementsByTagName
- getElementsByClassName
需要知道的是,getElementById、getElementsByName、getElementsByTagName、getElementsByClassName 这几个 API 性能高于 querySelector;而这几个 API 获取的集合并非数组,而是一个能够动态更新的集合。
遍历
通过 Node 的相关属性,我们可以用 JavaScript 遍历整个树,实际上,DOM API 还提供了 NodeIterator 和 TreeWalker 来遍历树。
比起直接用属性来遍历,NodeIterator 和 TreeWalker 提供了过滤功能(来自winter老师课程),还可以把属性节点也包含在遍历之内;NodeIterator 基本用法:
1 | var iterator = document.createNodeIterator(document.body, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT, null, false); |
再看一下 TreeWalker 的基本用法:
1 | var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, null, false) |
这里两个方法一般情况不会怎么使用,这里了解一波。
Range
Range API 是一个更加专业的领域,它主要用来做富文本编辑之类的业务功能,这里仅做学习理解。Range API 表示一个 HTML 上的范围,这个范围是以文字为最小单位的,所以 Range 不一定包含完整的节点,它可能是 Text 节点中的一段等。
通过 Range API 能够比节点 API 更准确的操作 DOM 树,凡是节点 API 能做到的,Range API 都可以做到,而且性能更好,一般在实际项目中不适用,只有底层框架和做富文本编辑器有强需求。
创建 Range 一般是设置它的起止来实现,看一个例子:
1 | var range = new Range(), |
总结
从 DOM API 的内容及分类进行了学习,对它相应的一些操作 API 进行了补强。