JavaScript:文档对象模型

文档对象模型

Document Object Model 表示文档对象模型,经常简称为「DOM」。

当向网站发出请求时 HTML 网页会发生:

  1. 收到 HTML
  2. HTML 标签被转换为令牌
  3. 令牌被转换为节点
  4. 节点被转换为 DOM

当你请求一个网站时,无论该网站的后端语言是什么,它都会用 HTML 进行响应。浏览器会接收到一连串的 HTML。这些字节通过一个复杂的(但完全记录在案的)解析过程来运行,该过程可以确定不同的字符(例如开始标签字符 <,像 href 这样的属性,像 > 这样的右尖括号)。解析发生后,接下来是一个称为标记化的过程。标记化过程每次使用一个字符来构建令牌。这些令牌包括:

  • 文档类型(DOCTYPE)
  • 开始标签(start tag)
  • 结束标签(end tag)
  • 注释(comment)
  • 字符(character)
  • 文件结束(end-of-file)

在这个阶段,浏览器已经收到了服务器发送的字节,并将字节转换为标签,然后读取了所有标签,进而创建了一个令牌列表。

接下来,这个令牌列表将会通过树构建阶段。这个阶段的输出结果是一个树状结构——这就是 DOM!

重点有二:

  • 一个树状结构,反映了 HTML 的内容和属性,以及节点之间的所有关系
  • DOM 是 HTML 的完整解析表示

因此,DOM 是所接收到的 HTML 文档的关系和属性的模型(描述)。请记住,DOM 代表「文档对象模型」。在学习过程中要想理解某个名词,只要倒着读一下就清楚了:

Document Object Model(文档对象模型)

…倒过来就是…

Object Model of the Document(文档的对象模型)!

请记住,JavaScript 对象是一个具有属性和值的树状结构。因此,可以使用浏览器提供的一个特殊对象来访问 DOM:document

尝试一下:

  1. 打开 https://duckduckgo.com/ 并打开控制台
  2. 打出单词 document 并回车
    • 注意不要声明它 (const document)
    • 注意不要用引号括起来 (“document”)

返回:
HTMLDocument

document 对象是由浏览器提供的,是 HTML 文档的表示。这个对象不是由 JavaScript 语言提供的。ECMAScript 是 JavaScript 所基于的语言规范,而它只在一个地方引用文档对象模型,就是在其「全局对象」部分:

除了本规范中定义的属性之外,全局对象还可以有其他主机定义的属性。这可能包括一个值为全局对象本身的属性;例如,在 HTML 文档对象模型中,全局对象的窗口属性就是全局对象本身。——来源

这就是说,document 对象不是 JavaScript 的一部分,但应该已经存在,并且可供 JavaScript 代码自由访问。

DOM 由 W3C 组织进行标准化。有很多组成 DOM 的规范,以下是其中几个:

  • 核心规范
  • 事件规范
  • 样式规范
  • 验证规范
  • 加载和保存规范

要查看 DOM 规范的完整列表,请参见标准

文档对象模型小结

DOM 代表“文档对象模型”,是一种树状结构,是 HTML 文档的表示,反映了元素之间的关系,并包含元素的内容和属性。

DOM 不是

  • JavaScript 语言的一部分

DOM 是:

  • 从浏览器构建的
  • 可以通过使用 document 对象供 JavaScript 代码全局访问

延伸

使用 CSS 选择器选择页面元素

通过 ID 来选择元素

接着就利用上面的得到 document 对象!请记住 document 对象是一个对象,就像一个 JavaScript 对象一样,这意味着它有键/值对。有些值只是数据片段,而其他值则是可以提供某种功能的函数(也称为方法!)。第一个要说的 DOM 方法是 .getElementById() 方法:

let container = document.getElementById("container"); // 查找并返回名为 container 的 ID 元素

小结

  • 它在 document 对象上被调用
  • 因为使用的是 .getElementById() 从字面意思就是获取一个 ID 的元素,所以括号内的 ID 名不需要像 CSS 里一样加上井号。
  • 查找并返回单个项目
  • 如果查找的 ID 元素不存在会返回:null

如果想了解更多有 getElementById() 的信息,请查看 MDN 文档

通过类或标签选择页面元素

在学习 HTML 结构和 CSS 样式时了解到的,ID 应该是唯一的。这意味着,两个或更多元素不应该具有相同的 ID。由于 ID 是唯一的,而且在 HTML 中只有一个元素具有该 ID,因此 document.getElementById() 最多只会返回一个元素。那么,我们如何选择多个 DOM 元素呢?

接下来我们将要介绍的两个 DOM 方法都会返回多个元素,它们是:

  • .getElementsByClassName()
  • .getElementsByTagName()

注意:相比于getElementById()多了个s

通过类来访问元素

document.getElementsByClassName('sider-bar');

如果想了解更多有 getElementById() 的信息,请查看 MDN 文档

通过标签来访问元素

document.getElementsByTagName('div');

如果想了解更多有 getElementsByTagName() 的信息,请查看 MDN 文档

小结

  • 两种方法都使用 document 对象
  • 二者都会返回多个项目
  • 所返回的是 HTML 列表虽然像但不是数组

接口

节点接口

例如在 HTML 中尖括号里的文本(<>)具有特殊含义属于标记,因此每当遇到一个标记浏览器都会发出一个令牌,完成后到下一个令牌

<!doctype html> 
<html lang="en">StartTag:html
<head> StartTag:head
    <meta charset="UTF-8"> Tag:meta
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>Tag:title
</head> EndTag:head
<body>  StartTag:body

</body> EndTag:body
</html> EndTag:html

