DOM:指文档对象模型,通过它,可以访问 HTML 文档的所有元素。
BOM:指浏览器对象模型,它使 JavaScript 有能力与浏览器进行"对话"。
DOM
Web 开发技术 > Web API > 文档对象模型(DOM)
Document Object Model,文档对象模型。通过将文档(例如 HTML、SVG 或 XML 文档)的结构以树形结构对象的形式存储在内存中,将网页与脚本或编程语言连接起来。这种表示方式使得开发者可以使用面向对象的方法来操作文档。
通过 DOM API,脚本或编程语言可以动态地访问和更新文档的内容、结构、样式、对元素增删改查,还可以为元素添加事件监听器。
DOM 是一种跨语言的标准接口,可以被多种编程语言所使用,而不仅限于 JavaScript。
DOM 核心名词
Tree(树)
-
定义:DOM 以树状结构表示文档,这个结构被称为 DOM 树(DOM Tree)。
-
特性:
-
树的根是 Document 对象。
-
树的节点包括 Element 节点、Text 节点、Comment 节点等。
-
通过这个树状结构,开发人员可以方便地访问、修改和删除文档中的任何部分。
-
-
示例:HTML 文档解析后生成的 DOM 树中,
<html>
元素是根节点,<head>
和<body>
是子节点,而<head>
和<body>
下的其他元素(如<div>
、<p>
等)则是更下一层的子节点。
Node(节点)
-
定义:DOM 中的基本单元,表示文档中的一个部分。
-
特性:
-
每个 Node 都是 Document 对象的一部分。
-
具有特定的属性,如 nodeType(表示节点类型)、childNodes(表示子节点集合)等。
-
-
示例:在 HTML 文档中,
<html>、<head>、<body>、<p>、<div>
等元素以及文本、注释等都是节点。
Element(元素)
-
定义:在 DOM 中,Element 是 Node 的一种,代表 HTML 或 XML 文档中的一个标签。
-
特性:
-
每个 Element 都是 Element 对象的一个实例。
-
包含了有关元素的信息,如元素的标签名、属性、样式、内容和相关事件。
-
-
示例:每个 HTML 元素(如
<html>、<body>、<div>、<p>
等)都是一个 Element 对象。
Node 和 Element 的区别
-
Node 的类型由 nodeType 属性表示,该属性是一个数字值,用于区分不同类型的节点。例如:nodeType 为 1 表示 Element 类型,为 3 表示 Text 类型,为 8 表示 Comment 类型,为 9 表示 Document 类型。
-
Element 是 Node 的一种特殊类型。
-
Element 是 Node 的一个子集。
Collection(集合)
-
定义:在 DOM 中,Collection 通常指的是一组具有相似特征或属性的 Element 对象或 Node 对象。
-
特性:
-
集合中的元素通常是有序的(如 HTMLCollection)或无序的(如 NodeList)。
-
通过特定的方法(如
getElementsByTagName
、getElementsByClassName
等)可以获取到这些集合。
-
-
示例:通过
document.getElementsByTagName('p')
可以获取到文档中所有的<p>
元素,这些元素组成了一个 HTMLCollection。
DOM 常用 API
节点查找
API | 描述 |
---|---|
document.getElementById(id) | 根据 ID 查找元素,返回单个 Node。 |
document.getElementsByClassName(className) | 根据类名查找元素,返回一个 HTMLCollection(集合)。 |
document.getElementsByTagName(tagName) | 根据标签查找元素,返回一个 HTMLCollection(集合)。 |
document.getElementsByName(name) | 根据元素的name属性查找,返回一个 NodeList(集合)。 |
document.querySelector(selector) | 返回与指定 CSS 选择器匹配的第一个 Element,如果没有匹配的元素,则返回 null |
document.querySelectorAll(selector) | 返回文档中所有与指定 CSS 选择器匹配的 Element 集合,返回一个 NodeList。 |
举例:
let element = document.querySelector('.myClass');
let element = document.querySelector('div > span');
// 遍历 a 节点的父节点下的所有子节点
let elements = document.getElementById("a").parentNode.children;
节点创建
API | 描述 |
---|---|
document.createElement(tagName) | 创建一个新的元素节点。 |
document.createTextNode(text) | 创建一个新的文本节点。 |
document.createDocumentFragment() | 创建一个新的 DocumentFragment 节点,通常用于性能优化,因为它不是文档树的一部分,插入 DocumentFragment 时,不会触发页面回流。 |
<body>
<!-- 新的div将插入到这里 -->
<div id="div1">The text above has been created dynamically. </div>
</body>
<script>
document.body.onload = addElement;
function addElement() {
// 创建一个新的 div 元素
let newDiv = document.createElement("div");
// 给它一些内容
let newContent = document.createTextNode("Hi there and greetings!");
// 添加文本节点 到这个新的 div 元素
newDiv.appendChild(newContent);
// 将这个新的元素和它的文本添加到 DOM 中
let currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
</script>
节点修改
API | 描述 |
---|---|
parentNode.appendChild(childNode) | 将一个节点添加到父节点的子节点列表的末尾。 |
parentNode.insertBefore(newNode, referenceNode) | 将一个新节点插入到父节点的子节点列表中的指定参考节点之前。 |
parentNode.removeChild(childNode) | 从父节点的子节点列表中移除一个节点。 |
parentNode.replaceChild(newNode, oldNode) | 用一个新的节点替换父节点中的一个子节点。 |
<div id="target">
<!-- 新的div将插入到这里 -->
</div>
<script>
// 1.确定插入位置
var targetElement = document.getElementById('target');
// 2. 创建新的<div>元素
var newDiv = document.createElement('div');
// 3. 设置新元素的属性或内容(可选)
newDiv.className = 'new-div'; // 添加类名(可选)
newDiv.textContent = '我是新插入的div!'; // 设置文本内容
// 4. 将新元素插入到 DOM 中
// 在 targetElement 内部插入newDiv
targetElement.appendChild(newDiv);
// 如果你想在 targetElement 之前插入 newDiv,你可以使用:
// targetElement.parentNode.insertBefore(newDiv, targetElement);
</script>
节点关系
API | 描述 |
---|---|
node.parentNode | 返回节点的父节点。 |
node.parentElement | 返回节点的父元素节点(如果父节点是元素节点的话)。 |
node.children | 返回节点的所有子元素节点(HTMLCollection) |
node.childNodes | 返回节点的所有子节点(NodeList),包括元素节点、文本节点和注释节点等。 |
node.previousSibling | 返回节点的前一个兄弟节点。 |
node.previousElementSibling | 返回节点的前一个元素兄弟节点。 |
node.nextSibling | 返回节点的后一个兄弟节点。 |
node.nextElementSibling | 返回节点的后一个元素兄弟节点。 |
<div id="parent">
我是父元素
<p id="child">我是子元素</p>
</div>
<script>
// 获取子元素
var childElement = document.getElementById('child');
// 获取子元素的父元素
var parentElement = childElement.parentNode;
// 打印父元素的 ID(注意:如果父元素是一个文本节点,则可能没有 ID)
if (parentElement.id) {
console.log('父元素的 ID 是:', parentElement.id);
} else {
console.log('父元素没有 ID');
}
</script>
节点属性
API | 描述 |
---|---|
element.getAttribute(name) | 获取元素的属性值,返回 null 或字符串。 |
element.setAttribute(name, value) | 设置元素的属性值。 |
element.removeAttribute(name) | 移除元素的属性。 |
事件处理
API | 描述 |
---|---|
element.addEventListener(type, listener) | 为指定元素添加一个事件监听器。 |
element.removeEventListener(type, listener) | 移除元素上的事件监听器。 |
-
element:要添加事件监听器的元素。
-
type:字符串,要监听的事件名称(如 ‘click’, ‘mouseover’, ‘keydown’ 等)。
-
listener:当事件触发时要调用的函数。这个函数通常接受一个事件对象作为参数,该对象包含了与事件相关的所有信息。
addEventListener 还有一个可选参数:
addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);
-
options:可选
-
capture:布尔值,设置事件是在冒泡阶段(false)触发还是捕获阶段(true)触发。默认 false。
-
once:布尔值,表示 listener 在添加之后最多只调用一次。如果为 true,listener 会在其被调用之后自动移除。
-
passive:布尔值,设置为 true 时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
-
-
signal:AbortSignal,该 AbortSignal 的 abort() 方法被调用时,监听器会被移除。
-
useCapture:可选,布尔值,设置事件是在冒泡阶段(false)触发还是捕获阶段(true)触发。默认 false。
举例:
// 获取元素
var button = document.getElementById('myButton');
// 定义 listener
function handleClick(event) {
alert('Button clicked!');
console.log(event.target); // 输出触发事件的元素
}
// 添加事件监听器
button.addEventListener('click', handleClick);
注意事项:
-
多个监听器:你可以为同一个元素和同一个事件添加多个监听器。当事件触发时,这些监听器会按照它们被添加的顺序依次执行。
-
移除监听器:如果你不再需要某个监听器,可以使用 removeEventListener 方法来移除它。但是,请注意,removeEventListener 需要你提供与添加时完全相同的函数引用作为参数。因此,如果你使用匿名函数作为回调函数,你将无法移除它。
BOM
BOM(Browser Object Model)浏览器对象模型,它提供了浏览器窗口、导航等的属性和方法。
BOM 允许 JavaScript 与浏览器进行交互,例如控制浏览器窗口的大小、位置,获取浏览器信息,以及处理历史记录等。
window 对象
BOM 是一个模型,它不是一个具体对象,谈论 BOM 时,我们通常是在一个更宏观的层面上讨论,谈论 window 对象时则更加具体,比如 window 对象的属性和方法。
BOM 的核心是 window 对象,window 对象代表了浏览器窗口本身,一个浏览器窗口就是一个 window 对象,多个窗口则有多个 window 对象,它们互相独立。
对于 window 对象,无论是它的属性,还是方法,都可以省略 window 前缀。如 window.alert() 可以简写为 alert(),window.document.getElementById() 可以简写为 document.getElementById(),以此类推。
window 对象的常用属性和方法
属性/方法 | 说明 |
---|---|
innerHeight | 浏览器窗口的内部高度(不包括工具栏和滚动条)。 |
innerWidth | 浏览器窗口的内部宽度 |
outerHeight | 返回窗口的外部高度(包括边框和滚动条)。 |
outerWidth | 返回窗口的外部宽度 |
name | 设置或返回窗口的名称。 |
status | 设置或返回浏览器状态栏的文本(部分现代浏览器可能不支持修改状态栏)。 |
alert() | 提示对话框 |
confirm() | 判断对话框 |
prompt() | 输入对话框 |
open() | 打开窗口 |
close() | 关闭窗口 |
setTimeout() | 开启一次性定时器 |
clearTimeout() | 关闭一次性定时器 |
setInterval() | 开启重复性定时器 |
clearInterval() | 关闭重复性定时器 |
window 对象属性
window 对象存放了这个页面的所有信息,为了更好地分类处理这些信息,window 对象又有很多属性:
属性 | 说明 |
---|---|
document | 文档对象,用于操作页面元素 |
location | 地址对象,用于操作 URL 地址 |
navigator | 浏览器对象,用于获取浏览器版本信息 |
history | 历史对象,用于操作浏览历史 |
screen | 屏幕对象,用于操作屏幕宽度高度 |
document
document 对象是 window 对象的一个属性。很多人误以为一个窗口就是一个 document 对象,但实际上,一个窗口包含的内容远不止 HTML 文档,还包括浏览器信息、历史记录、地址栏等。而 document 对象专门用于操作 HTML 文档中的元素。
DOM API 是如何与 window.document 关联的?
在浏览器环境中,每个加载的 HTML 页面都会被解析成一个 DOM 树,这个 DOM 树的根节点就是 document 对象。通过 window.document,我们可以访问到这个 DOM 树,进而使用 DOM API 来操作树中的节点。
需要注意的是,DOM 树是独立于 window 对象存在的,但它可以通过 window.document 属性被访问和操作。DOM 是一种编程接口,而 window 是一个全局对象,两者并不互相包含。
既然 DOM 树是独立于 window 对象存在的,为什么 window 可以通过 document 属性操作 DOM?
当浏览器加载一个 HTML 文档时,它会根据文档内容构建一个 DOM 树,并将 DOM 树的根节点(即 document 对象)暴露给 window 对象。因此,当开发者在 JavaScript 代码中引用 document 时,实际上是在引用这个 DOM 树的根节点。通过 document 对象及其属性和方法,开发者可以操作 DOM API。
一句话总结:document 是 window 对象的一个属性,代表当前页面的 DOM 树的根节点,而 DOM 是独立于 window 的编程接口,通过 window.document 可以访问和操作 DOM。
location
location 子对象来操作当前窗口的 URL,常用的属性和方法:
属性 | 说明 |
---|---|
href | 当前页面地址 |
search | 当前页面地址 “?” 后面的内容 |
hash | 当前页面地址 “#” 后面的内容 |
reload() | 重新加载当前页面 |
assign(url) | 加载指定的URL |
replace(url) | 用指定的URL替换当前页面,无法通过后退按钮返回 |
例如:window.location.href
history
// 后退到上一个页面
window.history.back();
// 前进到下一个页面
window.history.forward();
// 前进或后退两个页面
window.history.go(2);
// 获取历史记录中页面的数量
const historyLength = window.history.length;
// 向浏览器历史记录中添加一个新的状态
window.history.pushState({ page: 'page1' }, '页面1', '/page1');
// 替换当前页面在浏览器历史记录中的状态
window.history.replaceState({ page: 'page2' }, '页面2', '/page2');
history.pushState 和 history.replaceState 详解:
在传统的多页面应用中,每当用户点击跳转链接或跳转按钮时,浏览器都会加载一个新的页面,并更新历史记录。但在单页面(SPA)应用中,整个应用只在一个页面上运行,页面的不同部分(如不同的视图或组件)会根据用户的操作动态地显示或隐藏。
问题在于,虽然 SPA 中的视图在变化,但浏览器的 URL 并没有改变,这导致用户无法通过 URL 来分享当前的应用状态,也无法通过浏览器的后退按钮来回到上一个视图。
为了解决上述问题,HTML5 引入了 History API,其中的 history.pushState()
和 history.replaceState()
方法允许我们在不重新加载页面的情况下,更新浏览器的 URL 和历史记录。
-
history.pushState() :该方法会在浏览器的历史记录中添加一个新的条目。接收 3 个参数:
-
state(必需):
-
一个 JavaScript 对象,用于存储数据,可以将任何与当前页面状态相关的数据(比如页面编号、用户信息、表单数据等)存储在 state 中。
-
当用户通过浏览器的前进/后退按钮导航到该历史记录条目时,可以通过 popstate 事件获取这些数据,从而恢复页面的状态。
-
可以是任何可序列化的对象,如果不需要状态数据,可以传递 null。
-
-
title(目前大多数浏览器忽略):
-
理论上用于设置历史记录条目的标题,但大多数浏览器会忽略此参数。
-
通常传递空字符串 “” 或 null。
-
-
url(可选):
-
新的 URL,用于更新浏览器的地址栏。
-
可以是绝对路径或相对路径。
-
如果省略,则 URL 不会改变,但历史记录仍会更新。
-
当这个方法被调用时,浏览器的 URL 会更新,但页面不会重新加载。
-
-
history.replaceState():这个方法与
pushState
类似,区别是它不是添加一个新的历史记录条目,而是替换当前的历史记录条目。
history.pushState()
和 history.replaceState()
允许你在不重新加载页面的情况下,更新浏览器的 URL 和历史记录。pushState
会添加新的历史记录条目,而 replaceState
会替换当前的历史记录条目。这两个方法对于构建单页面应用来说非常有用。
navigator
navigator 子对象用来获取浏览器的类型
例如:window.navigator.userAgent
DOM 和 BOM 的区别
-
跨语言性
-
DOM:是一种跨语言的标准接口,可以被多种编程语言所使用,而不仅限于 JavaScript
-
BOM:特定于 JavaScript 和 Web 浏览器的概念,Python 中就没有 BOM 的概念
-
-
交互方式
-
DOM:通过对象之间的嵌套和引用进行交互。在 DOM 中,每个元素都是一个对象,具有自己的属性和方法。
-
BOM:主要通过 window 对象与 JavaScript 进行交互
-
-
应用范围
-
DOM:用于文档内容的操作和交互,如查找、修改、添加或删除 HTML 元素等。
-
BOM:用于浏览器窗口和浏览器的交互,例如操作浏览器窗口、访问浏览器历史等。
-
-
标准与规范
-
DOM:是 W3C 组织推荐的处理可扩展标志语言的标准编程接口,具有跨浏览器的兼容性。
-
BOM:虽然 BOM 是浏览器提供的,但它并不是 W3C 标准的一部分,因此不同浏览器之间可能存在差异。
-