【JavaScript-Day 39】从零到一,动态构建交互式网页的 DOM 节点操作秘籍

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

01-【JavaScript-Day 1】从零开始:全面了解 JavaScript 是什么、为什么学以及它与 Java 的区别
02-【JavaScript-Day 2】开启 JS 之旅:从浏览器控制台到 <script> 标签的 Hello World 实践
03-【JavaScript-Day 3】掌握JS语法规则:语句、分号、注释与大小写敏感详解
04-【JavaScript-Day 4】var 完全指南:掌握变量声明、作用域及提升
05-【JavaScript-Day 5】告别 var 陷阱:深入理解 letconst 的妙用
06-【JavaScript-Day 6】从零到精通:JavaScript 原始类型 String, Number, Boolean, Null, Undefined, Symbol, BigInt 详解
07-【JavaScript-Day 7】全面解析 Number 与 String:JS 数据核心操作指南
08-【JavaScript-Day 8】告别混淆:一文彻底搞懂 JavaScript 的 Boolean、null 和 undefined
09-【JavaScript-Day 9】从基础到进阶:掌握 JavaScript 核心运算符之算术与赋值篇
10-【JavaScript-Day 10】掌握代码决策核心:详解比较、逻辑与三元运算符
11-【JavaScript-Day 11】避坑指南!深入理解JavaScript隐式和显式类型转换
12-【JavaScript-Day 12】掌握程序流程:深入解析 if…else 条件语句
13-【JavaScript-Day 13】告别冗长if-else:精通switch语句,让代码清爽高效!
14-【JavaScript-Day 14】玩转 for 循环:从基础语法到遍历数组实战
15-【JavaScript-Day 15】深入解析 while 与 do…while 循环:满足条件的重复执行
16-【JavaScript-Day 16】函数探秘:代码复用的基石——声明、表达式与调用详解
17-【JavaScript-Day 17】函数的核心出口:深入解析 return 语句的奥秘
18-【JavaScript-Day 18】揭秘变量的“隐形边界”:深入理解全局与函数作用域
19-【JavaScript-Day 19】深入理解 JavaScript 作用域:块级、词法及 Hoisting 机制
20-【JavaScript-Day 20】揭秘函数的“记忆”:深入浅出理解闭包(Closure)
21-【JavaScript-Day 21】闭包实战:从模块化到内存管理,高级技巧全解析
22-【JavaScript-Day 22】告别 function 关键字?ES6 箭头函数 (=>) 深度解析
23-【JavaScript-Day 23】告别繁琐的参数处理:玩转 ES6 默认参数与剩余参数
24-【JavaScript-Day 24】从零到一,精通 JavaScript 对象:创建、访问与操作
25-【JavaScript-Day 25】深入探索:使用 for...in 循环遍历 JavaScript 对象属性
26-【JavaScript-Day 26】零基础掌握JavaScript数组:轻松理解创建、索引、长度和多维结构
27-【JavaScript-Day 27】玩转数组:push, pop, slice, splice 等方法详解与实战
28-【JavaScript-Day 28】告别繁琐循环:forEach, map, filter 数组遍历三剑客详解
29-【JavaScript-Day 29】数组迭代进阶:掌握 reduce、find、some 等高阶遍历方法
30-【JavaScript-Day 30】ES6新特性:Set与Map,让你的数据管理更高效!
31-【JavaScript-Day 31】对象的“蓝图”详解:构造函数、newinstanceof 完全指南
32-【JavaScript-Day 32】深入理解 prototype、__proto__ 与原型链的奥秘
33-【JavaScript-Day 33】深入浅出 ES6 Class:从入门到精通面向对象新姿势
34-【JavaScript-Day 34】前后端数据交互的通用语:深入解析JSON
35-【JavaScript-Day 35】从 window 到 location,一文掌握浏览器对象模型 BOM
36-【JavaScript-Day 36】前端基石:深入理解 DOM 并精通五大元素选择器
37-【JavaScript-Day 37】在 DOM 树中“行走”:节点遍历
38-【JavaScript-Day 38】JS操控网页外观:从innerHTML到classList的全方位指南
39-【JavaScript-Day 39】从零到一,动态构建交互式网页的 DOM 节点操作秘籍



前言

大家好,欢迎来到 JavaScript 学习系列的第 39 篇!在前几篇文章中,我们学会了如何像侦探一样在 DOM 树中查找元素(querySelector),以及如何像化妆师一样修改它们的内容和样式(innerHTML, classList)。然而,一个真正动态和充满活力的网页,不仅仅是修改现有内容,更需要能够从无到有地创造内容,或者移除不再需要的元素

想象一下你的社交媒体信息流、在线商城的商品列表、或是实时更新的评论区。这些功能的核心,都离不开今天我们要学习的知识:动态创建、添加、移除、替换和克隆 DOM 节点。这就像是给了你一盒无限的乐高积木,让你随心所欲地搭建和改造你的网页世界。

