JavaScript DOM 操作:选择器、事件与节点修改

FreeGuideOnline 最新 2026-06-15

JavaScript DOM 操作:选择器、事件与节点修改

DOM(文档对象模型)是浏览器将 HTML 文档解析为一个由节点和对象组成的树形结构。JavaScript 通过 DOM 可以实现对页面内容、结构和样式的动态控制,这是构建交互式网页的核心能力。本篇指南将系统介绍选择器、事件绑定与节点修改三大核心操作。


1. 核心概念:什么是 DOM?

浏览器在加载 HTML 页面后,会生成一个对应的 DOM 树。每个标签、属性、文本都成为树上的节点(Node),常见节点类型有:

  • 元素节点(element,对应 HTML 标签)
  • 文本节点(text,标签内的文字)
  • 属性节点(attribute,已废弃,现通过元素直接访问)

JavaScript 通过全局对象 document 来操作整个 DOM 树。


2. 选择器:精准定位页面元素

要操作某个元素,第一步是获取它的引用。DOM 提供了多种选择方法,按灵活性和性能可分为传统方法和现代方法。

2.1 传统选择方法(速度快、兼容性好)

方法 描述 返回值
document.getElementById(id) 通过 id 属性选取元素 单个元素 或 null
document.getElementsByClassName(cls) 通过类名选取元素 动态 HTMLCollection
document.getElementsByTagName(tag) 通过标签名选取元素 动态 HTMLCollection
document.getElementsByName(name) 通过 name 属性选取元素(常用于表单) 动态 NodeList

示例:

const header = document.getElementById('main-header');
const buttons = document.getElementsByClassName('btn');
const allDivs = document.getElementsByTagName('div');
const genderRadios = document.getElementsByName('gender');

注意getElementsByClassNamegetElementsByTagName 返回的是动态集合,即文档变化时集合会自动更新;查询性能优于 querySelectorAll,但遍历时需注意死循环。

2.2 现代选择方法(灵活、功能强大)

使用 CSS 选择器语法,几乎可以匹配任何元素。

方法 描述 返回值
document.querySelector(cssSelector) 匹配第一个满足选择器的元素 单个元素或 null
document.querySelectorAll(cssSelector) 匹配所有满足选择器的元素 静态 NodeList

示例:

// 选择第一个类为 .container 的元素
const container = document.querySelector('.container');

// 选择所有带有 data-type="special" 的 li 元素
const specialItems = document.querySelectorAll('li[data-type="special"]');

// 复杂选择器:选择 body 下所有偶数行的 div
const evenDivs = document.querySelectorAll('body > div:nth-child(even)');

静态 vs 动态:

  • querySelectorAll 返回的是静态 NodeList,不会随文档改变而更新。
  • NodeList 具备 forEach 方法,但并非数组,如需使用 map/filter 等,可用 Array.from() 或扩展运算符转换。

2.3 基于现有元素的范围选择

不仅可以用 document 调用选择器,任何元素实例也可以调用,从而在其后代中查找。

const card = document.querySelector('.card');
const title = card.querySelector('.title'); // 只在 card 内部查找

3. 事件:响应用户交互

事件是用户与页面交互的通道。事件驱动编程包含:获取元素、绑定事件监听器、编写处理函数。

3.1 事件绑定方式

推荐使用 addEventListener,因为它支持多个监听器、精确控制事件阶段。

element.addEventListener(eventType, handler [, options]);

示例:

const btn = document.getElementById('submitBtn');

btn.addEventListener('click', function(event) {
  console.log('按钮被点击', event.target);
  // 阻止默认行为,如表单提交
  event.preventDefault();
});

3.2 常用事件类型

分类 事件名 触发时机
鼠标事件 click 鼠标单击
dblclick 鼠标双击
mouseenter 鼠标进入元素(不冒泡)
mouseleave 鼠标离开元素(不冒泡)
键盘事件 keydown 键盘按下
keyup 键盘松开
表单事件 submit 表单提交
input 表单输入值变化(实时)
change 表单值改变并失去焦点时
焦点事件 focus 元素获得焦点
blur 元素失去焦点
文档/窗口 DOMContentLoaded HTML 解析完成,DOM 就绪
load 页面所有资源加载完成
scroll 滚动条滚动
resize 窗口大小改变

3.3 事件对象

事件处理函数自动接收一个 event 对象,包含丰富信息:

  • event.target:触发事件的实际元素(事件源)
  • event.currentTarget:绑定监听器的元素(即 this,箭头函数不能用)
  • event.type:事件类型
  • event.preventDefault():取消默认行为(链接跳转、表单提交等)
  • event.stopPropagation():阻止事件冒泡
  • 键盘事件的 event.keyevent.code
  • 鼠标事件的 event.clientXevent.clientY

3.4 事件委托(提高性能,处理动态元素)

利用事件冒泡机制,将事件绑定在父元素上,通过 event.target 判断实际触发子元素。适用于列表项、动态插入的元素等场景。

const todoList = document.querySelector('#todo-list');

todoList.addEventListener('click', function(e) {
  if (e.target.tagName === 'LI') {
    e.target.classList.toggle('completed');
  }
});

3.5 移除事件监听

必须使用与绑定相同的函数引用,匿名函数无法直接移除。

function handleClick(e) { /* ... */ }
element.addEventListener('click', handleClick);
// 稍后移除
element.removeEventListener('click', handleClick);

4. 节点修改:动态改变页面内容与结构

4.1 修改元素内容

