MDN 提供的 JavaScript DOM 操作标准示例有哪些

作者:窗边听雨时间:2026-10-30 18:45:41浏览:332

全面梳理 MDN Web Docs 中推荐的 JavaScript DOM 操作标准示例,涵盖元素查找、节点创建挂载、属性与类名管理、事件委托及官方经典 Shopping List 范例。

在 Web 前端开发中,MDN Web Docs(Mozilla 开发者网络)被公认为权威的 Web 标准参考资料。本文系统梳理 MDN 在“文档对象模型(DOM)操作”与“Manipulating documents”教程中提供的核心标准示例,覆盖元素检索、节点动态增删、属性与类名响应、事件委托分发,以及官方最具代表性的完整实战范例“动态购物清单”。通过这些标准示例,帮助开发者掌握符合现代 Web 规范的原生 JavaScript DOM 编程方式。

一、DOM 树模型与标准节点认知

在深入具体代码前,MDN 首先通过结构模型向开发者阐明:浏览器在解析 HTML 文档后,会将其转换为由节点(Node)构成的树状层次结构,即 DOM 树。在 JavaScript 中,所有页面元素、文本内容甚至属性都是这棵树上的一个节点。

理解标准 DOM 操作的核心,在于明确不同节点类型之间的职责。Document 是整棵树的访问入口,Element(元素节点)负责承载 HTML 标签与布局结构,而标签内部的纯文字则被封装在 Text(文本节点)中。MDN 强调,现代 DOM 操作的一大准则是区分“元素操作”与“纯文本注入”,从而在根源上避免安全缺陷。

MDN 提供的 JavaScript DOM 操作标准示例有哪些
图 1:DOM 树的层次结构与节点类型映射关系

二、标准示例一:元素查询与选择范式

在 MDN 文档体系中,检索页面元素被归类为所有交互的第一步。在早期的 JavaScript 教学中,经常能看到 document.getElementById() 与 document.getElementsByTagName()。而在现代 MDN 标准教程中,推荐采用统一基于 CSS 选择器语法的 querySelector 系列 API。

1. 单个与多个元素的标准查询

当需要匹配页面中符合规则的第一个元素时,使用 document.querySelector();当需要批量匹配所有符合规则的元素时,使用 document.querySelectorAll()。

// 1. 匹配 ID 选择器(返回符合条件的第一个 Element,未找到返回 null)
const heading = document.querySelector('#main-heading');

// 2. 匹配类选择器或复合选择器
const submitBtn = document.querySelector('button.btn-primary');

// 3. 批量匹配元素(返回静态 NodeList 集合)
const items = document.querySelectorAll('ul.todo-list > li');

// 标准遍历 NodeList
items.forEach((item, index) => {
  console.log(`第 ${index + 1} 项内容: ${item.textContent}`);
});
易混淆点辨析:querySelectorAll 返回的 NodeList vs getElementsByClassName 返回的 HTMLCollection

许多初学者常疑惑:既然旧方法 getElementsByClassName() 执行性能略快,为什么 MDN 仍全面推荐 querySelectorAll()?

原因在于“实时性(Live)”带来的遍历陷阱。getElementsByClassName() 返回的是动态 HTMLCollection,当在循环体内向页面增删带有该类名的元素时,集合长度(length)会随之动态改变,极易引发死循环或索引漂移。而 querySelectorAll() 返回的是静态快照(Static NodeList),并且原生支持 .forEach() 迭代方法,逻辑可预测性更强。

三、标准示例二:节点的动态创建、配置与挂载

MDN 在“动态操作页面(Manipulating documents)”教学中,给出了极其严格且通用的节点创建流程,通常遵循四步法:创建孤岛节点 → 配置属性与文本 → 绑定必要事件 → 挂载至主文档树。

MDN 提供的 JavaScript DOM 操作标准示例有哪些
图 2:MDN 经典 DOM 动态创建与挂载的标准四步法

2. 基础创建示例:动态生成列表项

// 获取目标容器
const listContainer = document.querySelector('#product-list');

// 1. 创建 <li> 节点
const newListItem = document.createElement('li');

// 2. 设置内容与属性
newListItem.textContent = '高质量降噪耳机';
newListItem.classList.add('list-item', 'active');
newListItem.setAttribute('data-id', 'sku-1002');

// 3. 挂载到容器中
listContainer.appendChild(newListItem);

3. 现代 API 扩展:appendChild 与 append 的标准选择

在传统示例中,MDN 使用 Node.appendChild()。而在现代规范更新后,MDN 同步引入了 Element.append(),两者存在明确的特性差异:

  • appendChild() 仅接受一个 Node 对象,并返回被追加的该节点。
  • append() 支持同时传入多个参数,不仅支持 Node 节点,还直接支持插入字符串(内部会自动转换为 Text 节点),且没有返回值。
// 现代 append() 标准语法:一次性插入多个元素与文本
const badge = document.createElement('span');
badge.textContent = '热销';
badge.className = 'badge';

