一、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.getElementByIdelement.style
DOM 事件模型 定义 DOM 元素可响应的事件(如onclickonloadonchange),及事件处理机制
(扩展)DOM 样式 专门用于操作元素样式的接口(如element.classListelement.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 个属性,用于获取节点的基本信息(nodeNamenodeType只读):

节点类型 nodeName(节点名称) nodeValue(节点值) nodeType(节点类型)
文档节点 #document null 9
元素节点 标签名(大写,如DIVA null 1
属性节点 属性名(如idhref 属性值(如boxhttps://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 原生属性(如idhrefsrc 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 获取 / 设置表单元素的值 仅适用于表单元素(如inputtextareaselect 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) 克隆当前节点 deeptrue时:克隆节点及所有后代节点deepfalse时:仅克隆节点本身(无后代) 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 特殊属性)

除了 getElementByIdquerySelector 等通用方法,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(类数组),需通过索引遍历,常用两种方式:

  1. 普通 for 循环(推荐,兼容性好):

    javascript

    运行

    // 遍历所有图片,设置宽高
    const imgs = document.images;
    for (let i = 0; i < imgs.length; i++) {
      imgs[i].style.width = '200px';
      imgs[i].style.height = 'auto';
    }
    
  2. 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,统一资源定位符)是 “互联网资源的唯一地址”,用于浏览器定位服务器上的资源,其标准结构如下:

示例 URLhttps://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 跳转到页面顶部),不会发送到服务器。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