本文将带你彻底掌握这些 DOM 操作的“魔法”,通过清晰的讲解、丰富的示例和实战演练,让你具备构建复杂交互界面的能力。准备好了吗?让我们开始搭建吧!


一、创建新节点:从无到有

在将新内容展示到页面之前,我们首先需要在 JavaScript 的世界里将它“制造”出来。这些新创建的节点暂时存在于内存中,像一个等待拼接到位的乐高零件,等待我们一声令下将它安放到 DOM 树的指定位置。

1.1 创造元素节点:document.createElement()

元素节点是构成网页结构的基本骨架,如 <div><p><li><a> 等。document.createElement() 方法就是我们的“元素制造机”。

1.1.1 方法解析

该方法接受一个参数,即你想要创建的元素的标签名 (tagName),并返回一个代表该新元素的 Element 对象。

  • 语法const newElement = document.createElement('tagName');

1.1.2 代码示例

让我们来创建几个常见的元素:

// 创建一个新的 <div> 元素
const newDiv = document.createElement('div');

// 创建一个新的段落 <p> 元素
const newParagraph = document.createElement('p');

// 创建一个新的列表项 <li> 元素
const newListItem = document.createElement('li');

console.log(newDiv);       // 输出: <div></div>
console.log(newParagraph); // 输出: <p></p>
console.log(newListItem);  // 输出: <li></li>

注意:此时,这些元素仅仅存在于 newDivnewParagraph 这些变量中,它们是“孤儿”节点,尚未被添加到页面的任何地方,因此在浏览器窗口中是不可见的。

1.2 创造文本节点:document.createTextNode()

光有元素的骨架还不够,我们还需要往里面填充内容。文本节点就是用来承载这些文字信息的。

1.2.1 方法解析

document.createTextNode() 用于创建一个只包含指定文本的 Text 节点。

  • 语法const newTextNode = document.createTextNode('your text content');

1.2.2 组合使用

通常,我们会将文本节点放入元素节点中,这才是完整的“零件”。

// 1. 创建一个 <p> 元素节点
const myParagraph = document.createElement('p');

// 2. 创建一个文本节点
const myText = document.createTextNode('这是动态创建的一段话。');

// 3. 将文本节点作为子节点添加到 <p> 元素中 (下一步会详解)
myParagraph.appendChild(myText);

console.log(myParagraph); // 输出: <p>这是动态创建的一段话。</p>

现在,我们有了一个包含了内容的、完整的 <p> 元素,只差最后一步——将它“安装”到页面上了。


二、将节点添加到 DOM 树:展示你的创作

创建好节点后,我们需要用 appendChild()insertBefore() 两个方法,将它们挂载到 DOM 树中,使其真正地在页面上渲染出来。

2.1 追加到末尾:appendChild()

appendChild() 是最常用的添加方法,它会将一个节点添加到指定父节点的子节点列表的末尾

2.1.1 方法解析

就像在队伍末尾排上一个人,appendChild() 将新节点安插在父元素所有已有子元素的后面。

  • 语法parentNode.appendChild(childNode);
  • parentNode:要插入新节点的父元素。
  • childNode:要被插入的节点(我们刚刚创建的)。

2.1.2 完整流程与示例

假设我们有如下 HTML 结构:

<div id="container">
    <h2>评论区</h2>
    </div>

现在,我们用 JavaScript 创建一条新评论并添加进去:

// 1. 获取父节点
const container = document.getElementById('container');

// 2. 创建新元素节点 <p>
const newComment = document.createElement('p');

// 3. 给新元素设置内容 (使用 textContent 更简洁)
newComment.textContent = '这是一个非常有见地的评论!';

// 4. 将新创建的 <p> 元素追加到 container 中
container.appendChild(newComment);

执行后,页面的 DOM 结构会变成:

<div id="container">
    <h2>评论区</h2>
    <p>这是一个非常有见地的评论!</p> </div>

2.2 精准插入:insertBefore()

如果你想更精确地控制新节点的插入位置,比如插入到某个特定元素之前,那么 insertBefore() 就是你的不二之选。

2.2.1 方法解析

此方法将一个节点插入到参考节点之前。

  • 语法parentNode.insertBefore(newNode, referenceNode);
  • parentNode:父元素。
  • newNode:要插入的新节点。
  • referenceNode:一个已存在的子节点,新节点将被插入到它前面。

2.2.2 应用场景与示例

假设我们有一个有序列表,希望在特定位置插入新项目。

HTML 结构:

<ul id="taskList">
    <li id="task-1">完成报告</li>
    <li id="task-3">回复邮件</li>
</ul>

我们想在“回复邮件”前,插入一项“开会”的任务。