属性 / 方法 描述
element.textContent 获取/设置纯文本内容,自动转义 HTML 标签
element.innerText textContent 类似,但考虑 CSS 样式(隐藏元素不可见),且触发回流
element.innerHTML 获取/设置 HTML 内容,会解析标签,存在 XSS 风险

最佳实践:
优先使用 textContent 处理文本,避免注入攻击;仅在需要插入 HTML 片段时使用 innerHTML,并确保内容可信。

const para = document.querySelector('p');
para.textContent = '这是安全的文本';
para.innerHTML = '<strong>加粗文本</strong>'; // 仅在内容可控时使用

4.2 修改元素属性

标准 HTML 属性可直接通过属性名操作,自定义属性(data-*)推荐使用 dataset

  • 标准属性element.idelement.hrefelement.srcelement.className 等。
  • 通用方法
    • element.getAttribute('attr')
    • element.setAttribute('attr', 'value')
    • element.removeAttribute('attr')
  • 类名操作(推荐使用 classList)
    • element.classList.add('class')
    • element.classList.remove('class')
    • element.classList.toggle('class')
    • element.classList.contains('class')(返回布尔值)
  • data 属性
    <div data-user-id="123" data-role="admin"></div>
    
    const div = document.querySelector('div');
    console.log(div.dataset.userId); // "123"
    div.dataset.isActive = 'true';   // 对应 data-is-active
    

4.3 修改样式

通过 element.style 可以修改内联样式,但更优雅的方式是操作类名(切换 CSS 类)。

element.style.color = 'red';
element.style.fontSize = '18px'; // 驼峰命名

// 更推荐:定义 CSS 类,然后切换
element.classList.toggle('highlight');

获取计算后的样式(包括继承与样式表值)使用 getComputedStyle

const computedColor = window.getComputedStyle(element).color;

4.4 创建与插入节点

方法 描述
document.createElement(tag) 创建元素节点
document.createTextNode(text) 创建文本节点
parent.appendChild(newNode) 将节点添加为父元素的最后一个子节点
parent.insertBefore(newNode, refNode) 在父元素的指定子节点之前插入新节点
parent.append(node1, node2, ...) 现代方法,可插入多个节点或文本字符串
parent.prepend(node1, ...) 插入为第一个子节点
refNode.before(node) 在某个节点前面插入
refNode.after(node) 在某个节点后面插入

示例:创建完整列表项并插入

const ul = document.querySelector('ul');

// 创建元素
const li = document.createElement('li');
li.textContent = '新项目';
li.classList.add('new-item');

// 插入到列表末尾
ul.append(li);

// 或插入到第一个位置
ul.prepend(li);

4.5 替换与删除节点

// 替换
const oldNode = document.getElementById('old');
const newNode = document.createElement('span');
newNode.textContent = '新内容';
oldNode.parentNode.replaceChild(newNode, oldNode);

// 现代方法:直接调用 replaceWith
oldNode.replaceWith(newNode);

// 删除
const element = document.querySelector('.remove-me');
element.parentNode.removeChild(element);

// 现代方法:直接删除自身
element.remove();

4.6 克隆节点

node.cloneNode(deep)deeptrue 时深克隆(包含后代节点),false 时仅克隆当前节点。

const template = document.querySelector('.template');
const clone = template.cloneNode(true);
document.body.appendChild(clone);

5. 实战模式:一个完整的待办事项示例

下面综合运用选择器、事件委托和 DOM 修改,实现基础待办添加与删除功能。

HTML 结构:

<div id="app">
  <input type="text" id="todoInput" placeholder="输入新任务">
  <button id="addBtn">添加</button>
  <ul id="todoList"></ul>
</div>

JavaScript:

const input = document.getElementById('todoInput');
const addBtn = document.getElementById('addBtn');
const list = document.getElementById('todoList');

// 添加任务
addBtn.addEventListener('click', () => {
  const text = input.value.trim();
  if (text === '') return;

  const li = document.createElement('li');
  li.textContent = text;

  // 添加删除按钮
  const delBtn = document.createElement('button');
  delBtn.textContent = '删除';
  delBtn.classList.add('delete-btn');
  li.appendChild(delBtn);

  list.appendChild(li);
  input.value = '';
  input.focus();
});

// 事件委托处理列表点击(完成切换、删除)
list.addEventListener('click', (e) => {
  const target = e.target;
  if (target.tagName === 'LI') {
    target.classList.toggle('completed');
  }
  if (target.classList.contains('delete-btn')) {
    target.parentElement.remove();
  }
});

6. 性能与最佳实践

  • 减少回流与重绘:批量修改 DOM 时,使用 DocumentFragment 或先移除元素修改后再插入,或使用 cloneNode 在内存中构建。
  • 事件委托优先:在父容器上绑定一个监听器,避免为每个子元素单独绑定。
  • 避免使用 innerHTML 进行频繁拼接:使用 createElement + append 更具可预测性且安全。
  • 缓存选择器结果:多次使用的元素应存储为变量,避免重复查询 DOM。
  • 使用 requestAnimationFrame 进行视觉更新:连续样式变化应放在动画帧回调中。

7. 总结

JavaScript DOM 操作是前端开发的基础。掌握现代选择器(querySelector/querySelectorAll)能快速定位元素;理解事件模型、学会使用事件委托可以提高代码健壮性;通过节点创建、属性/内容修改、插入删除等方法可以实现任意动态交互效果。遵循安全与性能实践,你将能构建出流畅、可维护的用户界面。

下一步建议:深入学习 DOM 遍历(父子兄弟节点)、MutationObserver 监听变化、以及结合 AJAX 动态获取数据更新 DOM。持续练习,将理论转化为肌肉记忆。