JavaScript DOM 操作
学习用JS操作网页元素 — 选择、修改、创建 · 难度:进阶 · +15XP
JavaScript DOM 操作 — 让网页动起来
一、什么是 DOM?为什么它如此重要?
DOM(Document Object Model,文档对象模型)是浏览器将 HTML 文档解析后生成的一个树形结构对象。通过 DOM,JavaScript 可以访问和修改网页上的任何元素、属性和样式。
简单来说:HTML 是网页的骨架,CSS 是皮肤,而 JavaScript(通过 DOM)是肌肉和大脑。没有 DOM 操作,网页就只是静态的文档,无法响应用户操作。
为什么 DOM 操作重要?
- 动态内容 — 无需刷新页面就能更新内容(如聊天消息、实时数据)
- 交互体验 — 点击按钮显示弹窗、切换标签页、拖拽排序
- 表单处理 — 实时验证用户输入、显示错误提示
- 动画效果 — 轮播图、滚动动画、页面过渡
- 所有前端框架(React、Vue、Angular)底层都是对 DOM 操作的封装和优化
二、DOM 树结构图解
document(根节点)
└─ html
├─ head
│ ├─ title → "我的网页"
│ └─ meta
└─ body
├─ h1 → "欢迎"
├─ p → "这是一段文字"
├─ div#app → (id="app")
│ └─ button → "点击我"
└─ ul.list
├─ li → "项目1"
└─ li → "项目2"
三、获取 DOM 元素的六种方法 — 对比表
| 方法 | 选择器 | 返回值 | 兼容性 | 使用场景 |
|---|---|---|---|---|
| getElementById() | #id | 单个元素或 null | 全部浏览器 | 获取唯一元素(最快) |
| getElementsByClassName() | .class | 动态 HTMLCollection | 全部浏览器 | 获取一组同类元素 |
| getElementsByTagName() | tag | 动态 HTMLCollection | 全部浏览器 | 获取所有同标签元素 |
| querySelector() | CSS 选择器 | 第一个匹配元素 | IE8+ | 最灵活(推荐) |
| querySelectorAll() | CSS 选择器 | 静态 NodeList | IE8+ | 获取所有匹配元素(推荐) |
| closest() | CSS 选择器 | 最近的祖先元素 | 现代浏览器 | 事件委托时向上查找 |
四、详细代码示例(逐行注释)
// ======== 1. 获取元素 ========
// 通过 ID 获取(最快,返回单个元素)
const title = document.getElementById("main-title");
console.log(title.textContent); // 读取元素的文本内容
// 通过 CSS 选择器获取(最灵活,推荐日常使用)
const firstBtn = document.querySelector(".btn"); // 获取第一个 .btn
const allBtns = document.querySelectorAll(".btn"); // 获取所有 .btn(NodeList)
const appDiv = document.querySelector("#app"); // 获取 id="app" 的元素
const allLinks = document.querySelectorAll("nav a"); // 获取 nav 下所有链接
// 通过类名获取(返回动态集合)
const items = document.getElementsByClassName("item"); // HTMLCollection(动态)
// ======== 2. 修改内容 ========
// textContent:纯文本,不解析 HTML(安全)
title.textContent = "新标题";
// innerHTML:解析 HTML 标签(注意 XSS 风险!)
appDiv.innerHTML = "加粗文字";
// innerText:考虑 CSS 样式的可见文本(会触发回流,较慢)
// ======== 3. 修改样式 ========
const box = document.querySelector(".box");
box.style.backgroundColor = "red"; // 驼峰命名(CSS 的 background-color)
box.style.fontSize = "20px"; // 字符串,必须带单位
box.style.display = "none"; // 隐藏元素
box.classList.add("active"); // 添加 CSS 类(推荐方式)
box.classList.remove("hidden"); // 移除 CSS 类
box.classList.toggle("dark"); // 切换 CSS 类(有则删,无则加)
box.classList.contains("active"); // 检查是否包含某类
// ======== 4. 修改属性 ========
const img = document.querySelector("img");
img.src = "new-image.jpg"; // 修改图片源
img.alt = "描述文字"; // 修改替代文本
const link = document.querySelector("a");
link.href = "https://example.com"; // 修改链接地址
link.setAttribute("target", "_blank");// 设置属性
const value = link.getAttribute("href"); // 读取属性
link.removeAttribute("target"); // 移除属性
// data-* 属性使用 dataset
link.dataset.id = "123"; // 对应 data-id="123"
console.log(link.dataset.id); // 读取 data-id
// ======== 5. 创建和删除元素 ========
// 创建新元素
const newP = document.createElement("p"); // 创建 p 标签
newP.textContent = "这是新添加的段落"; // 设置内容
newP.className = "text-red"; // 设置类名
// 添加到页面
document.body.appendChild(newP); // 追加到 body 末尾
// 插入到指定位置
const ref = document.querySelector("#ref");
ref.insertAdjacentElement("beforebegin", newP); // 在 ref 前面插入
// 参数:beforebegin(元素前), afterbegin(第一个子元素前),
// beforeend(最后一个子元素后), afterend(元素后)
// 删除元素
const oldEl = document.querySelector(".remove-me");
oldEl.remove(); // 现代方法(推荐)
// 旧方法:oldEl.parentNode.removeChild(oldEl);
// 克隆元素
const clone = newP.cloneNode(true); // true = 深克隆(包括子元素)
// false = 浅克隆(只克隆标签本身)
// ======== 6. 遍历 DOM 树 ========
const current = document.querySelector(".current");
const parent = current.parentElement; // 父元素
const children = current.children; // 所有子元素(HTMLCollection)
const first = current.firstElementChild; // 第一个子元素
const last = current.lastElementChild; // 最后一个子元素
const prev = current.previousElementSibling; // 前一个兄弟元素
const next = current.nextElementSibling; // 后一个兄弟元素
// ======== 7. 综合示例:动态创建列表 ========
const fruits = ["苹果", "香蕉", "橙子", "葡萄"];
const ul = document.querySelector("#fruit-list");
ul.innerHTML = ""; // 清空列表
fruits.forEach(fruit => {
const li = document.createElement("li"); // 创建 li
li.textContent = fruit; // 设置文字
li.className = "fruit-item"; // 添加类名
ul.appendChild(li); // 添加到 ul 中
});
五、innerHTML vs textContent vs createElement — 性能对比
| 方式 | 速度 | 安全性 | 适用场景 |
|---|---|---|---|
| innerHTML | 快(批量设置) | 有 XSS 风险 | 大量 HTML 片段替换 |
| textContent | 快 | 安全 | 纯文本更改 |
| createElement | 较慢(逐个创建) | 安全 | 精确控制、事件绑定 |
| insertAdjacentHTML | 最快(不破坏现有 DOM) | 有 XSS 风险 | 在指定位置插入 HTML |
六、实践任务
- 创建一个 HTML 页面,包含一个空
<ul id="todo-list"> - 用 JS 动态添加 5 个待办事项到列表中
- 点击每个待办事项时,用
classList.toggle("done")切换完成样式(加删除线) - 添加一个"全部删除"按钮,点击后清空列表
- 使用
querySelectorAll统计当前列表中还有多少个未完成的项目,显示在页面上