// 1. 获取父节点和参考节点
const taskList = document.getElementById('taskList');
const task3 = document.getElementById('task-3');

// 2. 创建新列表项
const newTask = document.createElement('li');
newTask.textContent = '开会'; // 设置内容

// 3. 在 task-3 之前插入 newTask
taskList.insertBefore(newTask, task3);

最终效果:

<ul id="taskList">
    <li id="task-1">完成报告</li>
    <li>开会</li> <li id="task-3">回复邮件</li>
</ul>
(1)特殊情况

如果 insertBefore() 的第二个参数 referenceNodenull,那么它的行为将和 appendChild() 完全一样,即把新节点添加到末尾。

// 这行代码等同于 taskList.appendChild(newTask);
taskList.insertBefore(newTask, null); 

三、从 DOM 树中移除节点:清理与更新

当某些内容不再需要时,我们就需要将其从页面上移除,以保持界面的整洁和信息的准确性。

3.1 指定父子关系移除:removeChild()

removeChild() 是传统的节点移除方法,它需要通过父节点来移除其下的某个子节点。

3.1.1 方法解析

这个方法从 DOM 中移除一个子节点,并返回被移除的节点。

  • 语法const removedChild = parentNode.removeChild(childNode);
  • parentNode:要移除节点的父元素。
  • childNode:要被移除的子节点。

核心要点:你必须先获取到父元素的引用,才能调用这个方法来删除它的孩子。

3.1.2 示例:删除一个任务

继续上面的任务列表例子,现在我们要删除“完成报告”这一项。

// 1. 获取父节点和要删除的子节点
const taskList = document.getElementById('taskList');
const task1 = document.getElementById('task-1');

// 2. 从父节点中移除子节点
if (task1) { // 确保节点存在再删除
    taskList.removeChild(task1);
}

3.1.3 常见技巧:只知道子节点时如何移除?

在很多场景下,我们可能只直接获取到了要删除的节点本身(比如用户点击了“删除”按钮)。这时,可以通过其 parentNode 属性找到父节点,然后执行删除。

const nodeToRemove = document.getElementById('task-1');

if (nodeToRemove) {
    // 找到它的父节点,然后命令父节点移除它
    nodeToRemove.parentNode.removeChild(nodeToRemove);
}

3.2 自我移除(现代方法):remove()

为了简化操作,现代浏览器提供了一个更直观的方法:remove()。它可以让一个元素“自我毁灭”,无需先获取其父节点。

3.2.1 方法解析

直接在要删除的元素上调用此方法即可。

  • 语法elementToRemove.remove();

3.2.2 示例对比

使用 remove() 来删除 task-1 会变得异常简单:

const nodeToRemove = document.getElementById('task-1');

if (nodeToRemove) {
    nodeToRemove.remove(); // 直接移除,代码更简洁!
}

对比remove() 可读性更高,代码更短,是目前推荐的首选方案(它在现代浏览器中得到了广泛支持,无需担心兼容性问题)。


四、替换与克隆:高级操作

除了增删,DOM 还提供了替换和复制节点的能力,让动态操作更加灵活。

4.1 更新换代:replaceChild()

此方法用一个新的节点替换掉父元素下的一个已有子节点。

4.1.1 方法解析

  • 语法parentNode.replaceChild(newNode, oldNode);
  • parentNode:父元素。
  • newNode:用来替换的新节点。
  • oldNode:将被替换掉的旧节点。

4.1.2 示例:更新任务内容

假设我们要将“开会”这个任务替换为“代码审查”。

// 假设 'meetingTask' 是对“开会”那个 <li> 的引用
const taskList = document.getElementById('taskList');
const meetingTask = taskList.children[1]; // 假设它是第二个子元素

// 1. 创建新节点
const reviewTask = document.createElement('li');
reviewTask.textContent = '代码审查';

// 2. 执行替换
if (meetingTask) {
    taskList.replaceChild(reviewTask, meetingTask);
}

4.2 复制节点:cloneNode()

cloneNode() 方法可以创建一个节点的精确副本。这在需要重复生成相似结构时非常有用。

4.2.1 方法解析

  • 语法const clonedNode = originalNode.cloneNode(deep);
  • deep (布尔值):这是一个至关重要的参数。
    • true (深克隆):复制节点及其所有后代(子节点、孙节点等)。这是最常用的方式。
    • false (浅克隆):只复制节点本身,不包括它的任何子节点。

4.2.2 示例:深克隆 vs 浅克隆

HTML 结构:

<div id="source">
    <p>这是一个<span>模板</span></p>
</div>
<div id="destination"></div>

JavaScript:

const source = document.getElementById('source');
const destination = document.getElementById('destination');

