JavaScript DOM
一、DOM 核心概念与规范
1. 官方定义
- DOM 是W3C 制定的国际标准,本质是一套 “访问和操作网页元素” 的接口规范,目的是让程序(如 JavaScript)能动态修改网页的内容、样式、结构。
- 规范的 “落地实现”:浏览器将 DOM 规范以对象的形式内置(如
document对象、元素对象),开发者通过调用这些对象的属性和方法操作网页。 - 兼容性:1998 年 W3C 发布DOM Level 1规范,目前所有主流浏览器均完全支持该标准,DOM 兼容性问题已基本消除。
2. DOM 的核心作用
- 对 HTML 文档进行增、删、改、查操作;
- 响应网页事件(如点击、加载);
- 动态修改元素样式、属性或内容。
二、DOM 结构:倒树形 “节点树”
HTML DOM 将整个文档抽象为倒树形结构,树中的每一个 “组成部分” 都称为 “节点(Node)”,树的层级关系反映了 HTML 元素的嵌套关系。
结构示例(简化)
plaintext
document(文档节点)
└── html(元素节点)
├── head(元素节点)
│ └── title(元素节点)
│ └── "DOM总结"(文本节点)
└── body(元素节点)
├── div(元素节点,id="box")
│ ├── "这是"(文本节点)
│ ├── span(元素节点,class="text")
│ │ └── "示例文本"(文本节点)
│ └── <!-- 注释 -->(注释节点)
└── a(元素节点,href="https://baidu.com")
└── "百度"(文本节点)
三、DOM 的 5 大组成部分
DOM 规范按功能分为 5 类,覆盖不同场景的操作需求:
| 分类 | 作用说明 |
|---|---|
| 核心 DOM | 通用接口,可同时操作 HTML 和 XML 文档(如节点的增删改查、节点关系获取) |
| XML DOM | 仅针对 XML 文档的专用接口(HTML 开发中极少使用) |
| HTML DOM | HTML 专用接口,简化 HTML 元素操作(如document.getElementById、element.style) |
| DOM 事件模型 | 定义 DOM 元素可响应的事件(如onclick、onload、onchange),及事件处理机制 |
| (扩展)DOM 样式 | 专门用于操作元素样式的接口(如element.classList、element.getComputedStyle) |
四、DOM 节点:类型、属性与关系
1. 5 种核心节点类型(HTML 常用)
每个节点都有固定的nodeType(只读),用于区分类型:
| 节点类型 | nodeType 值 | 描述示例 | 关键特征 |
|---|---|---|---|
| 文档节点 | 9 | document对象(整个 HTML 文档的根) |
唯一,无父节点 |
| 元素节点 | 1 | <div>、<a>、<input>等 HTML 标签 |
可包含子节点(文本、元素等) |
| 属性节点 | 2 | 元素的属性(如id="box"、href="xxx") |
依附于元素节点,无子节点 |
| 文本节点 | 3 | 元素内的文本(如<div>文本</div>中的 “文本”) |
无子节点,nodeName为#text |
| 注释节点 | 8 | HTML 中的<!-- 这是注释 --> |
不显示在页面,nodeName为#comment |
2. 节点的 3 个核心属性(nodeName/nodeValue/nodeType)
所有节点都包含这 3 个属性,用于获取节点的基本信息(nodeName和nodeType只读):
| 节点类型 | nodeName(节点名称) | nodeValue(节点值) | nodeType(节点类型) |
|---|---|---|---|
| 文档节点 | #document |
null |
9 |
| 元素节点 | 标签名(大写,如DIV、A) |
null |
1 |
| 属性节点 | 属性名(如id、href) |
属性值(如box、https://xxx) |
2 |
| 文本节点 | #text |
文本内容(如 “Hello”) | 3 |
| 注释节点 | #comment |
注释内容(如 “这是注释”) | 8 |
3. 节点关系:父、子、同胞与后代 / 先辈
节点树中,节点间的关系如同 “家族树”,通过关系属性可快速定位其他节点:
| 关系类型 | 属性 / 方法 | 描述 |
|---|---|---|
| 父节点 | node.parentNode |
获取当前节点的直接父节点(仅 1 个,若为document则返回null) |
| 子节点 | node.childNodes |
获取所有子节点(包含文本、注释节点,返回类数组NodeList) |
node.children |
仅获取元素子节点(排除文本、注释,返回HTMLCollection,常用) |
|
| 第一个子节点 | node.firstChild |
获取第一个子节点(可能是文本 / 注释节点) |
node.firstElementChild |
仅获取第一个元素子节点(常用,避免文本节点干扰) | |
| 最后一个子节点 | node.lastChild |
获取最后一个子节点(可能是文本 / 注释节点) |
node.lastElementChild |
仅获取最后一个元素子节点(常用) | |
| 前一个同胞节点 | node.previousSibling |
获取前一个同级节点(可能是文本 / 注释节点) |
node.previousElementSibling |
仅获取前一个元素同胞节点(常用) | |
| 后一个同胞节点 | node.nextSibling |
获取后一个同级节点(可能是文本 / 注释节点) |
node.nextElementSibling |
仅获取后一个元素同胞节点(常用) | |
| 后代节点 | node.querySelectorAll(selector) |
获取当前节点下所有匹配选择器的后代元素节点(常用) |
| 先辈节点 | 需通过parentNode递归向上查找 |
如:function getAncestors(node) { let arr = []; while(node.parentNode) { arr.push(node.parentNode); node = node.parentNode; } return arr; } |
五、获取 DOM 节点对象
所有获取操作的 “起点” 是document对象,根据需求选择不同方法:
| 方法 | 作用 | 返回值类型 | 关键说明 |
|---|---|---|---|
document.getElementById(id) |
通过 ID 获取元素 | 单个元素对象(或null) |
ID 在文档中唯一,效率最高 |
document.getElementsByTagName(tag) |
通过标签名获取元素 | HTMLCollection(类数组) |
动态更新(元素变化时自动同步) |
document.getElementsByClassName(cls) |
通过类名获取元素 | HTMLCollection(类数组) |
支持多类名(用空格分隔,如"box active") |
document.getElementsByName(name) |
通过name属性获取元素 |
NodeList(类数组) |
常用于表单元素(如<input name="username">) |
document.querySelector(selector) |
通过 CSS 选择器获取第一个匹配元素 | 单个元素对象(或null) |
支持复杂选择器(如"div#box .text") |
document.querySelectorAll(selector) |
通过 CSS 选择器获取所有匹配元素 | NodeList(类数组) |
静态(元素变化时不自动更新),支持复杂选择器 |
元素对象。方法(如div.getElementsByTagName(tag)) |
获取指定元素下的子节点 | 同对应document方法 |
缩小查找范围,效率更高 |
实用封装:简化获取操作
javascript
运行
// 封装querySelector(获取单个元素)
function $(selector) {
return document.querySelector(selector);
}
// 封装querySelectorAll(获取多个元素,返回数组)
function $$(selector) {
return Array.from(document.querySelectorAll(selector));
}
// 使用示例
const box = $('#box'); // 获取#box元素
const items = $$('ul li'); // 获取所有ul下的li元素(返回数组,支持forEach)
六、操作 DOM 节点对象
1. 操作元素属性
(1)获取属性值
| 方法 | 适用场景 | 示例 |
|---|---|---|
元素对象。属性名(如div.id) |
获取 HTML 原生属性(如id、href、src) |
div.id → "box";a.href → 完整 URL(如https://baidu.com) |
element.getAttribute(attrName) |
获取任意属性(包括自定义属性) | div.getAttribute("data-index") → "1";div.getAttribute("class") → "box active" |
element.dataset.attr |
获取data-*自定义属性(H5 新增) |
元素div data-index="1" data-msg="hello" → div.dataset.index → "1";div.dataset.msg → "hello" |
element.attributes |
获取元素所有属性的集合 | div.attributes → NamedNodeMap(类数组,可通过索引或属性名访问,如div.attributes["id"]) |
(2)设置属性值
| 方法 | 适用场景 | 示例 |
|---|---|---|
元素对象。属性名 = 值(如div.id = "newBox") |
设置 HTML 原生属性 | a.href = "https://google.com";input.value = "用户名" |
element.setAttribute(attrName, value) |
设置任意属性(包括自定义属性) | div.setAttribute("class", "active");div.setAttribute("data-index", "2") |
element.dataset.attr = value |
设置data-*自定义属性 |
div.dataset.msg = "new message" → 元素添加data-msg="new message" |
element.classList.add/remove/toggle |
操作class属性(推荐) |
div.classList.add("active");div.classList.remove("box");div.classList.toggle("show")(存在则移除,不存在则添加) |
(3)移除属性
| 方法 | 适用场景 | 示例 |
|---|---|---|
element.removeAttribute(attrName) |
移除任意属性 | div.removeAttribute("data-index") |
delete element.dataset.attr |
移除data-*自定义属性 |
delete div.dataset.msg |
2. 操作元素内容
| 方法 / 属性 | 作用 | 特点 | 示例 |
|---|---|---|---|
element.innerHTML |
获取 / 设置元素内的 HTML 内容(包括标签) | 支持 HTML 解析,可插入标签 | div.innerHTML = "<span>新内容</span>" → 元素内插入<span>标签 |
element.outerHTML |
获取 / 设置元素自身 + 内部的 HTML 内容 | 会替换元素本身 | div.outerHTML = "<p>替换div</p>" → div被<p>替换 |
element.innerText |
获取 / 设置元素内的纯文本内容 | 忽略 HTML 标签,仅保留文本 | div.innerText = "<span>文本</span>" → 页面显示<span>文本</span>(不解析标签) |
element.textContent |
同innerText(标准属性,兼容性更好) |
忽略 HTML 标签,返回所有文本(包括隐藏文本) | 推荐优先使用(innerText为 IE 专有,后被其他浏览器兼容) |
formElement.value |
获取 / 设置表单元素的值 | 仅适用于表单元素(如input、textarea、select) |
input.value = "默认值";textarea.value = "多行文本" |
3. 操作节点树(增删改克隆)
(1)创建节点
| 方法 | 作用 | 示例 |
|---|---|---|
document.createElement(tagName) |
创建元素节点 | const div = document.createElement("div"); |
document.createTextNode(text) |
创建文本节点 | const textNode = document.createTextNode("新文本"); |
document.createComment(comment) |
创建注释节点 | const commentNode = document.createComment("这是新注释"); |
element.appendChild(childNode) |
将节点添加为当前元素的最后一个子节点 | div.appendChild(textNode); → 文本节点添加到div内部末尾 |
element.insertBefore(newNode, referenceNode) |
将节点插入到referenceNode之前 |
div.insertBefore(newDiv, existingDiv); → 新节点插入到existingDiv前面 |
(2)删除节点
| 方法 | 作用 | 示例 |
|---|---|---|
parentNode.removeChild(childNode) |
通过父节点删除子节点 | const parent = document.getElementById("parent"); const child = document.getElementById("child"); parent.removeChild(child); |
element.remove() |
直接删除当前元素(H5 新增,推荐) | child.remove(); → 无需获取父节点,直接删除自身 |
(3)替换节点
| 方法 | 作用 | 示例 |
|---|---|---|
parentNode.replaceChild(newNode, oldNode) |
用新节点替换父节点下的旧节点 | const newDiv = document.createElement("div"); parent.replaceChild(newDiv, oldDiv); |
(4)克隆节点
| 方法 | 作用 | 关键说明 | 示例 |
|---|---|---|---|
element.cloneNode(deep) |
克隆当前节点 | deep为true时:克隆节点及所有后代节点;deep为false时:仅克隆节点本身(无后代) |
const cloneDiv = div.cloneNode(true); → 克隆div及所有子节点 |
七、DOM 元素尺寸与位置获取
在前端开发中(如拖拽、放大镜、滚动定位等场景),需要精准获取 DOM 元素的尺寸(宽高)和位置(坐标),DOM 提供了多组属性用于满足不同需求,需根据场景选择合适的属性。
1. 核心尺寸属性:获取元素宽高
元素的 “宽高” 分为内容区尺寸、内容 + 内边距尺寸、内容 + 内边距 + 边框尺寸,对应不同的属性:
| 属性 | 含义 | 是否包含内边距(padding) | 是否包含边框(border) | 是否包含外边距(margin) | 只读 / 可写 |
|---|---|---|---|---|---|
element.offsetWidth |
元素内容 + 内边距 + 边框的宽度(含垂直滚动条宽度,若存在) | 是 | 是 | 否 | 只读 |
element.offsetHeight |
元素内容 + 内边距 + 边框的高度(含水平滚动条高度,若存在) | 是 | 是 | 否 | 只读 |
element.clientWidth |
元素内容 + 内边距的宽度(不含边框、滚动条,仅内容区 + 内边距) | 是 | 否 | 否 | 只读 |
element.clientHeight |
元素内容 + 内边距的高度(不含边框、滚动条,仅内容区 + 内边距) | 是 | 否 | 否 | 只读 |
element.scrollWidth |
元素实际内容的宽度(含超出可视区域的内容,不含边框、滚动条) | 是 | 否 | 否 | 只读 |
element.scrollHeight |
元素实际内容的高度(含超出可视区域的内容,不含边框、滚动条) | 是 | 否 | 否 | 只读 |
应用场景示例
- 获取元素占位宽高(如布局计算):用
offsetWidth/offsetHeight(包含边框,反映元素实际占据的空间)。 - 获取元素可视区域宽高(如判断内容是否溢出):用
clientWidth/clientHeight(不含边框,反映元素内部可显示内容的区域)。 - 获取元素实际内容宽高(如滚动到底部判断):用
scrollWidth/scrollHeight(含溢出内容,反映内容的真实尺寸)。
2. 核心位置属性:获取元素坐标
元素的 “位置” 分为相对父元素的坐标和相对文档的坐标,核心属性为 offset 系列和 getBoundingClientRect() 方法。
(1)offset 系列:相对定位父元素的坐标
element.offsetLeft:元素左边框到其 “最近已定位父元素”(position为relative/absolute/fixed)左边框的水平距离。element.offsetTop:元素上边框到其 “最近已定位父元素”上边框的垂直距离。- 若元素没有已定位的父元素,则相对
body(文档根节点)计算。
示例:在放大镜案例中,计算鼠标相对小图容器的坐标:
javascript
运行
// showimg 是小图容器(position: relative)
const x = event.pageX - showimg.offsetLeft; // 鼠标X - 容器左边界到body的距离 → 相对容器的X坐标
const y = event.pageY - showimg.offsetTop; // 鼠标Y - 容器上边界到body的距离 → 相对容器的Y坐标
(2)getBoundingClientRect() 方法:相对视口的坐标
该方法返回一个对象,包含元素相对浏览器视口(可见区域)的坐标和尺寸,常用属性如下:
| 属性 | 含义 |
|---|---|
left |
元素左边框到视口左边界的水平距离(滚动后会变化) |
top |
元素上边框到视口上边界的垂直距离(滚动后会变化) |
right |
元素右边框到视口左边界的水平距离(等价于 left + width) |
bottom |
元素下边框到视口上边界的垂直距离(等价于 top + height) |
width |
元素的宽度(同 offsetWidth) |
height |
元素的高度(同 offsetHeight) |
应用场景:判断元素是否在视口中(如懒加载):
javascript
运行
function isInViewport(element) {
const rect = element.getBoundingClientRect();
// 元素顶部 <= 视口高度(元素底部进入视口)且 元素底部 >= 0(元素顶部进入视口)
return rect.top <= window.innerHeight && rect.bottom >= 0;
}
(3)相对文档的坐标(需结合滚动距离)
getBoundingClientRect() 的 left/top 会随页面滚动变化,若需获取相对文档左上角的固定坐标,需加上页面滚动距离:
javascript
运行
function getElementDocPosition(element) {
const rect = element.getBoundingClientRect();
// 文档坐标 = 视口坐标 + 页面滚动距离
const docLeft = rect.left + window.scrollX; // window.scrollX 是页面水平滚动距离
const docTop = rect.top + window.scrollY; // window.scrollY 是页面垂直滚动距离
return { docLeft, docTop };
}
3. 实战案例:放大镜效果解析
放大镜功能的核心是 “遮罩层跟随鼠标 + 大图同步移动”,依赖尺寸与位置属性的计算,关键逻辑如下:
(1)遮罩层边界控制
遮罩层(mask)不能超出小图容器(showimg),需通过尺寸属性判断边界:
javascript
运行
// 遮罩层可移动的最大X距离 = 小图容器宽度 - 遮罩层宽度
const maxMaskX = showimg.offsetWidth - mask.offsetWidth;
// 遮罩层可移动的最大Y距离 = 小图容器高度 - 遮罩层高度
const maxMaskY = showimg.offsetHeight - mask.offsetHeight;
// 计算遮罩层坐标(鼠标居中在遮罩层)
let maskX = event.pageX - showimg.offsetLeft - mask.offsetWidth / 2;
let maskY = event.pageY - showimg.offsetTop - mask.offsetHeight / 2;
// 边界限制:遮罩层不能超出小图容器
maskX = maskX < 0 ? 0 : (maskX > maxMaskX ? maxMaskX : maskX);
maskY = maskY < 0 ? 0 : (maskY > maxMaskY ? maxMaskY : maskY);
// 应用坐标
mask.style.left = maskX + 'px';
mask.style.top = maskY + 'px';
(2)大图同步移动(比例计算)
大图(bigs)的移动距离与遮罩层的移动距离成固定比例(比例 = 大图尺寸 / 小图尺寸),核心公式:
javascript
运行
// 大图可移动的最大距离 = 大图宽度 - 大图容器宽度
const maxBigX = bigs.offsetWidth - bigImg.offsetWidth;
const maxBigY = bigs.offsetHeight - bigImg.offsetHeight;
// 比例 = 遮罩层移动距离 / 遮罩层最大移动距离 = 大图移动距离 / 大图最大移动距离
const bigX = (maskX / maxMaskX) * maxBigX;
const bigY = (maskY / maxMaskY) * maxBigY;
// 大图反向移动(遮罩层向右,大图向左,故加负号)
bigs.style.left = -bigX + 'px';
bigs.style.top = -bigY + 'px';
八、DOM 元素的其他获取方式(document 特殊属性)
除了 getElementById、querySelector 等通用方法,document 对象还提供了一系列特殊属性,可快速获取文档中的特定元素或元素集合,简化开发。
1. 快速获取单个核心元素
这些属性直接返回文档中的关键节点(无需遍历或查找),效率极高:
| 属性 | 含义 | 示例 |
|---|---|---|
document.documentElement |
返回 HTML 文档的根元素 <html> |
document.documentElement.scrollTop → 获取页面垂直滚动距离 |
document.head |
返回文档的 <head> 元素 |
document.head.appendChild(style) → 向 head 插入样式表 |
document.body |
返回文档的 <body> 元素 |
document.body.style.backgroundColor = '#fff' → 设置页面背景 |
document.title |
返回或设置文档的标题(<title> 标签内容) |
document.title = '新标题' → 修改页面标题 |
2. 快速获取元素集合
这些属性返回文档中特定类型元素的集合(HTMLCollection 或 NodeList),支持通过索引或名称访问:
| 属性 | 含义 | 元素类型 | 集合类型 |
|---|---|---|---|
document.all |
返回文档中所有元素的集合(包含 <html>、<body> 等),兼容性较差 |
所有 HTML 元素 | HTMLCollection |
document.forms |
返回文档中所有表单(<form>)的集合 |
<form> 元素 |
HTMLCollection |
document.images |
返回文档中所有图片(<img>)的集合 |
<img> 元素 |
HTMLCollection |
document.links |
返回文档中所有超链接(带 href 属性的 <a>)的集合 |
<a href="..."> 元素 |
HTMLCollection |
document.anchors |
返回文档中所有锚点(带 name 属性的 <a>)的集合 |
<a name="..."> 元素 |
HTMLCollection |
集合遍历方式
由于返回的集合多为 HTMLCollection(类数组),需通过索引遍历,常用两种方式:
- 普通 for 循环(推荐,兼容性好):
javascript
运行
// 遍历所有图片,设置宽高 const imgs = document.images; for (let i = 0; i < imgs.length; i++) { imgs[i].style.width = '200px'; imgs[i].style.height = 'auto'; } - for...in 循环(需过滤非索引属性):
javascript
运行
const forms = document.forms; for (let key in forms) { // 仅处理数字索引(排除集合的内置属性,如 length) if (!isNaN(Number(key))) { console.log(forms[key]); // 输出单个表单元素 } }
九、HTTP 基础:状态码、端口号与 URL 构成
DOM 操作依赖浏览器与服务器的交互,了解 HTTP 基础概念(状态码、端口号、URL)是前端开发的必备知识,可帮助排查接口请求问题。
1. HTTP 状态码:请求结果标识
HTTP 状态码是服务器对客户端请求的 “响应状态标识”,由 3 位数字组成,分为 5 大类:
| 类别 | 含义 | 常见状态码及说明 |
|---|---|---|
| 1XX | 消息(请求已接收,需继续处理) | 100 Continue(客户端需继续发送请求体)→ 极少直接接触。 |
| 2XX | 成功(请求已正常处理) | 200 OK(请求成功,服务器返回数据)→ 最常见的成功状态码。 |
| 3XX | 重定向(请求需进一步操作) | 301 Moved Permanently(资源永久迁移,浏览器会缓存新地址);302 Found(资源临时迁移)。 |
| 4XX | 请求错误(客户端请求存在问题) | 404 Not Found(请求的资源不存在,如 URL 写错);403 Forbidden(服务器拒绝请求,如无权限)。 |
| 5XX | 服务器错误(服务器处理请求失败) | 500 Internal Server Error(服务器内部错误,如代码 bug);503 Service Unavailable(服务器暂时不可用)。 |
2. 常用端口号:服务访问入口
端口号是 “服务器内部服务的唯一标识”,用于区分同一服务器上的不同服务(如 HTTP 服务、数据库服务),常见端口号如下:
| 服务类型 | 默认端口号 | 说明 |
|---|---|---|
| HTTP 服务 | 80 | 浏览器访问网页的默认端口(URL 中可省略,如 http://baidu.com 等价于 http://baidu.com:80)。 |
| HTTPS 服务 | 443 | 加密的 HTTP 服务(安全访问),默认端口(如 https://baidu.com 等价于 https://baidu.com:443)。 |
| MySQL 数据库 | 3306 | 关系型数据库 MySQL 的默认端口,用于后端程序连接数据库。 |
| FTP 服务 | 21 | 文件传输协议(FTP)的默认端口,用于服务器与客户端的文件上传 / 下载。 |
3. URL 构成:资源定位地址
URL(Uniform Resource Locator,统一资源定位符)是 “互联网资源的唯一地址”,用于浏览器定位服务器上的资源,其标准结构如下:
示例 URL:https://www.baidu.com:443/index/index.html?page=1&num=10#top
| 组成部分 | 示例值 | 含义 |
|---|---|---|
| 协议(Protocol) | https |
客户端与服务器的通信规则(http 为明文,https 为加密,更安全)。 |
| 域名(Domain) | www.baidu.com |
服务器的 “网络地址”,通过 DNS 解析为服务器的 IP 地址(如 180.101.49.11)。 |
| 端口号(Port) | 443 |
服务器上的服务入口(https 默认 443,可省略;非默认端口需显式写,如 :8080)。 |
| 路径(Path) | /index/index.html |
资源在服务器上的 “文件路径”(定位具体的文件或接口)。 |
| 查询参数(Query) | page=1&num=10 |
客户端向服务器传递的 “动态参数”,以 ? 开头,多个参数用 & 分隔(如分页参数 page=1 表示第 1 页)。 |
| 锚点(Fragment) | #top |
页面内的 “定位标记”,用于跳转到页面的指定位置(如 #top 跳转到页面顶部),不会发送到服务器。 |
更多推荐



所有评论(0)