// 同时追加 DOM 节点与文本内容
newListItem.append(badge, '(限时特惠中)');

4. 批量插入优化示例:DocumentFragment

当需要连续插入成百上千个节点时,若每次循环都直接调用 container.appendChild(),会导致浏览器频繁触发重排(Reflow)与重绘(Repaint)。MDN 在性能优化指导中给出的标准范例是使用轻量级的文档片段:document.createDocumentFragment()。

const fragment = document.createDocumentFragment();
const dataList = ['JavaScript', 'TypeScript', 'Node.js', 'Web API'];

dataList.forEach((tech) => {
  const li = document.createElement('li');
  li.textContent = tech;
  fragment.appendChild(li); // 此时仍在内存中操作,不引起页面重绘
});

// 将包含所有子节点的 fragment 一次性挂载至主树
listContainer.appendChild(fragment);

四、标准示例三:内容注入安全性与 innerHTML 辨析

在 DOM 操作教学中,如何更新容器内部内容是一个不可回避的问题。MDN 在其安全指南与 DOM 教程中明确指出了 innerHTML 与 textContent 的边界。

MDN 提供的 JavaScript DOM 操作标准示例有哪些
图 3:innerHTML 与 createElement + textContent 的运作机制与安全性对比
写作辨析:是否必须完全废弃 innerHTML?

许多教程常常提出“永远不要使用 innerHTML”的论断,这一说法看似正确,但不够严谨。MDN 给出的客观建议是:

  • 什么时候可以使用:当内容是硬编码的静态 HTML 片段,且内部不需要维持事件绑定时,或者用来一行代码清空容器(element.innerHTML = ''),此时使用 innerHTML 是极为简练高效的。
  • 什么时候严禁使用:凡是内容中混入了来自输入框、URL 参数、服务端未受信任返回的动态字符串,严禁直接拼接进 innerHTML,必须改用 textContent 或 document.createTextNode()。

五、标准示例四:属性控制与 classList 样式管理

修改元素样式和状态是 DOM 操作中最频繁的需求。MDN 强调将“结构、样式与行为解耦”,反对在 JavaScript 中大段编写行内 style 属性。

1. 标准类名控制器:classList

MDN 规范指出,控制样式的最佳实践是在 CSS 文件中预定义好类选择器,并在 JavaScript 中通过 Element.classList 接口来增删状态类。

const panel = document.querySelector('.info-panel');

// 添加样式类
panel.classList.add('visible', 'highlight');

// 移除样式类
panel.classList.remove('hidden');

// 切换样式类(存在则移除,不存在则添加)
panel.classList.toggle('active');

// 条件式切换(第二个布尔参数:true 时添加,false 时移除)
const isOnline = checkConnection();
panel.classList.toggle('online-mode', isOnline);

// 判断是否包含特定样式类
if (panel.classList.contains('visible')) {
  console.log('面板正处于展示状态');
}

2. 自定义数据属性的标准读取:dataset

HTML5 允许在元素上定义以 data- 为前缀的自定义属性。MDN 示例展示了如何使用 dataset 接口实现 JavaScript 与 HTML 标记的解耦传参:

// HTML: <div id="user" data-user-id="9527" data-role-type="admin"></div>
const userEl = document.querySelector('#user');

// 读取属性(kebab-case 自动映射为 camelCase 驼峰命名)
console.log(userEl.dataset.userId);   // 输出: "9527"
console.log(userEl.dataset.roleType); // 输出: "admin"

// 写入或更新自定义属性
userEl.dataset.status = 'active'; // 相当于添加 data-status="active"

六、标准示例五:事件监听与高效“事件委托”范式

响应用户交互是 DOM 编程的核心。MDN 在“Introduction to events”章节中彻底规范了事件绑定的标准写法,即使用 EventTarget.addEventListener()。

1. 基础事件监听规范

MDN 强烈建议避免使用 HTML 内联 onclick="..." 或 DOM0 级属性 element.onclick = fn,因为它们无法为一个事件附加多个监听器,且容易造成覆盖。

const actionBtn = document.querySelector('#submit-action');

function handleClick(event) {
  // 阻止表单默认提交刷新
  event.preventDefault();
  console.log('点击按钮触发:', event.target);
}

// 绑定标准监听
actionBtn.addEventListener('click', handleClick);

// 在必要时精确移除监听(必须引用同一个具名函数)
// actionBtn.removeEventListener('click', handleClick);

2. 经典进阶示例:事件冒泡与事件委托(Event Delegation)

在处理动态列表或大量子元素(如几十个表格行、列表项)时,若为每个子元素单独调用 addEventListener,不仅浪费内存,而且动态新增的子项将无法响应事件。

MDN 提供的标准解法是利用 DOM 的事件冒泡(Event Bubbling)机制:将监听器绑定在公共父级容器上,通过 event.target 判断真正被点击的子元素。

const parentList = document.querySelector('#dynamic-list');

