JavaScript DOM操作实例:动态修改HTML元素与样式

作者:白露时间:2026-10-30 18:39:46浏览:81

深入讲解JavaScript DOM操作的核心方法与实战技巧,通过具体代码实例演示如何高效、安全地查找节点、动态修改HTML元素内容、增删节点结构以及通过内联style与classList切换视觉样式。

在前端交互开发中,通过 JavaScript 操作文档对象模型(DOM)是实现动态界面的基础。浏览器加载 HTML 文档时会将其解析为由节点构成的树状结构,脚本通过 DOM API 定位目标元素后,即可实时修改其文本内容、属性状态或视觉样式。本文将以一个可直接运行的交互卡片为例,系统梳理 DOM 元素的查找定位、文本与结构更新、样式修改的两种主要途径(内联 style 与 classList),并提供生产环境中需遵循的安全与性能防范准则。

一、DOM 树解析与目标元素定位

操作 DOM 的前置步骤是准确定位需要修改的 HTML 节点。现代浏览器提供了标准的选择器 API,开发者可以依据 ID、类名、标签名或复合 CSS 属性精确锁定单个节点或节点集合。

常用的元素查找方法主要分为两类:专用获取方法(如 getElementById)与现代选择器方法(如 querySelector 和 querySelectorAll)。在实际开发中,querySelector 允许使用标准的 CSS 选择器语法,能够统一处理类选择器、属性选择器和层级关系,具有极高的灵活性。

JavaScript DOM操作实例:动态修改HTML元素与样式
图 1:DOM 树层级拓扑与常用选择器定位映射关系

检索元素时需注意返回值类型:getElementById 与 querySelector 匹配成功时返回具体的 Element 对象,未匹配到时返回 null;而 querySelectorAll 返回的是静态 NodeList 集合,即便没有找到匹配项也会返回空列表而非 null。因此在后续执行修改前,应养成检查节点是否存在的良好习惯。

// 1. 获取外层容器
const card = document.querySelector('#product-card');

// 2. 在指定容器作用域内检索子节点,避免全局污染
const titleEl = card.querySelector('.title');
const badgeEl = card.querySelector('.badge');
const priceEl = document.getElementById('price-tag');
const toggleBtn = document.getElementById('btn-toggle');

// 3. 安全防御式检查
if (!card || !toggleBtn) {
  console.warn('关键 DOM 节点未找到,请检查 HTML 结构。');
}

二、动态修改 HTML 元素内容与结构

成功获取节点后,最常见的需求是动态更新界面中展示的文本或增删 HTML 标签。对元素内容的修改主要有三个常用属性:textContent、innerText 以及 innerHTML。

1. 文本内容更新:textContent 与 innerHTML 的选型边界

textContent:用于读写节点及其所有后代的纯文本。赋值时,浏览器会将输入值当做普通字符渲染,任何 HTML 标签字符串都会被自动转义而不会被当作标签解析。它不会触发页面的复杂重排计算,执行速度快,且能天然防御跨站脚本攻击(XSS)。

innerHTML:用于读写节点内部的 HTML 标记。当需要动态渲染富文本或批量构建复杂嵌套标签时使用。但若内容来源于外部用户输入,必须在插入前做严格的净化与转义,否则容易引发严重的 XSS 安全隐患。

开发建议:只要目标是单纯修改文字(如更新价格、修改按钮文本、替换提示信息),请一律优先使用 textContent,避免盲目使用 innerHTML。

2. 节点的新增与挂载流程

当需要向容器中添加新的 DOM 节点时,规范的流程包含三步:使用 document.createElement() 创建新节点、配置该节点的属性及文本、使用 appendChild() 或 append() 将其插入目标父容器中。

JavaScript DOM操作实例:动态修改HTML元素与样式
图 2:动态创建、修改并挂载 DOM 节点的标准执行流水线

下面是动态更新商品卡片信息以及动态增加标签属性的示例代码:

// 1. 动态修改纯文本内容
badgeEl.textContent = '已售罄';
priceEl.textContent = '¥0.00';