// 1. 深克隆 (true)
const deepClone = source.cloneNode(true);
destination.appendChild(deepClone); 
// 结果: destination 中会出现一个完整的、包含 <p> 和 <span> 的 <div>

// 2. 浅克隆 (false)
const shallowClone = source.cloneNode(false);
console.log(shallowClone); // 输出: <div id="source"></div> (是空的!)

对比表格

克隆类型cloneNode() 参数结果
浅克隆false (或不填)只复制节点本身,其 childNodes 为空。
深克隆true复制节点及其所有子孙节点,结构完全保留。

重要提醒cloneNode() 只复制 DOM 属性和结构,通过 addEventListener() 添加的事件监听器是不会被复制的。如果克隆的节点也需要响应事件,你需要为克隆体重新绑定事件。


五、实战演练:动态生成一个评论列表

理论结合实践是最好的学习方式。让我们综合运用今天所学的知识,制作一个简单的“发表评论”功能。

HTML 骨架:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>评论列表</title>
    <style>
        body { font-family: sans-serif; }
        #comment-container { max-width: 500px; margin: 20px auto; padding: 15px; border: 1px solid #ccc; border-radius: 8px; }
        input[type="text"] { width: 70%; padding: 8px; }
        button { padding: 8px 12px; }
        ul { list-style: none; padding-left: 0; }
        li { background-color: #f0f0f0; padding: 10px; margin-top: 10px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; }
        .delete-btn { cursor: pointer; color: red; font-weight: bold; }
    </style>
</head>
<body>

    <div id="comment-container">
        <h3>发表你的看法</h3>
        <div>
            <input type="text" id="comment-input" placeholder="输入评论内容...">
            <button id="submit-btn">发布</button>
        </div>
        <ul id="comment-list">
            </ul>
    </div>

    <script>
        // JavaScript 代码将写在这里
    </script>

</body>
</html>

JavaScript 逻辑:

// 1. 获取所有需要的 DOM 元素
const submitBtn = document.getElementById('submit-btn');
const commentInput = document.getElementById('comment-input');
const commentList = document.getElementById('comment-list');

// 2. 为“发布”按钮添加点击事件监听
submitBtn.addEventListener('click', function() {
    const commentText = commentInput.value.trim(); // 获取输入内容并去除首尾空格

    // 3. 校验输入是否为空
    if (commentText === '') {
        alert('评论内容不能为空!');
        return; // 结束函数执行
    }

    // 4. 创建新的评论项 <li>
    const newListItem = document.createElement('li');
    
    // 5. 创建文本内容和删除按钮
    const textSpan = document.createElement('span');
    textSpan.textContent = commentText;

    const deleteBtn = document.createElement('span');
    deleteBtn.textContent = '删除';
    deleteBtn.className = 'delete-btn'; // 添加样式类

    // 为删除按钮添加点击事件
    deleteBtn.addEventListener('click', function() {
        // 使用 remove() 方法轻松删除该评论项
        newListItem.remove(); 
    });

    // 6. 将文本和删除按钮组装到 <li> 中
    newListItem.appendChild(textSpan);
    newListItem.appendChild(deleteBtn);

    // 7. 将新的 <li> 追加到评论列表 <ul> 的开头(使用 insertBefore)
    commentList.insertBefore(newListItem, commentList.firstChild);

    // 8. 清空输入框,方便下次输入
    commentInput.value = '';
});

在这个例子中,我们综合运用了 createElement, textContent, appendChild, insertBefore, remove 以及事件监听,完美实现了一个动态、交互式的评论列表功能。


六、总结

恭喜你,你已经掌握了像操控乐高积木一样动态构建网页的核心技能!回顾一下今天的关键知识点:

  1. 创建节点

    • document.createElement('tagName'):用于创建元素节点,是构建网页结构的基石。
    • document.createTextNode('text'):用于创建文本节点,为元素填充内容。
  2. 添加节点

    • parentNode.appendChild(childNode):将节点添加到父元素的末尾,是最常用的添加方式。
    • parentNode.insertBefore(newNode, referenceNode):将新节点插入到指定参考节点之前,实现精准定位。
  3. 移除节点

    • parentNode.removeChild(childNode):传统的移除方式,需要通过父节点操作。
    • element.remove():现代、简洁的移除方式,直接在元素自身调用即可,强烈推荐
  4. 替换节点

    • parentNode.replaceChild(newNode, oldNode):用一个新节点替换掉一个旧节点。
  5. 克隆节点

    • node.cloneNode(isDeep):复制一个节点。参数为 true 时进行深克隆(复制所有后代),为 false 时进行浅克隆(只复制节点本身)。

熟练掌握这些 DOM 操作方法,是从静态页面开发者迈向前端交互工程师的关键一步。现在,就去打开你的代码编辑器,尝试为你自己的项目添加一些动态的生命力吧!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴师兄大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值