下载工作台
前端开发入门

DOM 操作与事件

试读上半部分 · 解锁后可读全文

第 9 章 · DOM 操作与事件

本章目标:用 JavaScript 查找、修改、创建页面元素,并响应用户操作。贯穿案例 academy-demo(贤紫技术学院课程门户演示站)中的课程卡片、主题切换与导航交互。

前置:第 8 章 JavaScript 基础语法;建议在 academy-demo/ 目录跟练。


9.1 什么是 DOM

浏览器将 HTML 解析为文档对象模型(DOM)——一棵可编程的树,每个标签是一个节点。JavaScript 通过 DOM API 读写这棵树,实现「页面即数据」的交互。

document
 └── html
      ├── head
      └── body
           ├── header#site-header
           ├── main#course-list
           │    └── article.course-card × N
           └── footer
概念说明academy-demo 示例
文档节点整棵树的根document
元素节点HTML 标签<article class="course-card">
文本节点标签内的文字课程标题字符串
属性节点idclassdata-*data-course-id="fe-09"

9.1.1 查询元素

// academy-demo/js/dom.js
const title = document.getElementById('page-title');
const firstCard = document.querySelector('.course-card');
const allCards = document.querySelectorAll('.course-card');
const navLink = document.querySelector('nav a[href="#courses"]');
方法返回值适用场景注意
getElementById单个或 null唯一 idid 勿重复
querySelector第一个匹配或 null复杂 CSS 选择器性能:避免超深嵌套
querySelectorAll类数组 NodeList批量操作非 Live,后续 DOM 变化不自动更新
getElementsByClassName实时 HTMLCollection老项目兼容动态增删时会「活」更新
行业案例 · 贤紫优选商城:商品列表页曾用 document.getElementsByTagName('div') 遍历筛选,DOM 节点上万时主线程卡顿 200ms+;改为 querySelectorAll('[data-sku]') + 事件委托后,首屏交互延迟降至 30ms 内。

9.2 修改内容、属性与样式

const card = document.querySelector('.course-card');

// 文本:优先 textContent,防 XSS
card.querySelector('h3').textContent = 'DOM 操作与事件';

// HTML:仅信任来源时使用
// card.innerHTML = '<strong>新课</strong>'; // 用户输入禁止直插

// class 与无障碍属性
card.classList.add('featured');
card.classList.toggle('expanded', true);
card.setAttribute('aria-expanded', 'true');

// 行内样式(优先用 CSS 类)
card.style.setProperty('--accent', '#c2185b');

// dataset 读写 data-* 
card.dataset.level = 'beginner'; // data-level
console.log(card.dataset.level);
API作用推荐度
textContent纯文本替换★★★ 默认
innerHTML解析 HTML 片段★ 仅静态模板
classList增删切换类名★★★
setAttribute通用属性★★★
element.style行内样式★ 特例覆盖

9.3 创建、插入与移除节点

function createCourseCard({ id, title, desc, hours }) {
  const article = document.createElement('article');
  article.className = 'course-card';
  article.dataset.courseId = id;

  const h3 = document.createElement('h3');
  h3.textContent = title;

  const p = document.createElement('p');
  p.className = 'desc';
  p.textContent = desc;

  const meta = document.createElement('span');
  meta.className = 'meta';
  meta.textContent = `${hours} 课时`;

  article.append(h3, p, meta);
  return article;
}

const grid = document.querySelector('#course-grid');
grid.appendChild(createCourseCard({
  id: 'fe-09',
  title: 'DOM 操作与事件',
  desc: '查找、修改、监听用户操作',
  hours: 4,
}));

批量渲染(模板字符串 + 转义):

function escapeHtml(str) {
  return String(str)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

function renderCourses(courses) {
  const html = courses.map(c => `
    <article class="course-card" data-course-id="${escapeHtml(c.id)}">
      <h3>${escapeHtml(c.title)}</h3>
      <p class="desc">${escapeHtml(c.desc)}</p>
    </article>
  `).join('');
  document.querySelector('#course-grid').innerHTML = html;
}
操作API说明
末尾追加appendChild / appendappend 可一次多个
前面插入insertBefore / prepend
替换replaceChild / replaceWith
移除removeChild / remove子节点用 remove 更简洁
克隆cloneNode(deep)深拷贝含子树

9.4 事件机制

9.4.1 注册与移除

const themeBtn = document.querySelector('#theme-toggle');

function onThemeClick(e) {
  document.body.classList.toggle('theme-dark');
  localStorage.setItem('theme', document.body.classList.contains('theme-dark') ? 'dark' : 'light');
}

themeBtn.addEventListener('click', onThemeClick);
// 不再需要时:themeBtn.removeEventListener('click', onThemeClick);

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。