// 2. 动态修改 HTML 属性
toggleBtn.setAttribute('disabled', 'true');
toggleBtn.setAttribute('aria-disabled', 'true');

// 3. 在内存中创建新节点并挂载到卡片内部
const tag = document.createElement('span');
tag.className = 'status-tag highlight';
tag.textContent = '清仓特惠';

// 挂载到标题元素下方
card.insertBefore(tag, priceEl);

三、动态修改样式的核心方式:style 与 classList

修改元素的视觉表现是 DOM 操作的核心环节。JavaScript 提供了两种主流方式:通过 element.style 直接操作行内样式,或者通过 element.classList 动态切换 CSS 类名。

1. 直接操作内联样式:element.style

通过 element.style.property 可以读取或设置元素的行内 CSS。由于 JavaScript 标识符不能包含减号(-),所有带横杠的 CSS 属性名在 JS 中必须转换为小驼峰命名法(camelCase)。例如:background-color 对应 style.backgroundColor,font-size 对应 style.fontSize。

// 设置单一内联属性
card.style.backgroundColor = '#F2F6FA';
card.style.border = '1px solid #AD8060';
card.style.opacity = '0.7';

// 动态计算数值型场景(如依据进度条百分比)
const currentProgress = 75;
card.style.width = currentProgress + '%';

内联样式的局限在于:它的优先级相当于行内属性,会强行覆盖外部样式表中声明的普通选择器规则;同时多次分别修改 style 属性容易导致代码冗长且不易维护。

2. 操作 CSS 类名:element.classList(业界首选)

classList 是一个只读的 DOMTokenList 对象,它提供了一组简洁的方法来添加、移除和切换类名,使样式的具体呈现仍然保留在 CSS 样式表中,JavaScript 仅负责状态标识的变更,实现了“样式与表现分离”。

  • classList.add('class-name'):添加一个或多个类名(如果已存在则自动忽略)。
  • classList.remove('class-name'):移除指定类名。
  • classList.toggle('class-name'):如果存在则移除,不存在则添加,常用于折叠、展开、切换开关。
  • classList.contains('class-name'):判断元素是否包含某个类名,返回布尔值。
  • classList.replace('old-class', 'new-class'):直接替换类名。
JavaScript DOM操作实例:动态修改HTML元素与样式
图 3:内联样式 style 与类名列表 classList 的机制对比与选型原则
对比维度 element.style element.classList
关注点分离 弱,样式逻辑硬编码在 JS 逻辑中 强,样式规范统一交由外部 CSS 维护
批量更新开销 连续修改多个属性可能诱发多次微重排 单一修改类名,浏览器合并进行单次样式计算
状态回滚便利性 需手动恢复先前的 style 字符串或置空 使用 classList.remove() 即可干净恢复
动态数值支持 支持直接拼接运算数值(如 px、%) 不支持,类名仅代表预设固定的样式规则

四、综合实战:可交互状态切换卡片完整实现

为了将元素定位、文本变更与样式切换贯通起来,本节实现一个具体的“商品详情卡片”交互案例:点击按钮时,动态在“在售正常”与“已下架禁用”两种状态间平滑切换。

1. HTML 结构准备

<!-- 卡片容器 -->
<div id="product-card" class="card">
  <div class="card-header">
    <h3 class="title">高性能企业级云服务器</h3>
    <span class="badge in-stock">在售</span>
  </div>
  <p class="description">8核 16G 内存 / 100G SSD / 独立公网IP</p>
  <div class="card-footer">
    <span id="price-tag" class="price">¥199.00 / 月</span>
    <button id="btn-toggle" class="btn">执行下架</button>
  </div>
</div>

2. CSS 预设样式

/* 基础卡片样式 */
.card {
  width: 360px;
  border: 1px solid #E2E8F0;
  border-radius: 8px;
  padding: 20px;
  background-color: #FFFFFF;
  transition: all 0.3s ease;
}

/* 状态徽章 */
.badge {
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
}
.badge.in-stock {
  background-color: #E6F4EA;
  color: #137333;
}
.badge.out-of-stock {
  background-color: #FCE8E6;
  color: #C5221F;
}

