DAY_13DOM操作完全指南DOM基础API与节点操作(上)

JavaScript DOM 操作完全指南(上篇):基础 API、尺寸位置与节点操作

本篇聚焦 DOM 的基础知识体系,系统讲解节点类型、元素获取、内容读写、尺寸位置、滚动控制、节点增删改查和 document 对象,并通过全选、半选、倒计时等案例完成基础能力闭环。


前言

DOM 是 JavaScript 操作页面结构、内容与样式的核心接口。上篇的目标不是罗列 API,而是把常用 DOM 能力按真实开发路径串起来:先理解节点和元素,再掌握内容、尺寸、位置、滚动、节点操作,最后用典型案例完成实战落地。

阅读完本篇,你将能够独立完成页面元素选择、内容更新、尺寸位置计算、滚动控制、节点动态创建,以及常见表单交互和倒计时功能。

目录

  • 核心名词解释
  • [1. DOM 概述](#1. DOM 概述)
    • [1.1 什么是 DOM](#1.1 什么是 DOM)
    • [1.2 DOM 节点类型](#1.2 DOM 节点类型)
    • [1.3 获取元素的方式](#1.3 获取元素的方式)
    • [1.4 DOM 树关系属性](#1.4 DOM 树关系属性)
    • [1.5 属性与样式操作](#1.5 属性与样式操作)
  • [2. 读写元素内容](#2. 读写元素内容)
    • [2.1 核心属性对比](#2.1 核心属性对比)
    • [2.2 innerHTML - 读写内部 HTML](#2.2 innerHTML - 读写内部 HTML)
    • [2.3 outerHTML - 读写包含自身的 HTML](#2.3 outerHTML - 读写包含自身的 HTML)
    • [2.4 innerText - 渲染后的文本内容](#2.4 innerText - 渲染后的文本内容)
    • [2.5 textContent - 所有文本内容](#2.5 textContent - 所有文本内容)
    • [2.6 内容属性选择决策图](#2.6 内容属性选择决策图)
    • [2.7 实际应用场景](#2.7 实际应用场景)
  • [3. 获取元素尺寸](#3. 获取元素尺寸)
    • [3.1 尺寸属性全景图](#3.1 尺寸属性全景图)
    • [3.2 offsetWidth / offsetHeight](#3.2 offsetWidth / offsetHeight)
    • [3.3 clientWidth / clientHeight](#3.3 clientWidth / clientHeight)
    • [3.4 scrollWidth / scrollHeight](#3.4 scrollWidth / scrollHeight)
    • [3.5 尺寸属性对比表](#3.5 尺寸属性对比表)
    • [3.6 getBoundingClientRect() - 获取完整尺寸和位置](#3.6 getBoundingClientRect() - 获取完整尺寸和位置)
    • [3.7 视口尺寸获取](#3.7 视口尺寸获取)
  • [4. 获取元素位置](#4. 获取元素位置)
    • [4.1 位置属性概览](#4.1 位置属性概览)
    • [4.2 offsetLeft / offsetTop](#4.2 offsetLeft / offsetTop)
    • [4.3 clientLeft / clientTop](#4.3 clientLeft / clientTop)
    • [4.4 位置属性关系图](#4.4 位置属性关系图)
    • [4.5 获取元素在页面中的绝对位置](#4.5 获取元素在页面中的绝对位置)
  • [5. 滚动位置控制](#5. 滚动位置控制)
    • [5.1 滚动属性](#5.1 滚动属性)
    • [5.2 基础滚动控制](#5.2 基础滚动控制)
    • [5.3 页面滚动控制](#5.3 页面滚动控制)
    • [5.4 检测滚动到底部](#5.4 检测滚动到底部)
  • [6. 节点增删改查](#6. 节点增删改查)
    • [6.1 创建节点](#6.1 创建节点)
    • [6.2 添加节点](#6.2 添加节点)
    • [6.3 删除节点](#6.3 删除节点)
    • [6.4 替换节点](#6.4 替换节点)
    • [6.5 克隆节点](#6.5 克隆节点)
    • [6.6 节点操作关系图](#6.6 节点操作关系图)
  • [7. document 对象详解](#7. document 对象详解)
    • [7.1 document 核心属性](#7.1 document 核心属性)
    • [7.2 document 核心方法](#7.2 document 核心方法)
    • [7.3 实战示例:标题滚动效果](#7.3 实战示例:标题滚动效果)
  • [8. 实战案例](#8. 实战案例)
    • [8.1 案例一(基础):三按钮全选/取消全选/反选](#8.1 案例一(基础):三按钮全选/取消全选/反选)
    • [8.2 案例二(增强):父级全选与半选状态](#8.2 案例二(增强):父级全选与半选状态)
    • [8.3 案例三:目标日期倒计时](#8.3 案例三:目标日期倒计时)

核心名词解释

名词 英文 解析
DOM Document Object Model 将 HTML/XML 文档抽象为可编程的对象树,脚本可通过 API 读写结构、内容与样式。
节点 Node DOM 树中的基本单位,包括元素、文本、注释、属性等类型。
元素节点 Element 对应 HTML 标签的节点,如 <div> 在 DOM 中即元素节点。
文档节点 Document 整份页面的根,document 即其 JavaScript 入口。
回流 Reflow 布局相关属性变化后,浏览器重新计算几何信息并绘制,频繁触发影响性能。
重绘 Repaint 仅视觉样式变化、不影响布局时的绘制,开销通常小于回流。
视口 Viewport 浏览器可见区域;getBoundingClientRect() 返回的坐标均相对视口。
定位祖先 Offset Parent offsetLeft/Top 参照的第一个 positionstatic 的祖先,若无则参照文档。
事件委托 Event Delegation 在父元素统一监听子元素事件,利用冒泡减少监听器数量。
懒加载 Lazy Loading 图片等资源进入可视区域后再赋值 src,降低首屏请求与流量。
XSS Cross-Site Scripting 将不可信字符串写入 innerHTML 可能执行恶意脚本,需用 textContent 或消毒。
HTMLCollection --- getElementsByTagName 等返回的动态集合,DOM 变化时自动更新。
NodeList --- querySelectorAll 返回的静态快照,后续 DOM 变化不会自动反映。
DocumentFragment --- 轻量文档片段,批量插入子节点时减少回流次数。
indeterminate --- 复选框的「半选」视觉状态,表示子项部分选中,需配合 JS 维护。

1. DOM 概述

1.1 什么是 DOM

DOM(Document Object Model,文档对象模型)是 HTML 和 XML 文档的编程接口。它将文档表示为一个树形结构,其中每个节点都是文档的一部分,允许程序和脚本动态地访问和更新文档的内容、结构和样式。
Document
html
head
body
meta
title
div
ul
li
li

1.2 DOM 节点类型

节点类型 说明 常见示例
Document 文档根节点 document
Element 元素节点 <div>, <p>, <span>
Attribute 属性节点 class, id, src
Text 文本节点 元素内的文本内容
Comment 注释节点 <!-- 注释 -->

1.3 获取元素的方式

javascript 复制代码
// 1. 通过 ID 获取
const elem1 = document.getElementById('myId');

// 2. 通过标签名获取
const elem2 = document.getElementsByTagName('div');

// 3. 通过类名获取
const elem3 = document.getElementsByClassName('myClass');

// 4. 通过 name 属性获取
const elem4 = document.getElementsByName('username');

// 5. 使用 CSS 选择器(推荐)
const elem5 = document.querySelector('#myId');
const elem6 = document.querySelectorAll('.myClass');

// 6. 快捷方法
const html = document.documentElement; // html 元素
const body = document.body;            // body 元素
const head = document.head;            // head 元素

代码解释

  • getElementById(): 通过元素的唯一 ID 获取单个元素,效率最高
  • getElementsByTagName(): 通过标签名获取元素集合(HTMLCollection),返回动态集合
  • getElementsByClassName(): 通过类名获取元素集合
  • getElementsByName(): 通过 name 属性获取元素集合,常用于表单元素
  • querySelector(): 使用 CSS 选择器获取第一个匹配的元素,支持复杂选择器
  • querySelectorAll(): 使用 CSS 选择器获取所有匹配的元素,返回静态 NodeList
  • documentElement/body/head: 快捷访问页面根元素的属性,省去选择器操作

1.4 DOM 树关系属性

除「查询选择器」外,还可通过节点关系在已获取的元素上继续查找,适合列表、菜单、表格等层级结构。

属性 说明
children 元素节点集合(不含文本节点)
childElementCount 子元素个数
firstElementChild / lastElementChild 第一个 / 最后一个子元素
parentElement 父元素节点
previousElementSibling / nextElementSibling 前一个 / 后一个同级元素

parentElement
previousElementSibling
nextElementSibling
ul#list
li 第一项
li 第二项
li 第三项

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>DOM 树关系示例</title>
</head>
<body>
    <ul id="list">
        <li>任务 A</li>
        <li id="current">任务 B(当前)</li>
        <li>任务 C</li>
    </ul>
    <button id="prevBtn">上一项</button>
    <button id="nextBtn">下一项</button>
    <p id="info"></p>
    <script>
        const current = document.querySelector('#current');
        const info = document.querySelector('#info');

        document.querySelector('#prevBtn').onclick = function () {
            const prev = current.previousElementSibling;
            if (prev) {
                info.textContent = '上一项:' + prev.textContent;
            }
        };

        document.querySelector('#nextBtn').onclick = function () {
            const next = current.nextElementSibling;
            if (next) {
                info.textContent = '下一项:' + next.textContent;
            }
        };

        // 父元素与子元素数量
        info.textContent = '父元素标签:' + current.parentElement.tagName
            + ',子元素数:' + current.parentElement.childElementCount;
    </script>
</body>
</html>

代码解释previousElementSibling / nextElementSibling 只匹配元素节点 ,会跳过文本节点;firstElementChild 常用于 insertBefore(newNode, parent.firstElementChild) 在列表头部插入。

经典场景:评论回复链定位、表格行高亮切换、轮播图上一张/下一张、面包屑导航回溯。

1.5 属性与样式操作

DOM 不仅能改内容,还能读写属性样式,常与动态 UI 配合使用。

1.5.1 内置属性与自定义属性
javascript 复制代码
// 读写内置属性(属性名与 DOM 属性一致)
img.src = 'images/db01.jpg';
input.value = 'hello';
link.href = 'https://example.com';

// 通用 attribute API
el.setAttribute('data-id', '1001');
el.getAttribute('data-id'); // '1001'

// data-* 自定义属性 → dataset(驼峰)
// HTML: <div data-user-id="8">
el.dataset.userId = '8';
console.log(el.dataset.userId);
1.5.2 行内样式与计算样式
javascript 复制代码
box.style.width = '200px';
box.style.backgroundColor = 'tomato'; // 驼峰写法

// 读取最终生效样式(只读)
const styles = getComputedStyle(box);
console.log(styles.width);
1.5.3 className 与 classList
javascript 复制代码
// 覆盖式,易误删其他类名
el.className = 'active';

// 推荐:逐项增删
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('expanded'); // 有则删、无则加
el.classList.contains('active');  // true / false



修改外观
改单条样式?
style.xxx
classList
add / remove / toggle
需精确像素/颜色时

网站应用 :京东商品卡片 hover 高亮(classList)、GitHub 暗色模式切换(dataset + classList)、Ant Design 表单校验状态(setAttribute('aria-invalid'))。

1.5.4 本文高频 CSS 样式速查

DOM 案例中的 CSS 不只是装饰,它经常承担「布局」「状态反馈」「交互动效」三类职责。下面列出本文示例高频样式的作用、最小例子和常见网站场景。

CSS 样式 作用 最小例子 常见网站场景
display: flex 一维布局,让子元素横向或纵向排列 .toolbar { display: flex; gap: 12px; } 电商筛选栏、后台操作按钮组
flex-wrap: wrap 子元素空间不足时自动换行 .gallery { display: flex; flex-wrap: wrap; } 图片墙、商品列表
gap 控制 flex/grid 子项间距 .list { display: flex; gap: 16px; } 卡片列表、导航菜单
position: sticky 滚动到指定位置后吸顶 .header { position: sticky; top: 0; } 文档目录、商品详情页顶部栏
position: fixed 固定在视口某处 .badge { position: fixed; right: 20px; bottom: 20px; } 返回顶部按钮、加载提示
overflow: hidden 隐藏溢出内容 .marquee { overflow: hidden; } 无缝滚动、轮播容器
overflow: auto 内容超出时出现滚动条 .panel { height: 300px; overflow: auto; } 聊天记录、侧边列表
box-shadow 添加阴影,形成层次 .card { box-shadow: 0 4px 16px rgba(0,0,0,.12); } 商品卡片、弹窗、仪表盘
border-radius 设置圆角 .btn { border-radius: 8px; } 按钮、头像、卡片
transition 属性变化时平滑过渡 .btn { transition: background .3s; } hover 效果、状态切换
transform 位移、缩放、旋转 .item:hover { transform: translateY(-2px); } 卡片悬浮、轮播动画
animation 绑定关键帧动画 .track { animation: scroll 20s linear infinite; } 公告滚动、加载动画
object-fit: cover 图片裁剪填满容器 img { width: 100%; height: 180px; object-fit: cover; } 商品图、头像封面、图库
linear-gradient 线性渐变背景 .banner { background: linear-gradient(135deg, #667eea, #764ba2); } 活动横幅、按钮背景
z-index 控制定位元素层级 .header { position: sticky; z-index: 100; } 吸顶导航、浮层、遮罩

归纳 :布局类样式优先决定结构(displayflex-wrapgap),状态类样式表达用户反馈(hovertransitionbox-shadow),动效类样式用于连续运动(animationtransform)。DOM 操作通常通过 classList 切换这些样式,而不是频繁直接写入多个 style 属性。


2. 读写元素内容

2.1 核心属性对比

属性 读写 返回内容 解析HTML 受CSS影响 标准状态
innerHTML 内部HTML+文本 标准属性
outerHTML 包含自身的HTML 标准属性
innerText 渲染后的文本 HTML 标准,历史兼容性复杂
textContent 所有文本内容 标准属性

2.2 innerHTML - 读写内部 HTML

功能:获取或设置元素内部的 HTML 内容(从起始标签到结束标签之间的内容)。

特点

  • 包含所有子元素和 HTML 标签
  • 赋值时会解析 HTML 字符串
  • 符合现代 DOM 标准
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>innerHTML 示例</title>
    <style>
        .wrapper {
            width: 600px;
            padding: 20px;
            border: 2px dashed #999;
        }
        .item {
            padding: 20px;
            background: #ccc;
        }
    </style>
</head>
<body>
    <button id="getBtn">读取内容</button>
    <button id="setBtn">设置内容</button>
    <br><br>

    <div id="box" class="wrapper">
        <p>原始内容</p>
        <ul>
            <li>列表项 1</li>
            <li>列表项 2</li>
        </ul>
    </div>

    <script>
        const box = document.querySelector('#box');
        const getBtn = document.querySelector('#getBtn');
        const setBtn = document.querySelector('#setBtn');

        // 读取内容
        getBtn.onclick = function() {
            console.log(box.innerHTML);
            // 输出: <p>原始内容</p><ul><li>列表项 1</li><li>列表项 2</li></ul>
        };

        // 设置内容 - 会解析 HTML 标签
        setBtn.onclick = function() {
            box.innerHTML = '<h1 class="item">Hello World</h1><p>新段落</p>';
        };
    </script>
</body>
</html>

代码解释

  • CSS 部分 :定义了容器样式 .wrapper(虚线边框、内边距)和内容项样式 .item(灰色背景)
  • HTML 部分:创建了两个按钮和一个包含列表的容器元素
  • JavaScript 部分
    • 使用 querySelector 获取元素引用
    • getBtn.onclick: 点击"读取内容"按钮时,innerHTML 返回元素内部的所有 HTML 代码(包括标签)
    • setBtn.onclick: 点击"设置内容"按钮时,innerHTML 会解析传入的 HTML 字符串,将 <h1><p> 标签渲染为实际的 HTML 元素
  • 关键点innerHTML 既能读取也能写入,写入时会解析 HTML 标签,这是它与 textContent 的核心区别

⚠️ 安全警告 :使用 innerHTML 插入用户输入的内容存在 XSS 攻击风险。

javascript 复制代码
// 危险示例
const userInput = '<img src="x" onerror="alert(1)">';
element.innerHTML = userInput; // 会执行恶意代码

// 安全替代方案
element.textContent = userInput; // 不会解析 HTML

代码解释

  • 危险示例 :当用户输入包含恶意代码(如 onerror 事件处理器)时,innerHTML 会解析并执行这段代码,导致 XSS 攻击
  • 安全方案 :使用 textContent 代替 innerHTML,它会将内容作为纯文本处理,不会解析 HTML 标签
  • 最佳实践 :处理用户输入时,优先使用 textContent;如果必须使用 innerHTML,先对输入进行消毒(sanitize)处理

2.3 outerHTML - 读写包含自身的 HTML

功能:获取或设置包含元素自身的完整 HTML。

特点

  • 包含元素自身的起始和结束标签
  • 赋值时会替换整个元素
  • 符合现代 DOM 标准
html 复制代码
<div id="container">
    <div id="box">原始内容</div>
</div>

<script>
    const box = document.querySelector('#box');

    // 读取 outerHTML
    console.log(box.outerHTML);
    // 输出: <div id="box">原始内容</div>

    // 设置 outerHTML - 会替换整个元素
    box.outerHTML = '<span id="box">新内容</span>';

    // 此时 box 变量指向的元素已被移除
    // 需要重新获取
    const newBox = document.querySelector('#box');
    console.log(newBox.tagName); // SPAN
</script>

代码解释

  • outerHTML 读取:返回包含元素自身在内的完整 HTML,包括起始标签、内容和结束标签
  • outerHTML 写入 :与 innerHTML 不同,设置 outerHTML替换整个元素,而不是只修改内部内容
  • 变量引用失效 :执行 outerHTML 赋值后,原 box 变量仍指向被移除的元素,需要重新查询获取新元素
  • 实际应用 :适合需要完全替换某个元素(包括其标签类型)的场景,如将 <div> 替换为 <span>

2.4 innerText - 渲染后的文本内容

功能:获取元素渲染后的可见文本内容。

特点

  • 受 CSS 样式影响display: none 的元素不会返回)
  • 触发回流,性能较差
  • 只能 HTMLElement 调用
  • 兼容性复杂:早期并非 DOM 标准属性,现代浏览器已广泛支持,使用时仍要理解它会受 CSS 与布局影响
html 复制代码
<div id="box">
    Hello <span style="display:none">Hidden</span>
    <style>.ignore { display: none; }</style>
    <span class="ignore">Ignored</span>
</div>

<script>
    const box = document.querySelector('#box');
    console.log(box.innerText);
    // 输出: "Hello "(不包含 Hidden 和 Ignored)
</script>

代码解释

  • 受 CSS 影响innerText 会考虑 CSS 样式,display: none 的元素内容不会被返回
  • 触发回流:浏览器需要计算元素的渲染结果才能确定可见文本,这会触发页面回流(reflow)
  • 与 textContent 的区别textContent 会返回所有文本内容,包括隐藏元素的文本
  • 使用场景:需要获取用户实际能看到的文本内容时使用

2.5 textContent - 所有文本内容

功能:获取节点及其后代的纯文本内容。

特点

  • 不受 CSS 影响
  • 包括隐藏元素的文本
  • 任意 Node 节点都可调用
  • 标准属性
  • 性能更好(不触发回流)
html 复制代码
<div id="box">
    Hello <span style="display:none">Hidden</span>
</div>

<script>
    const box = document.querySelector('#box');
    console.log(box.textContent);
    // 输出: "Hello Hidden"(包含隐藏元素)

    // 设置纯文本(推荐用于用户输入)
    box.textContent = '<script>alert("XSS")<\/script>';
    // 不会被解析为 HTML,而是作为纯文本显示
</script>

代码解释

  • 不受 CSS 影响textContent 会返回所有文本,包括隐藏元素的文本
  • 标准属性 :属于现代 DOM 标准,性能优于 innerText(不触发回流)
  • 防 XSS :设置 textContent 时,HTML 标签会被转义为纯文本,不会执行脚本
  • 推荐使用 :处理用户输入、需要设置纯文本内容时,优先使用 textContent

2.6 内容属性选择决策图





需要
不需要

需要修改元素内容
是否需要插入HTML?
使用 innerHTML
内容是否来自用户输入?
使用 textContent
是否需要隐藏元素?
使用 innerText
使用 textContent
是否需要替换自身?
使用 outerHTML

2.7 实际应用场景

场景 推荐属性 原因
富文本编辑器 innerHTML 需要解析 HTML 标签
显示用户评论 textContent 防止 XSS 攻击
获取可见文本 innerText 自动排除隐藏元素
批量文本操作 textContent 性能更优

3. 获取元素尺寸

3.1 尺寸属性全景图

元素尺寸
offsetWidth/Height
clientWidth/Height
scrollWidth/Height
getBoundingClientRect
内容+padding+border
内容+padding
内容+padding+溢出
完整尺寸+位置信息

3.2 offsetWidth / offsetHeight

功能:获取元素的总宽度和总高度。

计算公式

复制代码
offsetWidth = content + padding + border
offsetHeight = content + padding + border
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>offset 尺寸示例</title>
    <style>
        .box {
            width: 300px;
            padding: 20px;
            border: 5px solid #333;
            margin: 10px;
        }
    </style>
</head>
<body>
    <div class="box" id="demo">内容</div>

    <script>
        const box = document.querySelector('#demo');
        console.log('offsetWidth:', box.offsetWidth);  // 350 = 300 + 20*2 + 5*2
        console.log('offsetHeight:', box.offsetHeight);
    </script>
</body>
</html>

代码解释

  • offsetWidth 计算公式:内容宽度(300) + 左右内边距(20×2) + 左右边框(5×2) = 350px
  • offsetHeight 计算公式:内容高度 + 上下内边距 + 上下边框
  • 注意:不包括外边距(margin),因为 margin 是元素外部的空间
  • 只读属性:offsetWidth/offsetHeight 是只读的,不能通过赋值来改变元素尺寸

3.3 clientWidth / clientHeight

功能:获取元素内容区域的宽度和高度。

计算公式

复制代码
clientWidth = content + padding
clientHeight = content + padding

注意:不包括边框(border)和滚动条。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>client 尺寸示例</title>
    <style>
        .box {
            width: 300px;
            padding: 20px;
            border: 5px solid #333;
        }
    </style>
</head>
<body>
    <div class="box" id="demo">内容</div>

    <script>
        const box = document.querySelector('#demo');
        console.log('clientWidth:', box.clientWidth);   // 340 = 300 + 20*2
        console.log('clientHeight:', box.clientHeight);
    </script>
</body>
</html>

代码解释

  • clientWidth 计算公式:内容宽度(300) + 左右内边距(20×2) = 340px
  • 不包括边框:与 offsetWidth 不同,clientWidth 不包含边框宽度
  • 实际意义:表示元素内容区域(可绘制内容的区域)的宽度
  • 应用场景:计算元素实际可用空间、检测内容是否溢出等

3.4 scrollWidth / scrollHeight

功能:获取元素完整的滚动宽度和高度。

计算公式

复制代码
scrollWidth = content + padding + 溢出部分
scrollHeight = content + padding + 溢出部分

应用场景:检测内容是否溢出、实现自定义滚动条等。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>scroll 尺寸示例</title>
    <style>
        .box {
            width: 300px;
            height: 100px;
            padding: 20px;
            border: 2px solid #333;
            overflow: auto;
        }
        .content {
            width: 500px;
            height: 300px;
            background: #f0f0f0;
        }
    </style>
</head>
<body>
    <div class="box" id="demo">
        <div class="content">超长内容</div>
    </div>

    <script>
        const box = document.querySelector('#demo');
        console.log('clientWidth:', box.clientWidth);    // 340
        console.log('scrollWidth:', box.scrollWidth);    // 540 (包含溢出)
        console.log('clientHeight:', box.clientHeight);  // 140
        console.log('scrollHeight:', box.scrollHeight);  // 340 (包含溢出)
    </script>
</body>
</html>

代码解释

  • clientWidth:300(内容宽) + 20×2(左右内边距) = 340px,表示可见区域宽度
  • scrollWidth:340(clientWidth) + 200(溢出部分) = 540px,表示完整滚动宽度
  • scrollHeight:140(clientHeight) + 200(溢出部分) = 340px,表示完整滚动高度
  • 关键区别:scrollWidth/scrollHeight 包含因内容溢出而不可见的部分
  • 应用场景
    • 判断内容是否溢出(scrollWidth > clientWidth)
    • 计算滚动条位置
    • 实现"滚动到底部"功能
    • 检测是否还有更多内容可滚动

3.5 尺寸属性对比表

属性 内容 padding border 溢出部分 滚动条
offsetWidth
clientWidth
scrollWidth

3.6 getBoundingClientRect() - 获取完整尺寸和位置

功能:返回一个 DOMRect 对象,包含元素的大小及其相对于视口的位置。

javascript 复制代码
const rect = element.getBoundingClientRect();

// 返回对象包含以下属性
console.log(rect.width);   // 元素宽度(同 offsetWidth)
console.log(rect.height);  // 元素高度(同 offsetHeight)
console.log(rect.top);     // 元素顶部到视口顶部的距离
console.log(rect.left);    // 元素左侧到视口左侧的距离
console.log(rect.right);   // 元素右侧到视口左侧的距离
console.log(rect.bottom);  // 元素底部到视口顶部的距离
console.log(rect.x);       // 同 left
console.log(rect.y);       // 同 top

代码解释

  • getBoundingClientRect():返回一个 DOMRect 对象,包含元素的尺寸和位置信息
  • 相对于视口:所有位置属性都是相对于浏览器视口(viewport)的,不是相对于页面
  • width/height:元素的完整宽高,与 offsetWidth/offsetHeight 相同
  • left/top:元素左上角相对于视口左上角的坐标
  • right/bottom:元素右下角相对于视口左上角的坐标
  • 滚动影响:页面滚动时,这些值会实时变化
  • 常用场景:检测元素是否在视口中可见、实现懒加载、计算元素位置用于定位其他元素

实际应用示例

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>getBoundingClientRect 示例</title>
    <style>
        .box {
            width: 200px;
            height: 150px;
            padding: 20px;
            border: 5px solid #333;
            margin: 50px;
            background: #f0f0f0;
        }
    </style>
</head>
<body>
    <div class="box" id="demo">内容</div>

    <script>
        const box = document.querySelector('#demo');
        const rect = box.getBoundingClientRect();

        console.log('尺寸信息:');
        console.log('  宽度:', rect.width, 'px');
        console.log('  高度:', rect.height, 'px');

        console.log('位置信息(相对于视口):');
        console.log('  左侧:', rect.left, 'px');
        console.log('  顶部:', rect.top, 'px');
        console.log('  右侧:', rect.right, 'px');
        console.log('  底部:', rect.bottom, 'px');

        // 判断元素是否在视口中可见
        const isVisible = (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= window.innerHeight &&
            rect.right <= window.innerWidth
        );
        console.log('元素完全可见:', isVisible);
    </script>
</body>
</html>

代码解释

  • 元素尺寸:width 和 height 包含 content + padding + border
  • 位置信息:left/top 是元素相对于视口左上角的坐标
  • right/bottom 计算:right = left + width,bottom = top + height
  • 可见性检测:通过比较元素边界与视口大小来判断元素是否完全可见
  • 实际应用:懒加载(元素进入视口时加载)、吸顶效果(元素离开视口时固定)、动画触发(元素进入视口时播放)

3.7 视口尺寸获取

javascript 复制代码
// 方法一:包含滚动条
const viewportWidth1 = window.innerWidth;
const viewportHeight1 = window.innerHeight;

// 方法二:不包含滚动条
const viewportWidth2 = document.documentElement.clientWidth;
const viewportHeight2 = document.documentElement.clientHeight;

console.log('视口尺寸:', viewportWidth1, 'x', viewportHeight1);

代码解释

  • window.innerWidth/innerHeight:获取浏览器窗口的内部宽高,包含滚动条的宽度
  • documentElement.clientWidth/clientHeight:获取文档根元素的宽高,不包含滚动条
  • 选择建议
    • 需要精确计算可用空间时使用 clientWidth/clientHeight(不包含滚动条)
    • 需要获取窗口总尺寸时使用 innerWidth/innerHeight(包含滚动条)
  • 响应式设计:常用于检测屏幕宽度、实现响应式布局、计算弹窗位置等

4. 获取元素位置

4.1 位置属性概览

属性 只读 参照物 说明
offsetLeft 定位祖先元素 到第一个定位祖先的左侧距离
offsetTop 定位祖先元素 到第一个定位祖先的顶部距离
clientLeft 元素自身 左边框宽度
clientTop 元素自身 上边框宽度

4.2 offsetLeft / offsetTop

功能:获取元素相对于第一个定位祖先元素的位置。

注意:如果没有定位的祖先元素,则相对于页面(document)。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>offset 位置示例</title>
    <style>
        .parent {
            position: relative;
            width: 400px;
            height: 300px;
            padding: 20px;
            border: 2px dashed #999;
            margin: 50px;
        }
        .child {
            position: relative;
            width: 100px;
            height: 100px;
            padding: 20px;
            margin: 30px;
            border: 10px solid #099;
            background: #f0f0f0;
        }
    </style>
</head>
<body>
    <div class="parent" id="parent">
        <div class="child" id="child">子元素</div>
    </div>

    <script>
        const child = document.querySelector('#child');
        const parent = document.querySelector('#parent');

        // child 到 parent 的距离(不包含 parent 的 padding)
        console.log('offsetLeft:', child.offsetLeft);   // 70 (margin 30 + border 2 + padding 20 + margin 30)
        console.log('offsetTop:', child.offsetTop);     // 70
    </script>
</body>
</html>

代码解释

  • 定位参照物 :由于父元素设置了 position: relative,子元素的 offsetLeft/offsetTop 相对于父元素计算
  • 计算细节:offsetLeft = 子元素外边距(30) + 父元素边框(2) + 父元素内边距(20) + 子元素外边距(30)
  • 定位祖先offsetParent 返回最近的定位祖先元素(position 不为 static)
  • 无定位祖先:如果所有祖先元素都没有定位,则相对于 document(页面)
  • 实际应用:计算元素相对位置、实现拖拽效果、元素碰撞检测等

4.3 clientLeft / clientTop

功能:获取元素左边框和上边框的宽度。

html 复制代码
<div style="border-left: 10px solid red; border-top: 5px solid blue;">
    内容
</div>

<script>
    const div = document.querySelector('div');
    console.log('左边框宽度:', div.clientLeft);   // 10
    console.log('上边框宽度:', div.clientTop);    // 5
</script>

代码解释

  • clientLeft:返回元素的左边框宽度,本例中为 10px
  • clientTop:返回元素的上边框宽度,本例中为 5px
  • 实际意义:通常用于计算元素内容区域的精确位置
  • 注意事项:如果滚动条在左侧(某些从右到左的语言环境),clientLeft 还会包含滚动条宽度
  • 应用场景:精确定位、边框计算、元素位置调整等

4.4 位置属性关系图

页面
定位祖先元素
父元素 padding-left
子元素 margin-left
offsetLeft 起点
子元素 border-left
clientLeft 值

4.5 获取元素在页面中的绝对位置

javascript 复制代码
function getAbsolutePosition(element) {
    let x = 0;
    let y = 0;

    while (element) {
        x += element.offsetLeft;
        y += element.offsetTop;
        element = element.offsetParent;
    }

    return { x, y };
}

// 使用 getBoundingClientRect 的现代方法
function getPagePosition(element) {
    const rect = element.getBoundingClientRect();
    return {
        x: rect.left + window.scrollX,
        y: rect.top + window.scrollY
    };
}

代码解释

  • 传统方法(getAbsolutePosition)

    • 通过循环遍历元素的 offsetParent 链
    • 累加每个 offsetLeft 和 offsetTop
    • 最终得到元素相对于页面的绝对位置
    • 缺点:需要遍历 DOM 树,性能相对较低
  • 现代方法(getPagePosition)

    • 使用 getBoundingClientRect() 获取元素相对于视口的位置
    • 加上页面的滚动距离(scrollX/scrollY)
    • 得到元素相对于页面的绝对位置
    • 优点:代码简洁,性能更好
  • 应用场景:元素绝对定位、拖拽效果保存、滚动后恢复位置等


5. 滚动位置控制

5.1 滚动属性

属性 读写 说明
scrollLeft 内容向左滚动的距离
scrollTop 内容向上滚动的距离

5.2 基础滚动控制

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>滚动控制示例</title>
    <style>
        .container {
            width: 300px;
            height: 200px;
            padding: 20px;
            border: 2px solid #999;
            overflow: hidden;
        }
        .content {
            width: 800px;
            padding: 20px;
            background: #f0f0f0;
        }
        .controls {
            position: fixed;
            right: 10px;
            bottom: 20px;
        }
        .controls button {
            display: block;
            margin: 5px;
            padding: 10px 20px;
        }
    </style>
</head>
<body>
    <div class="container" id="box">
        <div class="content">
            <p>长内容区域...</p>
            <p>可以滚动的内容...</p>
        </div>
    </div>

    <div class="controls">
        <button id="leftBtn">← 左移</button>
        <button id="rightBtn">→ 右移</button>
        <button id="upBtn">↑ 上移</button>
        <button id="downBtn">↓ 下移</button>
    </div>

    <script>
        const box = document.querySelector('#box');

        // 监听滚动事件
        box.addEventListener('scroll', function() {
            console.log('滚动位置:', box.scrollLeft, box.scrollTop);
        });

        // 控制按钮
        document.querySelector('#leftBtn').onclick = () => {
            box.scrollLeft += 20;
        };
        document.querySelector('#rightBtn').onclick = () => {
            box.scrollLeft -= 20;
        };
        document.querySelector('#upBtn').onclick = () => {
            box.scrollTop += 20;
        };
        document.querySelector('#downBtn').onclick = () => {
            box.scrollTop -= 20;
        };
    </script>
</body>
</html>

代码解释

  • scrollLeft:内容向左滚动的距离(正值表示内容向左移动,显示右侧内容)
  • scrollTop:内容向上滚动的距离(正值表示内容向上移动,显示下方内容)
  • 滚动监听 :通过 scroll 事件实时获取滚动位置
  • 按钮控制:点击按钮修改 scrollLeft/scrollTop 值,实现内容滚动
  • 应用场景:自定义滚动条、图片预览、幻灯片导航等

5.3 页面滚动控制

javascript 复制代码
// 获取页面滚动距离
const scrollX = window.scrollX || document.documentElement.scrollLeft;
const scrollY = window.scrollY || document.documentElement.scrollTop;

// 设置页面滚动位置
window.scrollTo({
    top: 0,
    left: 0,
    behavior: 'smooth' // 平滑滚动
});

// 相对滚动
window.scrollBy({
    top: 100,
    behavior: 'smooth'
});

// 滚动到指定元素
element.scrollIntoView({
    behavior: 'smooth',
    block: 'start' // 'start', 'center', 'end', 'nearest'
});

代码解释

  • 获取滚动距离scrollX/scrollY 是现代 API,兼容写法使用 documentElement.scrollLeft/scrollTop
  • scrollTo() :滚动到指定位置,behavior: 'smooth' 实现平滑滚动效果
  • scrollBy():相对当前滚动位置进行滚动,正值向下/向右滚动
  • scrollIntoView() :滚动页面使指定元素可见
    • block: 'start':元素顶部与视口顶部对齐
    • block: 'center':元素垂直居中
    • block: 'end':元素底部与视口底部对齐
  • 应用场景:返回顶部、页面导航、锚点跳转、表单验证滚动到错误位置等

5.4 检测滚动到底部

javascript 复制代码
function isScrollToBottom(element) {
    return element.scrollHeight - element.scrollTop === element.clientHeight;
}

// 页面滚动到底部检测
window.addEventListener('scroll', function() {
    const scrollTop = document.documentElement.scrollTop;
    const scrollHeight = document.documentElement.scrollHeight;
    const clientHeight = document.documentElement.clientHeight;

    if (scrollTop + clientHeight >= scrollHeight - 10) {
        console.log('已滚动到底部');
        // 触发加载更多内容
    }
});

代码解释

  • isScrollToBottom():判断元素是否滚动到底部

    • 公式:scrollHeight - scrollTop === clientHeight
    • 当已滚动距离 + 可见高度 ≥ 总高度时,表示已到底部
  • 页面滚动检测

    • scrollTop:已滚动的距离
    • scrollHeight:内容总高度
    • clientHeight:视口可见高度
    • - 10:留出 10px 的容差,提前触发加载
  • 应用场景

    • 无限滚动加载更多内容
    • 滚动到底部显示相关推荐
    • 滚动到一定位置显示"返回顶部"按钮
    • 懒加载触发时机判断

6. 节点增删改查

6.1 创建节点

javascript 复制代码
// 创建元素节点
const div = document.createElement('div');
const img = document.createElement('img');

// 创建文本节点
const text = document.createTextNode('文本内容');

// 创建图片快捷方式
const image1 = new Image();
const image2 = new Image(200);  // 指定宽度
const image3 = new Image(200, 150);  // 指定宽度和高度

代码解释

  • createElement():创建指定类型的元素节点,参数为标签名(如 'div'、'img')
  • createTextNode():创建文本节点,常用于添加纯文本内容
  • new Image() :创建图片元素的快捷方式,可选择性传入宽高参数
    • new Image():创建默认尺寸的图片
    • new Image(200):创建宽度为 200px 的图片
    • new Image(200, 150):创建宽度 200px、高度 150px 的图片
  • 注意:创建的节点需要通过 appendChild() 等方法添加到文档中才会显示

6.2 添加节点

方法 说明 返回值
appendChild(child) 在末尾添加子节点 添加的节点
insertBefore(new, ref) 在参考节点前插入 新节点
prepend() 在开头添加子节点 -
append() 在末尾添加子节点 -
after() 在元素后插入 -
before() 在元素前插入 -
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>节点操作示例</title>
    <style>
        .todolist {
            margin: 50px auto;
            width: 700px;
        }
        .todo-header input {
            width: 200px;
            padding: 10px;
            border: 1px solid #ccc;
        }
        .todo-header button {
            padding: 10px 20px;
            background-color: #f5f5f5;
            border: 1px solid #ccc;
            cursor: pointer;
        }
        .todo-header button:hover {
            background-color: #e0e0e0;
        }
        .todo-body ul {
            padding: 0;
            list-style: none;
        }
        .todo-body li {
            margin: 15px 0;
            padding: 10px;
            border: 1px solid #ccc;
            background: #fff;
        }
    </style>
</head>
<body>
    <div class="todolist">
        <div class="todo-header">
            <input type="text" id="input" placeholder="输入任务">
            <button id="addBtn">添加到末尾</button>
            <button id="insertBtn">插入到开头</button>
            <button id="deleteBtn">删除末尾</button>
            <button id="replaceBtn">替换末尾</button>
        </div>
        <div class="todo-body">
            <ul id="todoContent">
                <li>学习 JavaScript</li>
                <li>学习 DOM 操作</li>
                <li>练习项目实战</li>
            </ul>
        </div>
    </div>

    <script>
        const todoContent = document.querySelector('#todoContent');
        const inputBox = document.querySelector('#input');

        // 添加到末尾
        document.querySelector('#addBtn').onclick = function() {
            if (!inputBox.value.trim()) {
                alert('请输入任务内容');
                return;
            }

            const newLi = document.createElement('li');
            newLi.textContent = inputBox.value;
            todoContent.appendChild(newLi);

            inputBox.value = '';
        };

        // 插入到开头
        document.querySelector('#insertBtn').onclick = function() {
            if (!inputBox.value.trim()) {
                alert('请输入任务内容');
                return;
            }

            const newLi = document.createElement('li');
            newLi.textContent = inputBox.value;
            todoContent.insertBefore(newLi, todoContent.firstElementChild);

            inputBox.value = '';
        };

        // 删除末尾
        document.querySelector('#deleteBtn').onclick = function() {
            if (todoContent.lastElementChild) {
                todoContent.removeChild(todoContent.lastElementChild);
            }
        };

        // 替换末尾
        document.querySelector('#replaceBtn').onclick = function() {
            if (!inputBox.value.trim()) {
                alert('请输入任务内容');
                return;
            }

            if (todoContent.lastElementChild) {
                const newLi = document.createElement('li');
                newLi.textContent = inputBox.value;
                todoContent.replaceChild(newLi, todoContent.lastElementChild);

                inputBox.value = '';
            }
        };
    </script>
</body>
</html>

6.3 删除节点

javascript 复制代码
// 方法一:通过父节点删除
parent.removeChild(child);

// 方法二:直接删除(现代浏览器)
child.remove();

// 删除所有子节点
while (parent.firstChild) {
    parent.removeChild(parent.firstChild);
}

// 清空子节点(更高效)
parent.innerHTML = '';

代码解释

  • removeChild():传统方法,通过父节点删除指定子节点
  • remove():现代方法,元素可以直接调用自身删除,代码更简洁
  • 循环删除:使用 while 循环逐个删除子节点,直到没有子节点为止
  • innerHTML 清空:最快速的方法,但存在性能和安全问题
  • 推荐做法:根据场景选择,少量节点用 remove(),大量节点考虑批量操作

6.4 替换节点

javascript 复制代码
// replaceChild(newNode, oldNode)
const newLi = document.createElement('li');
newLi.textContent = '新任务';
todoContent.replaceChild(newLi, todoContent.lastElementChild);

// 现代方法
oldElement.replaceWith(newElement);

代码解释

  • replaceChild():传统方法,用新节点替换旧节点,参数顺序为(新节点,旧节点)
  • replaceWith():现代方法,旧节点调用自身来替换为新节点,代码更直观
  • DOM 变化:旧节点从文档树中移除,新节点占据相同位置
  • 应用场景:更新列表项、替换错误提示、动态内容刷新等

6.5 克隆节点

javascript 复制代码
// cloneNode(deep)
// deep: true - 深度克隆(包含所有后代)
// deep: false - 浅克隆(只克隆节点本身)

const original = document.querySelector('#original');
const deepClone = original.cloneNode(true);
const shallowClone = original.cloneNode(false);

代码解释

  • cloneNode(true):深度克隆,复制元素及其所有后代节点
  • cloneNode(false):浅克隆,只复制元素本身,不包含子节点
  • 克隆内容:复制元素的标签、属性、样式,但不复制事件监听器
  • 注意事项:克隆的节点需要手动添加到文档中,克隆的 id 重复可能导致问题
  • 应用场景:复制模板、批量创建相似元素、实现撤销/重做功能等
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>克隆节点示例</title>
    <style>
        .original {
            padding: 20px;
            background: #f0f0f0;
            margin: 10px;
        }
        .clone {
            padding: 20px;
            background: #e0e0e0;
            margin: 10px;
            border: 2px dashed #999;
        }
    </style>
</head>
<body>
    <div id="original" class="original">
        <h3>原始元素</h3>
        <p>这是原始内容</p>
    </div>

    <button id="cloneDeep">深度克隆</button>
    <button id="cloneShallow">浅克隆</button>

    <div id="container"></div>

    <script>
        const original = document.querySelector('#original');
        const container = document.querySelector('#container');

        // 深度克隆
        document.querySelector('#cloneDeep').onclick = function() {
            const clone = original.cloneNode(true);
            clone.classList.add('clone');
            clone.querySelector('h3').textContent = '深度克隆副本';
            container.appendChild(clone);
        };

        // 浅克隆
        document.querySelector('#cloneShallow').onclick = function() {
            const clone = original.cloneNode(false);
            clone.classList.add('clone');
            clone.innerHTML = '<h3>浅克隆副本</h3><p>只包含元素本身</p>';
            container.appendChild(clone);
        };
    </script>
</body>
</html>

6.6 节点操作关系图

添加
插入
替换
删除
克隆
document.createElement
创建新节点
操作类型
appendChild
insertBefore
replaceChild
removeChild
cloneNode
添加到父节点末尾
插入到指定位置
替换旧节点
从父节点移除
复制节点


7. document 对象详解

7.1 document 核心属性

javascript 复制代码
// 文档元素
document.documentElement  // html 元素
document.body            // body 元素
document.head            // head 元素
document.all             // 所有元素的集合

// 文档信息
document.title           // 读写页面标题
document.cookie          // 读写 cookie
document.URL             // 当前页面 URL
document.domain          // 页面域名
document.referrer        // 来源页面 URL

// 文档状态
document.readyState      // 'loading', 'interactive', 'complete'
document.compatMode      // 渲染模式

代码解释

  • 文档元素快捷访问

    • documentElement:返回 <html> 根元素
    • body:返回 <body> 元素
    • head:返回 <head> 元素
    • all:返回文档中所有元素的集合(非标准但广泛支持)
  • 文档信息

    • title:可读写,用于获取或修改浏览器标签页标题
    • cookie:用于读写 HTTP Cookie
    • URL:当前页面的完整地址
    • domain:当前页面的域名
    • referrer:链接到当前页面的来源页面 URL
  • 文档状态

    • readyState:文档加载状态,'loading'(加载中)、'interactive'(可交互)、'complete'(完成)
    • compatMode:渲染模式,'CSS1Compat'(标准模式)、'BackCompat'(怪异模式)

7.2 document 核心方法

javascript 复制代码
// 获取元素
document.getElementById(id)
document.getElementsByTagName(name)
document.getElementsByClassName(name)
document.getElementsByName(name)
document.querySelector(selectors)
document.querySelectorAll(selectors)

// 创建节点
document.createElement(tagName)
document.createTextNode(data)
document.createDocumentFragment()

// 文档写入(解析阶段)
document.write(markup)
document.writeln(markup)

代码解释

  • 元素获取方法

    • getElementById():通过 ID 获取单个元素,效率最高
    • getElementsByTagName():通过标签名获取元素集合
    • getElementsByClassName():通过类名获取元素集合
    • getElementsByName():通过 name 属性获取元素集合(常用于表单)
    • querySelector():使用 CSS 选择器获取第一个匹配元素
    • querySelectorAll():使用 CSS 选择器获取所有匹配元素
  • 节点创建方法

    • createElement():创建元素节点
    • createTextNode():创建文本节点
    • createDocumentFragment():创建文档片段(用于批量操作)
  • 文档写入方法

    • write():向文档写入内容
    • writeln():写入内容并添加换行符
    • 注意:这些方法只在文档解析阶段有效,页面加载完成后调用会清空整个文档

7.3 实战示例:标题滚动效果

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>标题滚动示例</title>
</head>
<body>
    <h1>请查看浏览器标题栏</h1>

    <script>
        (function() {
            // 设置初始标题
            const title = '欢迎访问我的网站 - 新消息提醒';
            document.title = title;

            // 标题滚动效果
            let position = 0;
            setInterval(function() {
                document.title = title.slice(position) + title.slice(0, position);
                position = (position + 1) % title.length;
            }, 300);
        })();
    </script>
</body>
</html>

代码解释

  • 初始设置 :使用 document.title 设置页面标题
  • 滚动逻辑
    • title.slice(position):获取从 position 位置到字符串末尾的子串
    • title.slice(0, position):获取从开头到 position 位置的子串
    • 将两部分拼接,实现标题字符循环移动的效果
  • position 更新 :每次增加 1,使用取模运算 % title.length 保证在 0 到标题长度之间循环
  • setInterval:每 300ms 执行一次,形成连续滚动效果
  • 实际应用:新消息提醒、活动公告、重要通知等场景

8. 实战案例

本章覆盖目录中全部交互案例:三按钮全选父级全选框目标日期倒计时图片懒加载scrollLeft 无缝滚动TodoList 节点增删改 (见第 6 节)、标题滚动 (见第 7.3 节),并补充随机点名器、选项卡、HTML DOM 与事件衔接。示例图片统一使用同目录下 images/db01.jpgdb10.jpg 资源。

8.1 案例一(基础):三按钮全选/取消全选/反选

应用场景:批量勾选列表项、简易权限勾选,逻辑直观,适合入门。

核心 APIquerySelectorAllchecked 属性、forEach

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>三按钮全选示例</title>
    <style>
        ul { list-style: none; padding-left: 0; }
        li { margin: 8px 0; }
        button { margin-right: 8px; padding: 6px 12px; cursor: pointer; }
    </style>
</head>
<body>
    <ul id="checkboxList">
        <li><input type="checkbox"> 待办事项 1</li>
        <li><input type="checkbox"> 待办事项 2</li>
        <li><input type="checkbox"> 待办事项 3</li>
        <li><input type="checkbox"> 待办事项 4</li>
        <li><input type="checkbox"> 待办事项 5</li>
        <li><input type="checkbox"> 待办事项 6</li>
    </ul>
    <hr>
    <button id="selectAllBtn">全选</button>
    <button id="selectNotAllBtn">取消全选</button>
    <button id="reverseBtn">反选</button>
    <script>
        const checkboxItems = document.querySelectorAll('#checkboxList input');

        document.querySelector('#selectAllBtn').onclick = function () {
            checkboxItems.forEach(function (item) { item.checked = true; });
        };

        document.querySelector('#selectNotAllBtn').onclick = function () {
            checkboxItems.forEach(function (item) { item.checked = false; });
        };

        document.querySelector('#reverseBtn').onclick = function () {
            checkboxItems.forEach(function (item) {
                item.checked = !item.checked;
            });
        };
    </script>
</body>
</html>

代码解释

  • querySelectorAll('#checkboxList input') 一次性拿到列表内所有复选框引用。
  • 全选/取消全选 :遍历集合并统一设置 checkedtruefalse
  • 反选 :对每个复选框取反 !item.checked,未选中变选中,反之亦然。
  • 网站应用:后台批量审核勾选、网盘多文件操作栏、简易问卷多选。

8.2 案例二(增强):父级全选与半选状态

应用场景:邮箱批量操作、购物车商品选择、权限管理等(含「全选框 + 子项联动」)。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>复选框全选示例</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            background: #f5f5f5;
        }

        .container {
            max-width: 600px;
            margin: 0 auto;
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px;
            background: #f0f0f0;
            border-radius: 5px;
            margin-bottom: 15px;
        }

        .checkbox-list {
            list-style: none;
        }

        .checkbox-list li {
            padding: 15px;
            border-bottom: 1px solid #eee;
            display: flex;
            align-items: center;
        }

        .checkbox-list li:hover {
            background: #f9f9f9;
        }

        .checkbox-list input[type="checkbox"] {
            width: 18px;
            height: 18px;
            margin-right: 10px;
            cursor: pointer;
        }

        .buttons {
            display: flex;
            gap: 10px;
            margin-top: 20px;
        }

        .buttons button {
            flex: 1;
            padding: 12px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
            transition: background 0.3s;
        }

        .btn-select {
            background: #4CAF50;
            color: white;
        }
        .btn-select:hover {
            background: #45a049;
        }

        .btn-deselect {
            background: #f44336;
            color: white;
        }
        .btn-deselect:hover {
            background: #da190b;
        }

        .btn-reverse {
            background: #2196F3;
            color: white;
        }
        .btn-reverse:hover {
            background: #0b7dda;
        }

        .status {
            text-align: center;
            padding: 10px;
            margin-top: 15px;
            color: #666;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <label style="display: flex; align-items: center; cursor: pointer;">
                <input type="checkbox" id="checkAll">
                <span style="margin-left: 8px;">全选 / 取消全选</span>
            </label>
            <span id="selectedCount">已选: 0 / 6</span>
        </div>

        <ul id="checkboxList" class="checkbox-list">
            <li><label><input type="checkbox"> HTML5 语义化标签</label></li>
            <li><label><input type="checkbox"> CSS3 Flexbox 布局</label></li>
            <li><label><input type="checkbox"> CSS3 Grid 网格布局</label></li>
            <li><label><input type="checkbox"> JavaScript ES6+ 新特性</label></li>
            <li><label><input type="checkbox"> DOM 操作与事件处理</label></li>
            <li><label><input type="checkbox"> 前端工程化与构建工具</label></li>
        </ul>

        <div class="buttons">
            <button class="btn-select" id="selectAllBtn">全选</button>
            <button class="btn-deselect" id="deselectAllBtn">取消全选</button>
            <button class="btn-reverse" id="reverseBtn">反选</button>
        </div>

        <div class="status" id="status"></div>
    </div>

    <script>
        (function() {
            // 获取元素
            const checkboxList = document.querySelector('#checkboxList');
            const checkboxes = checkboxList.querySelectorAll('input[type="checkbox"]');
            const checkAll = document.querySelector('#checkAll');
            const selectAllBtn = document.querySelector('#selectAllBtn');
            const deselectAllBtn = document.querySelector('#deselectAllBtn');
            const reverseBtn = document.querySelector('#reverseBtn');
            const selectedCount = document.querySelector('#selectedCount');
            const status = document.querySelector('#status');

            // 更新选中数量和全选状态
            function updateStatus() {
                const checkedCount = Array.from(checkboxes).filter(cb => cb.checked).length;
                selectedCount.textContent = `已选: ${checkedCount} / ${checkboxes.length}`;
                checkAll.checked = checkedCount === checkboxes.length && checkboxes.length > 0;
                checkAll.indeterminate = checkedCount > 0 && checkedCount < checkboxes.length;
            }

            // 全选复选框监听
            checkAll.addEventListener('change', function() {
                checkboxes.forEach(cb => cb.checked = this.checked);
                updateStatus();
                showStatus(this.checked ? '已全选' : '已取消全选');
            });

            // 单个复选框监听
            checkboxes.forEach(cb => {
                cb.addEventListener('change', updateStatus);
            });

            // 全选按钮
            selectAllBtn.addEventListener('click', function() {
                checkboxes.forEach(cb => cb.checked = true);
                updateStatus();
                showStatus('已全选');
            });

            // 取消全选按钮
            deselectAllBtn.addEventListener('click', function() {
                checkboxes.forEach(cb => cb.checked = false);
                updateStatus();
                showStatus('已取消全选');
            });

            // 反选按钮
            reverseBtn.addEventListener('click', function() {
                checkboxes.forEach(cb => cb.checked = !cb.checked);
                updateStatus();
                showStatus('已反选');
            });

            // 显示状态提示
            function showStatus(message) {
                status.textContent = message;
                status.style.opacity = '1';
                setTimeout(() => {
                    status.style.opacity = '0.5';
                }, 2000);
            }

            // 初始化状态
            updateStatus();
        })();
    </script>
</body>
</html>

代码解释

  • HTML 结构

    • 顶部全选框:控制所有复选框的状态
    • 复选框列表:6个技术主题选项
    • 操作按钮:全选、取消全选、反选
    • 状态显示:实时显示已选数量和提示信息
  • CSS 样式

    • 响应式布局,卡片式设计
    • 悬停效果和按钮过渡动画
    • 清晰的视觉层次和色彩区分
  • JavaScript 核心功能

    • updateStatus():更新选中数量和全选框状态,使用 indeterminate 属性表示部分选中状态
    • 全选框监听:点击全选框时,同步所有子复选框状态
    • 单个复选框监听:每个子复选框变化时,更新全选框状态
    • 三个操作按钮:全选、取消全选、反选(取反操作)
    • showStatus():显示操作反馈,2秒后淡出
  • 实际应用:邮箱批量删除、购物车全选、权限批量设置、数据批量导出等场景

8.3 案例三:目标日期倒计时

应用场景:活动开始提醒、课程开课提醒、考试时间提醒、任务截止时间提醒。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>目标日期倒计时</title>
    <style>
        body {
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: linear-gradient(135deg, #43cea2, #185a9d);
            font-family: Arial, sans-serif;
        }
        #box {
            padding: 40px;
            border-radius: 16px;
            background: rgba(255, 255, 255, .95);
            color: #185a9d;
            text-align: center;
            font-size: 42px;
            font-weight: 700;
            line-height: 1.8;
            box-shadow: 0 20px 60px rgba(0, 0, 0, .2);
        }
    </style>
</head>
<body>
    <div id="box"></div>

    <script>
        (function () {
            const box = document.querySelector('#box');

            // 目标时间:当前时间 3 天后。实际项目中可替换为后端返回的截止时间。
            const targetDate = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000 + 2 * 60 * 60 * 1000);
            let timerId = null;

            function pad(num) {
                return num < 10 ? '0' + num : String(num);
            }

            function render() {
                const diff = targetDate.getTime() - Date.now();

                if (diff <= 0) {
                    clearInterval(timerId);
                    box.innerHTML = '活动已开始';
                    return;
                }

                const days = Math.floor(diff / (24 * 60 * 60 * 1000));
                const hours = Math.floor(diff % (24 * 60 * 60 * 1000) / (60 * 60 * 1000));
                const minutes = Math.floor(diff % (60 * 60 * 1000) / (60 * 1000));
                const seconds = Math.floor(diff % (60 * 1000) / 1000);

                box.innerHTML = '距离活动开始还有<br>' +
                    pad(days) + ' 天 ' + pad(hours) + ' 时 ' +
                    pad(minutes) + ' 分 ' + pad(seconds) + ' 秒';
            }

            render();
            timerId = setInterval(render, 1000);
        })();
    </script>
</body>
</html>

代码解释

  • targetDate.getTime() - Date.now() 得到目标时间与当前时间的毫秒差。
  • 通过取整与取余依次拆出天、小时、分钟、秒。
  • pad() 负责补零,让视觉格式稳定。
  • 当差值小于等于 0 时,必须 clearInterval() 清除定时器,避免持续执行。
  • 网站应用:课程预约、直播开始提醒、限时表单提交、任务截止页。
相关推荐
lsx2024064 小时前
Vue3 表单深度解析
开发语言
zhoumeina994 小时前
如何保证不同位置切换合成底图的渲染顺序
java·前端·javascript
欢璃4 小时前
笔试强训练习
java·开发语言·jvm·数据结构·算法·贪心算法·动态规划
海上彼尚4 小时前
Nodejs也能写Agent - 3.基础篇 - Tools 与 Tool Calling
前端·人工智能·后端·node.js
用户125758524364 小时前
GoFrame + Vue3 后台管理框架,CRUD 代码生成器一键搭 RBAC 权限系统
前端
七十二時_阿川4 小时前
Electron 如何自定义菜单?这篇帮你实现原生体验!
前端·electron
bot5556664 小时前
企业微信ipad协议的消息引用与回复机制
javascript
七十二時_阿川4 小时前
Electron App 速查表:生命周期事件、方法、平台差异
前端·electron
七十二時_阿川4 小时前
Electron 多显示器开发?这篇帮你搞定屏幕坐标与窗口定位!
前端·electron