// 只在父容器上绑定一次点击监听
parentList.addEventListener('click', (event) => {
  // 使用 Element.closest() 准确定位被点击项是否属于目标列表项
  const targetItem = event.target.closest('li.list-item');
  
  if (!targetItem || !parentList.contains(targetItem)) {
    return; // 点击落在列表外部或空白区域,忽略
  }

  // 判断是否点击了删除按钮
  if (event.target.matches('button.delete-btn')) {
    targetItem.remove(); // 直接调用现代 Element.remove() 移除节点
    console.log('已成功删除列表项');
  } else {
    targetItem.classList.toggle('selected');
  }
});

七、MDN 官方经典综合示例:Shopping List 实战剖析

在 MDN 教程的“Manipulating documents”章节末尾,官方提供了一个标志性的实战综合演练——动态购物清单(Shopping List)。该范例紧凑而完整地组合了输入框读取、节点动态构建、嵌套结构挂载、事件驱动删除以及交互焦点管理。

1. 完整的 HTML 结构骨架

<div class="shopping-app">
  <h1>我的购物清单</h1>
  <div class="input-group">
    <label for="item-input">输入商品名:</label>
    <input type="text" id="item-input" placeholder="例如:鲜牛奶" />
    <button id="add-btn">添加到清单</button>
  </div>
  <ul id="shopping-list"></ul>
</div>

2. MDN 官方标准 JavaScript 逻辑实现

// 1. 获取核心 DOM 引用
const list = document.querySelector('#shopping-list');
const input = document.querySelector('#item-input');
const button = document.querySelector('#add-btn');

// 2. 绑定添加事件处理函数
button.addEventListener('click', () => {
  const itemText = input.value.trim();

  // 空值校验
  if (itemText === '') {
    input.focus();
    return;
  }

  // 3. 动态创建列表项及子组件
  const listItem = document.createElement('li');
  const textSpan = document.createElement('span');
  const deleteBtn = document.createElement('button');

  // 4. 组装子节点内容
  textSpan.textContent = itemText;
  deleteBtn.textContent = '删除';
  deleteBtn.className = 'delete-item-btn';

  // 5. 建立层级关系
  listItem.append(textSpan, deleteBtn);

  // 6. 为新建节点的删除按钮绑定专属销毁逻辑
  deleteBtn.addEventListener('click', () => {
    // 现代标准 API: 直接调用 element.remove()
    listItem.remove();
  });

  // 7. 挂载到主列表,清空输入框并重置焦点
  list.appendChild(listItem);
  input.value = '';
  input.focus();
});
Shopping List 范例展现的 MDN 标准工程习惯
  1. 焦点闭环(Focus Loop):添加或点击删除后,显式调用 input.focus(),极大地提升了无障碍(Accessibility)体验与键盘操作连贯性。
  2. 空值过滤与清理:通过 .trim() 防御纯空格无意义数据。
  3. 声明式结构组装:先使用 createElement 逐个创建 li、span、button,再通过层级关系进行安全挂载,绝不使用脆弱的 HTML 字符串拼接。

八、MDN DOM 操作核心 API 与最佳实践全景总结

综观 MDN Web Docs 提供的所有 DOM 教学与参考指南,其标准示例的设计哲学可以归纳为四大原则:CSS 选择器统一化、节点构建安全化、交互逻辑委托化、样式表现类名化。下方的总结矩阵清晰展示了各核心任务下的推荐 API 与技术要点。

MDN 提供的 JavaScript DOM 操作标准示例有哪些
图 4:MDN DOM 操作核心 API 与最佳实践矩阵全貌

常见操作 API 速查表

操作领域 现代 MDN 推荐写法 早期/易混淆写法 主要优势与推荐理由
元素查找 querySelector() / querySelectorAll() getElementById() / getElementsByTagName() 统一选择器语法,返回静态 NodeList,支持 forEach 迭代,无动态变动隐患。
内容插入 createElement() + textContent innerHTML = ... 拼接 自动转义纯文本,天然免疫 XSS 漏洞,保留容器内既有 DOM 节点与事件绑定。
节点挂载 parent.append(el1, el2) parent.appendChild(el1) 可一次性传入多个节点及字符串,语法更现代化、更灵活。
样式管理 el.classList.add() / toggle() el.style.cssText = ... 将表现形式交给 CSS 集中管理,JS 仅负责状态开关,保持代码高可维护性。
节点删除 element.remove() parent.removeChild(element) 节点直接调用自我删除,无需冗余获取 parentNode 引用。
事件交互 父级 addEventListener (事件委托) 给每个子元素遍历绑定 onclick 大幅降低内存占用,且能天然支持后续动态新增子项的点击响应。

掌握 MDN 提供的这些标准示例,能够让开发者在脱离各类前端框架(如 React、Vue)的情况下,编写出执行效率高、代码结构健壮且符合 Web 安全规范的原生 JavaScript 应用。这些标准范式同样是深入理解现代框架虚拟 DOM 与响应式渲染机制的坚实基石。

作者最新文章
相关文章
更多