/* 按钮样式 */
.btn {
  padding: 8px 16px;
  border: 1px solid #345E82;
  background-color: #345E82;
  color: #FFFFFF;
  border-radius: 4px;
  cursor: pointer;
}

/* 停用/下架状态(通过 class 批量修改外观) */
.card.is-disabled {
  background-color: #F8FAFC;
  border-color: #CBD5E1;
  opacity: 0.8;
}
.card.is-disabled .title {
  color: #64748B;
}
.card.is-disabled .price {
  text-decoration: line-through;
  color: #94A3B8;
}

3. JavaScript 逻辑装配

// 获取关键 DOM 节点
const card = document.querySelector('#product-card');
const badge = card.querySelector('.badge');
const price = card.querySelector('#price-tag');
const toggleBtn = document.querySelector('#btn-toggle');

// 绑定点击事件,通过 classList 与 textContent 协调更新
toggleBtn.addEventListener('click', () => {
  // 判断当前是否处于禁用状态
  const isDisabled = card.classList.toggle('is-disabled');

  if (isDisabled) {
    // 状态 A -> 状态 B(下架逻辑)
    badge.textContent = '已下架';
    badge.classList.replace('in-stock', 'out-of-stock');
    toggleBtn.textContent = '重新上架';

    // 辅助使用 style 进行动态微调
    toggleBtn.style.backgroundColor = '#AD8060';
    toggleBtn.style.borderColor = '#AD8060';
  } else {
    // 状态 B -> 状态 A(恢复上架)
    badge.textContent = '在售';
    badge.classList.replace('out-of-stock', 'in-stock');
    toggleBtn.textContent = '执行下架';

    // 清理内联覆盖,恢复默认 CSS 主题
    toggleBtn.style.backgroundColor = '';
    toggleBtn.style.borderColor = '';
  }
});

五、DOM 操作规范与避坑指南

在实际生产项目中,不规范的 DOM 操作往往是界面卡顿、内存泄漏以及安全漏洞的根源。在编写 DOM 操作脚本时,应遵循以下关键准则:

JavaScript DOM操作实例:动态修改HTML元素与样式
图 4:DOM 元素定位、内容结构更新与样式管理的综合技术全景总结

1. 避免在循环体中频繁读写 DOM(防御强制同步布局)

当 JavaScript 连续修改样式后紧接着读取几何属性(如 offsetWidth、clientHeight、offsetTop)时,浏览器为了返回准确的数值,会被迫立即中断当前流水线执行一次“同步重排(Reflow)”。如果在 for 循环中频繁出现此种操作,会导致严重的界面掉帧。

// 错误模式:在循环内部一边修改一边读取,诱发多次强制重排
for (let i = 0; i < items.length; i++) {
  items[i].style.width = (container.offsetWidth + 10) + 'px'; 
}

// 推荐做法:在循环外部一次性读取几何尺寸,在循环内纯写
const targetWidth = container.offsetWidth + 10;
for (let i = 0; i < items.length; i++) {
  items[i].style.width = targetWidth + 'px';
}

2. 脚本加载时机保障

如果将 <script> 标签放置在 <head> 中且未标记任何异步属性,脚本执行时下方的 HTML DOM 元素尚未解析完毕,此时调用 querySelector 会返回 null 并抛出 TypeError: Cannot read properties of null。

建议的做法是将脚本标签置于 </body> 结束标签之前,或者为 <script> 标签添加 defer 属性,确保 DOM 完全就绪后再执行操作逻辑:

<!-- 推荐:在 head 引入时添加 defer 属性 -->
<script src="app.js" defer></script>

<!-- 或者在脚本内部监听 DOMContentLoaded 事件 -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    // 安全执行 DOM 查找与绑定
    const card = document.querySelector('#product-card');
  });
</script>

掌握 DOM 查找、内容更新与基于 classList 的样式控制,便足以应对前端绝大多数日常交互需求。在实际编码中保持“先判空后操作”、“分离表现与逻辑”、“优先选择 textContent”的工程规范,能够大幅提升代码的健壮性与可维护性。

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