JavaScript之DOM操作与事件处理详解

JavaScript之DOM操作与事件处理详解

    • 一、DOM基础:理解文档对象模型
    • 二、DOM元素的获取与访问
      • 2.1 基础获取方法
      • 2.2 集合的区别与注意事项
    • 三、DOM元素的创建与修改
      • 3.1 创建与插入元素
      • 3.2 修改元素属性与样式
        • 3.2.1 属性操作
        • 3.2.2 样式操作
      • 3.3 元素内容的修改
    • 四、DOM元素的删除与替换
      • 4.1 删除元素
      • 4.2 替换元素
    • 五、事件处理:实现页面交互
      • 5.1 事件绑定的三种方式
        • 5.1.1 HTML属性绑定(不推荐)
        • 5.1.2 DOM属性绑定
        • 5.1.3 `addEventListener()`(推荐)
      • 5.2 事件对象(Event)
      • 5.3 事件流:捕获与冒泡
      • 5.4 事件委托:高效处理动态元素
    • 六、常见问题与避坑指南
      • 6.1 DOM操作的性能问题
      • 6.2 事件解绑失败
      • 6.3 `innerHTML`的安全风险

DOM(Document Object Model,文档对象模型)是JavaScript操作网页的接口,而事件处理则是实现交互的核心。本文我将系统梳理DOM的核心操作(元素获取、创建、修改、删除等),深入解析事件处理机制(绑定方式、事件对象、事件委托等),并通过实例演示其应用,帮你`构建完整的DOM交互知识体系。

一、DOM基础:理解文档对象模型

DOM将HTML文档抽象为树形结构,每个节点(元素、文本、属性等)都是树的组成部分。常见节点类型包括:

  • 元素节点(Element):如

    等标签

  • 文本节点(Text):标签内的文本内容
  • 属性节点(Attribute):元素的属性(如classid
  • 文档节点(Document):整个文档的根节点(document

<div id="container">
  <p class="text">Hello DOMp>
div>

对应的DOM树结构:

document(文档节点)
  └── html(元素节点)
      └── body(元素节点)
          └── div#container(元素节点)
              └── p.text(元素节点)
                  └── "Hello DOM"(文本节点)

二、DOM元素的获取与访问

操作DOM的第一步是获取目标元素,JavaScript提供了多种获取元素的方法,适用于不同场景。

2.1 基础获取方法

方法 说明 示例
getElementById() 通过id获取唯一元素(大小写敏感) document.getElementById("container")
getElementsByClassName() 通过class获取元素集合(HTMLCollection) document.getElementsByClassName("text")
getElementsByTagName() 通过标签名获取元素集合(HTMLCollection) document.getElementsByTagName("p")
querySelector() 通过CSS选择器获取第一个匹配元素 document.querySelector("#container p")
querySelectorAll() 通过CSS选择器获取所有匹配元素(NodeList) document.querySelectorAll(".text")

示例代码

// 获取元素
const container = document.getElementById("container");
const textElements = document.getElementsByClassName("text"); // 集合(类数组)
const pTags = document.getElementsByTagName("p"); // 集合
const firstP = document.querySelector("p"); // 第一个p元素
const allPs = document.querySelectorAll("div p"); // 所有div内的p元素

// 访问集合中的元素(类数组需通过索引访问)
console.log(textElements[0]); // 

Hello DOM

console.log(allPs[0]); // 第一个匹配的p元素

2.2 集合的区别与注意事项

  • HTMLCollection(如getElementsByClassName()返回值):实时更新(DOM变化时自动同步),仅包含元素节点;
  • NodeList(如querySelectorAll()返回值):静态集合(DOM变化时不更新),可能包含文本节点、注释节点等;
  • 类数组转换为数组:用Array.from()或扩展运算符[...collection],方便使用数组方法(如forEach)。
const htmlColl = document.getElementsByClassName("text");
const nodeList = document.querySelectorAll(".text");

// 转换为数组
const arr1 = Array.from(htmlColl);
const arr2 = [...nodeList];
arr1.forEach(el => console.log(el)); // 遍历元素

三、DOM元素的创建与修改

获取元素后,常需要创建新元素、修改元素属性或内容,实现动态页面效果。

3.1 创建与插入元素

步骤:创建元素 → 设置属性/内容 → 插入到DOM树。

// 1. 创建元素
const newP = document.createElement("p");

// 2. 设置属性和内容
newP.className = "new-text"; // 设置class
newP.id = "new-p"; // 设置id
newP.textContent = "新创建的段落"; // 设置文本内容(推荐,安全)
// 或设置HTML内容(注意XSS风险)
newP.innerHTML = "带标签的内容";

// 3. 插入到DOM树
const container = document.getElementById("container");
container.appendChild(newP); // 插入到container末尾

// 插入到指定元素前
const existingP = container.querySelector("p");
container.insertBefore(newP, existingP); // 插入到existingP前面

3.2 修改元素属性与样式

3.2.1 属性操作
const link = document.querySelector("a");

// 读取属性
console.log(link.href); // 获取href属性值(完整URL)
console.log(link.getAttribute("href")); // 获取原始属性值(如"#top")

// 修改属性
link.href = "https://example.com"; // 修改href
link.setAttribute("title", "示例链接"); // 设置title属性

// 移除属性
link.removeAttribute("class");
3.2.2 样式操作
const box = document.querySelector(".box");

// 方式1:通过style属性修改行内样式(驼峰命名)
box.style.width = "200px";
box.style.backgroundColor = "red"; // 对应CSS的background-color

// 方式2:通过classList操作类名(推荐,分离样式与逻辑)
box.classList.add("active"); // 添加类
box.classList.remove("old"); // 移除类
box.classList.toggle("hidden"); // 切换类(存在则移除,不存在则添加)
box.classList.contains("active"); // 判断是否包含类(返回布尔值)

3.3 元素内容的修改

方法/属性 说明 安全性
textContent 设置或获取元素的文本内容(不含标签) 安全(自动转义HTML)
innerHTML 设置或获取元素的HTML内容(含标签) 不安全(可能导致XSS)
innerText 类似textContent,但受样式影响(如隐藏文本不显示) 安全

示例

const p = document.querySelector("p");

p.textContent = "加粗文本"; // 显示为纯文本:加粗文本
p.innerHTML = "加粗文本"; // 显示为加粗文本(解析HTML)

四、DOM元素的删除与替换

移除或替换不需要的元素,可保持DOM结构的整洁。

4.1 删除元素

const toRemove = document.querySelector("#old-element");
// 方式1:通过父元素删除
toRemove.parentNode.removeChild(toRemove);

// 方式2:直接删除(ES6+)
toRemove.remove();

4.2 替换元素

const oldElement = document.querySelector("#old");
const newElement = document.createElement("div");
newElement.textContent = "新元素";

// 用新元素替换旧元素
oldElement.parentNode.replaceChild(newElement, oldElement);

五、事件处理:实现页面交互

事件是用户与页面交互的桥梁(如点击、输入、滚动等),事件处理是实现动态效果的核心。

5.1 事件绑定的三种方式

5.1.1 HTML属性绑定(不推荐)

直接在HTML标签中通过on前缀属性绑定事件,耦合性高,不利于维护。

<button onclick="handleClick()">点击我button>
<script>
  function handleClick() {
    alert("按钮被点击");
  }
script>
5.1.2 DOM属性绑定

通过元素的事件属性绑定函数,简单直观,但只能绑定一个事件处理函数。

const button = document.querySelector("button");
// 绑定事件
button.onclick = function() {
  console.log("按钮被点击");
};
// 解绑事件
button.onclick = null;
5.1.3 addEventListener()(推荐)

现代事件绑定方式,支持绑定多个处理函数,可指定事件捕获/冒泡阶段,功能最强大。

const button = document.querySelector("button");

// 绑定事件(事件类型,处理函数,是否在捕获阶段触发)
button.addEventListener("click", function() {
  console.log("点击事件1");
});

// 绑定第二个处理函数
button.addEventListener("click", handleClick);

function handleClick() {
  console.log("点击事件2");
}

// 解绑事件(需使用具名函数)
button.removeEventListener("click", handleClick);

优势

  • 可绑定多个处理函数(按绑定顺序执行);
  • 支持事件捕获与冒泡的控制(第三个参数useCapture,默认false为冒泡阶段);
  • 可解绑指定的处理函数(需传入与绑定相同的函数引用)。

5.2 事件对象(Event)

事件处理函数被触发时,会自动接收一个事件对象event),包含事件相关信息(如触发元素、坐标等)。

document.querySelector("button").addEventListener("click", function(event) {
  // 事件对象属性
  console.log(event.target); // 触发事件的元素(

常用方法

  • event.preventDefault():阻止事件的默认行为(如链接跳转、表单提交);
  • event.stopPropagation():阻止事件冒泡(父元素不再接收该事件);
  • event.stopImmediatePropagation():阻止事件冒泡且阻止当前元素的其他处理函数执行。

示例:阻止链接跳转

document.querySelector("a").addEventListener("click", function(event) {
  event.preventDefault(); // 阻止默认跳转行为
  console.log("链接被点击,但不跳转");
});

5.3 事件流:捕获与冒泡

事件触发时会经历捕获阶段(从文档根节点到目标元素)和冒泡阶段(从目标元素回到根节点),称为事件流。

<div id="outer">
  <div id="inner">点击我div>
div>
// 捕获阶段触发(useCapture: true)
outer.addEventListener("click", () => console.log("outer捕获"), true);
inner.addEventListener("click", () => console.log("inner捕获"), true);

// 冒泡阶段触发(useCapture: false,默认)
outer.addEventListener("click", () => console.log("outer冒泡"), false);
inner.addEventListener("click", () => console.log("inner冒泡"), false);

点击inner元素时,输出顺序:

outer捕获 → inner捕获 → inner冒泡 → outer冒泡

应用:利用冒泡机制实现事件委托(见5.4节)。

5.4 事件委托:高效处理动态元素

事件委托利用事件冒泡,将子元素的事件委托给父元素处理,适用于动态生成的元素(无需重复绑定事件)。

场景:列表项动态添加,点击列表项需要触发事件。

<ul id="list">
  <li>项目1li>
  <li>项目2li>
ul>
<button id="addItem">添加项目button>
const list = document.getElementById("list");

// 事件委托:将li的点击事件委托给ul
list.addEventListener("click", function(event) {
  // 判断触发事件的是否为li元素
  if (event.target.tagName === "LI") {
    console.log("点击了:", event.target.textContent);
  }
});

// 动态添加li元素(无需重新绑定事件)
document.getElementById("addItem").addEventListener("click", function() {
  const newLi = document.createElement("li");
  newLi.textContent = `项目${list.children.length + 1}`;
  list.appendChild(newLi);
});

优势

  • 减少事件绑定次数,提升性能(尤其对于大量子元素);
  • 自动支持动态添加的元素,无需额外绑定;
  • 简化代码,便于维护。

六、常见问题与避坑指南

6.1 DOM操作的性能问题

频繁操作DOM会导致浏览器频繁重绘(Repaint)和重排(Reflow),影响性能。

解决方案

  • 批量操作:通过文档片段(DocumentFragment)一次性插入多个元素;
  • 临时隐藏:操作前隐藏元素(display: none),操作后显示;
  • 避免频繁查询:将获取的元素存储在变量中,避免重复查询DOM。
// 优化前:频繁插入元素(多次重排)
const list = document.getElementById("list");
for (let i = 0; i < 100; i++) {
  const li = document.createElement("li");
  li.textContent = `项目${i}`;
  list.appendChild(li); // 每次循环都触发重排
}

// 优化后:用DocumentFragment批量插入
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const li = document.createElement("li");
  li.textContent = `项目${i}`;
  fragment.appendChild(li); // 先添加到文档片段(不触发重排)
}
list.appendChild(fragment); // 一次性插入(仅一次重排)

6.2 事件解绑失败

使用addEventListener()绑定的匿名函数无法解绑,因解绑需要相同的函数引用。

const btn = document.querySelector("button");

// 错误:匿名函数无法解绑
btn.addEventListener("click", () => console.log("点击"));
btn.removeEventListener("click", () => console.log("点击")); // 无效

// 正确:使用具名函数
function handleClick() {
  console.log("点击");
}
btn.addEventListener("click", handleClick);
btn.removeEventListener("click", handleClick); // 有效

6.3 innerHTML的安全风险

innerHTML会解析HTML标签,若内容包含用户输入,可能导致XSS攻击(跨站脚本)。

const userInput = '';
// 危险:直接插入用户输入
element.innerHTML = userInput; // 会执行onerror中的代码

// 安全:使用textContent(自动转义HTML)
element.textContent = userInput; // 显示为纯文本,无安全风险

总结:DOM操作与事件处理的核心要点:

  1. DOM操作
  • 元素获取:优先使用querySelector()/querySelectorAll(),灵活支持CSS选择器;
  • 元素修改:textContent安全修改文本,classList操作类名更便捷;
  • 性能优化:批量操作DOM时使用DocumentFragment,减少重排重绘。
  1. 事件处理
  • 绑定方式:优先使用addEventListener(),支持多处理函数和事件流控制;
  • 事件对象:利用event.target获取触发元素,preventDefault()阻止默认行为;
  • 事件委托:通过父元素代理子元素事件,高效处理动态元素。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

你可能感兴趣的:(JavaScript,javascript)