多年来,我们一直依赖第三方 JavaScript 库(例如 jQuery)来为 Web 编写 JavaScript。 然而,近年来,DOM API 有了很大的发展,因此使用原生 JavaScript 向网页添加动态功能对开发人员来说变得越来越容易。
本文是对每个现代 Web 浏览器中内置的 DOM API 的高级概述。 我们将研究 DOM API
是什么,它与 JavaScript 的关系,以及如何使用它为 Web 编写 JavaScript。
什么是DOM
DOM
代表文档对象模型( Document Object Model),它是 Web 文档的数据表示,例如 HTML 页面或 XML 文件。
常简单的来说,DOM 是组成网页的一堆对象(文档对象模型)。每个对象都是接口的真实实现(一个接口可以由多个对象实现)。
例如,每个 HTML <form>
元素都由 DOM 中的 HTMLFormElement
对象表示,它是 HTMLFormElement
接口的实现。该接口带有预先编写的属性(接口变量)
和方法(接口函数)
,您可以使用它们为每个表单对象(在 HTML 文档中显示为 <form>
标记)添加动态功能。例如,您可以使用 HTMLFormElement.reset()
方法将表单重置为其初始状态。
本质上,这就是整个 DOM 的工作方式。最基本的接口称为节点(Node)
——每个 HTML 元素、文本字符串和文档本身也是一个节点。其他接口扩展 Node 接口并继承其属性和方法,但由于每个接口都可以有子接口,因此继承可以在多个级别上工作。比如 HTMLFormElement
是 HTMLElement
的子接口,HTMLElement
是 Element
的子接口,Element
是 Node
的子接口。
Web 浏览器将所有这些节点排列成一个文档树(DOM 树)
,将节点及其之间的关系表示为树层次结构。
什么是DOM API
虽然有许多 DOM(每个 HTML、XML 文档都有一个 DOM),但只有一个 DOM API
,它是 W3C 规范
。 DOM API 是用 JavaScript 编写的,您可以使用它来使用 JavaScript 操作 Web 文档的 DOM。
API
代表“应用程序编程接口(Application Programming Interface)”,它是类、接口、属性、方法和其他代码结构的集合
,开发人员可以使用这些代码结构来访问应用程序的功能。
DOM API 是 Web 浏览器中内置的多个 Web API 之一。 有较低级别的 API,例如 Web Workers API(用于后台操作)和较高级别的 API,例如 DOM。
大多数 Web API,包括 DOM API,都是用 JavaScript 编写的。 首先,W3C 创建规范,然后浏览器供应商实施它们。 因此,“浏览器支持”是指浏览器是否实现了 DOM API 的特定功能。 如果浏览器支持良好,您可以安全地在生产环境中使用对象(接口实现)、属性或方法,而如果效果不佳,最好找到替代方案(您可以在 CanIUse 网站上查看浏览器支持)。
HTML 标签和属性以及 CSS 样式在 DOM API 中都有自己的句柄,因此您可以动态修改它们。 例如,以下 HTML 标记:
<a href='page.html' target='_blank'>Click this link</a>
由 DOM 中的四个节点(一个父节点和三个子节点)表示:
- (父节点)DOM 中的 HTMLAnchorElement 对象(即 HTMLAnchorElement 接口的实现); 这是一个元素节点
- (子节点)由 HTMLAnchorElement 接口定义的 HTMLAnchorElement.href 属性; 这是一个属性节点
- (子节点)一个 HTMLAnchorElement.target 属性; 这是另一个属性节点
- (子节点)文本节点(“单击此链接”)
接下来,让我们看看如何在实践中使用 DOM API。
使用 DOM API 的示例
假设您想将上面链接的文本颜色从默认的蓝色动态更改为洋红色。
如果您分析 HTMLAnchorElement 接口的文档,您将找不到任何可用于此目的的属性或方法。
但是,由于 HTMLAnchorElement 是 HTMLElement 的子接口,它也继承了它的属性和方法。 这个接口有一个 HTMLElement.style 属性,可以让你改变属于一个 HTML 元素的 CSS。 如果您阅读文档中的描述,您会发现它可以使用 CSS 属性参考中的属性,也由 DOM API 定义。
在我们的示例中,我们需要使用颜色属性,因此我们将编写以下代码:
element.style.color = 'magenta';
但是,如果我们运行此代码,它将在控制台中返回 ReferenceError 异常,因为未定义元素。 在我们可以使用 JavaScript 操作 DOM 元素之前,我们需要使用 DOM API 选择它并将其传递给一个变量。
要选择文档中的节点,我们需要用到 Document 接口。 这个接口有两个我们可以使用的方法,Document.querySelector() 找到选择器的第一个实例, Document.querySelectorAll() 找到选择器的所有实例。 在这里,我们将使用 querySelector(),因为页面上只有一个锚元素。 所以,新代码是:
// selects the first 'a' element on the page
let element = document.querySelector('a');
// sets the 'color' property to 'magenta'
element.style.color = 'magenta';
现在,此代码将正常工作并将链接颜色更改为洋红色。 这里有几点需要注意:
- 由于 style.color 是一个属性,我们可以使用赋值运算符(= 符号)来设置它的值
- 由于 document.querySelector() 是一个方法,我们可以通过在括号内添加一个参数 (‘a’) 来定义它的值
- 虽然我们需要定义 element 变量,但我们不必定义 document 变量,因为它是一个类似于 window 的全局对象(所属变量内置于浏览器中 - 请注意,该对象以大写首字母书写,例如 Document,而 变量全部小写,例如document)
- 如果页面上有多个链接,我们需要使用 querySelectorAll() 方法而不是 querySelector() 并将 element.style.color 声明放在 for 循环中以更改页面上所有链接的颜色 :
<a href='page01.html' target='_blank'>Link 01</a>
<a href='page02.html' target='_blank'>Link 02</a>
<a href='page03.html' target='_blank'>Link 03</a>
// selects all 'a' elements on the page
let elements = document.querySelectorAll('a');
// loops through the element object which is a NodeList
for (let i = 0; i < elements.length; i++) {
// sets the 'color' property to 'magenta'
elements[i].style.color = 'magenta';
}
我们需要遍历 elements 变量,因为 querySelectorAll()
返回一个包含多个项目的 NodeList
对象。
DOM API 最佳实践
DOM API 经常为同一任务提供多个解决方案。 因此,我们应该询问自己是否使用了最佳解决方案。 例如,在上面的例子中我们没有; 我们使用 JavaScript 更改了样式,这违背了关注点分离 (SoC) 软件设计原则。
如果我们为要更改颜色的每个锚标记动态添加一个类,然后使用 CSS 添加样式规则,这将更加有效,无论是性能还是工作流程。 幸运的是,DOM 有一个解决方案,分别是 Element.classList.add() 方法,我们可以在从通用 Element 对象继承的每个对象上使用该方法。
上面的示例使用此方法如下所示:
// selects all 'a' elements on the page
let elements = document.querySelectorAll('a');
// loops through the elements variable
for (let i = 0; i < elements.length; i++) {
// adds the 'dynamic-color' class to each element
elements[i].classList.add('dynamic-color');
}
然后,我们可以使用 CSS 添加颜色:
.dynamic-color {
color: magenta;
}
这样就好多了。 如果我们愿意,更改链接颜色也会更容易。