整个流程都由令牌解析器来完成,当令牌解析器在执行这一流程时有另一个流程正在消耗这些令牌并将它们转换为节点对象。这个过程,就是:

  • 字符
  • 标签
  • 令牌
  • 节点
  • DOM

节点(Node,大写「N」!)是一个蓝图,包含有关真实节点(nodes,小写「n」!)所有属性和方法的信息。 如果不熟悉这些词汇,「接口」、「属性」和「方法」这些词刚开始可能不太好懂。只要记住:

  • 接口 = 蓝图
  • 属性 = 数据
  • 方法 = 功能

如果想了解更多有「Node」的信息,请查看 MDN 文档

节点接口是每个真实节点创建后的所有属性(数据)和方法(功能)的蓝图。现在,节点接口有很多属性和方法。

元素接口

正如节点接口一样,元素接口是用于创建元素的蓝图:MDN 文档

有关元素接口非常重要的一点是,它是节点接口的子代。

由于元素指向节点,这表明元素接口继承了所有节点接口的属性和方法。这意味着,从元素接口创建的任何元素(element,小写「e」!)同时也是节点接口的子代…也就是说,元素(element,小写「e」!)同时也是节点(node,小写「n」!)。

$0

当在页面调试的时候,选中了一个元素,这时候按「Esc」可以快速调出控制台

$0

返回:
<a id="logo_homepage_link" class="logo_homepage" href="/about">

$0所返回的就是当前页面上所选的元素,这时候还可以更进一步

$0.className

返回:
"logo_homepage"

不仅是 document 对象上有 .getElementsByClassName() 方法吗,在查看元素接口的时候,它也有一个 .getElementsByClassName() 方法!元素接口继承自节点接口,而不是文档接口(是的,还有一个文档接口!)。元素接口有自己的 .getElementsByClassName(),它和 document 对象上的同名方法具有完全相同的功能。

这意味着,你可以使用 document 对象来选择一个元素,然后可以对该元素调用 .getElementsByClassName(),从而接收到一系列带有该类名的元素,它们都是该特定元素的子代!

// 选择 ID 为 "sidebar" 的 DOM 元素
const sidebarElement = document.getElementById('sidebar');
// 在 "sidebar" 元素中找寻任何具有 "sub-heading" 类的元素
const subHeadingList = sidebarElement.getElementsByClassName('sub-heading'); `

要查看所有不同的接口,请参见:Web API 接口

更多访问元素的方法

上面介绍了:

  • .getElementById()
  • .getElementsByClassName()
  • .getElementsByTagName()

这些 DOM 方法都是标准化的。但是,并非所有浏览器都支持每个标准。它们目前 都支持这三种方法,但此外还有数百种其他方法受到不同程度的支持。

因此,几乎 MDN 上的每个方法都有一个浏览器兼容性表格,列出了每个浏览器开始支持该特定方法的时间。

值得庆幸的是,所有浏览器基本上都支持官方标准。

但在过去,情况并非如此。你必须编写不同的代码,才能在不同的浏览器中执行相同的操作。而且,你还得编写代码来检查所使用的浏览器类型,才能为该浏览器运行正确的代码。说实话,这种体验简直糟透了。

几个 JavaScript 库应运而生,以帮助缓解这些问题,比如大名鼎鼎的 jQuery:

jQuery 库的主要作用之一是抽象化不同浏览器之间的区别,作为开发者使用特定的 jQuery 方法,然后 jQuery 会判断运行的是哪个浏览器并针对该浏览器使用正确的代码,因为 jQuery 使我们可以非常轻松地编写可在多个浏览器中正确运行的代码,所以变得热门起来。但随着现在每个浏览器都尝试支持官方标准,所以又逐渐不再使用 jQuery 并将其替换成原生 DOM 方法,但是 jQuery 不再流行后出现了新的 DOM 方法。

之前所介绍的是通过 ID、类和标签进行对元素的选择,而接下来有一种方法可以像 CSS 那样进行元素选择。

#header {
    color: 'red';
}

.header {
    color: 'red';
}

header {
    color: 'red';
}

以上每一项都会将颜色设置为红色。唯一的区别在于选择器;通过 ID 进行选择、通过类进行选择、通过标签进行选择。

querySelector

// 找寻并且返回 ID 名为 "header" 的元素
document.querySelector('#header');

// 找寻并且返回第一个 类 名为 "header" 的元素
document.querySelector('.header');

// 找寻并返回第一个 <header> 元素
document.querySelector('header');

注意,和 getElementById() 类似,querySelector() 只会返回一个元素,但是不同于前者,后者还可以找到类和标签元素,但是如果有多个结果 querySelector() 只会返回找到的第一个项目。

如果想了解更多有 querySelector() 的信息,请查看 MDN 文档

querySelectorAll

// 找寻并返回所有 类 名为 "header" 的元素列表
document.querySelectorAll('.header');

// 找寻并返回所有 <header> 的元素列表
document.querySelectorAll('header');

想查找查找所有累或标签元素的结果可使用 querySelectorAll() 解决

const allHeaders = document.querySelectorAll('header');
for(let i = 0; i < allHeaders; i++){
    console.dir(allHeaders[i]);
}

和 DOM 方法类似,所返回的集合也不是数组,而是 NodeList 所以在处理返回结果的时候不能使用数组方法,可以使用简单的 for 循环或者 .forEach() 方法(也叫 forEach 但是不是数组方法的那个)

如果想了解更多有 querySelectorAll() 的信息,请查看 MDN 文档

您可能还喜欢...

发表评论

电子邮件地址不会被公开。 必填项已用*标注