news 2026/9/27 10:21:57

The JavaScript Way:用 JavaScript 修改网页结构、内容与样式的完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
The JavaScript Way:用 JavaScript 修改网页结构、内容与样式的完整实战指南
  • 教程
  • 文档

【免费下载链接】thejsway

The JavaScript Way book

项目地址:https://gitcode.com/gh_mirrors/th/thejsway
点击查看免费下载

本篇对应《The JavaScript Way》第 15 章(Create interactive web pages 系列第 4 篇)。浏览器加载完 HTML 后,JavaScript 依然可以通过 DOM API 继续"改造"页面:修改已有元素的内容与样式、动态创建并插入新节点、替换或删除旧节点。读完本文,你将掌握innerHTML、textContent、classList、setAttribute()、createElement()、appendChild()、insertBefore()、insertAdjacentHTML()、replaceChild()、removeChild()、style与getComputedStyle()等一整套 DOM 修改 API,并能在真实页面中写出结构清晰、性能友好的动态更新代码。

本章导读与前置知识

在开始"修改"之前,先回顾《The JavaScript Way》的 DOM 学习路线,这对理解本篇文章的位置很有帮助:

  • 第 13 章 介绍了 DOM 树的基本概念:页面是一棵由元素节点与文本节点构成的树,document变量指向树根,childNodes、parentNode、nodeType等属性负责逐节点遍历;
  • 第 14 章 解决了"怎么快速找到要操作的元素"的问题:getElementById()、getElementsByTagName()、getElementsByClassName()、querySelector()、querySelectorAll()等选择方法(其中 querySelector/querySelectorAll 使用 CSS 选择器);
  • 本章则聚焦**"找到之后如何改"**:无论是修改已有元素的内容、属性、类名与样式,还是凭空创建一个新元素插入页面,都属于 DOM 修改(Modify page structure)的范畴;
  • 后续 第 16 章 将在此基础上引入事件监听(如addEventListener()),让页面能响应用户点击等操作,动态修改的技术基础正是本章内容。

示例页面:贯穿全章的编程语言列表

本章绝大部分示例都基于下面这段 HTML。它包含一个标题和一个<ul>无序列表,列表里有 C++、Java、C#、PHP 四种语言:

<h3 class="beginning">Some languages</h3> <div id="content"> <ul id="languages"> <li id="cpp">C++</li> <li id="java">Java</li> <li id="csharp">C#</li> <li id="php">PHP</li> </ul> </div>

你可以把这段代码保存为 HTML 文件,在浏览器中打开并打开开发者工具的控制台(Console),逐条运行后面的 JavaScript 示例观察页面变化——注意示例之间可能存在叠加效果,建议按顺序执行或刷新页面后再试。

修改已有元素

上一章学到的 DOM 遍历与选择属性/方法,同样可以用来更新页面中的元素。

用 innerHTML 修改 HTML 内容

innerHTML属性可以读取或改写元素内部的 HTML 内容。向列表追加一个新条目,可以用+=运算符拼接 HTML 字符串——下面这行代码先通过getElementById("languages")选中<ul>,再在列表末尾追加一个<li>:

// Modifying an HTML element: adding an <li> document.getElementById("languages").innerHTML += '<li id="c">C</li>';

innerHTML最常见的另一个用途是清空内容——把它赋值为空字符串即可删除元素内部所有子节点:

// Delete the HTML content of the list, replacing it with nothing document.getElementById("languages").innerHTML = "";

在继续之前,请把这行代码从你的程序中移除,否则列表就空了,后续示例将无处施展!

!!! tip "建议"

使用 `innerHTML` 时,HTML 内容是以**字符串**形式写入的。为了保持代码可读、避免拼接出错,建议只在改动**较小**的内容时使用 `innerHTML`。下面你会看到更灵活、更安全的解决方案。

值得一提的是,上一章已经介绍过innerHTML的读取能力——第 14 章用它输出元素内部的 HTML 源码;本章则演示了它的写入能力,读写两端合起来才算完整掌握该属性。另外需要留意:innerHTML不是 W3C DOM 规范的一部分(由微软提出),但如今所有主流浏览器都支持它。

用 textContent 修改文本内容

与innerHTML处理 HTML 字符串不同,textContent属性用于修改元素的纯文本内容。下面的代码选中页面第一个<h3>标题,在其现有文本后追加一段文字:

// Modify the title's text content document.querySelector("h3").textContent += " for programming";

执行后标题从 "Some languages" 变为 "Some languages for programming"。注意textContent会把内容当作纯文本处理,即使用户输入包含<、>之类的字符也不会被解析成 HTML,因此在处理用户输入、避免注入风险时,textContent往往比innerHTML更安全、更合适。

修改元素属性:setAttribute() 与属性直接赋值

setAttribute()方法可以为元素设置任意属性的值,参数依次是属性名和属性值:

// Define the id attribute of the first title document.querySelector("h3").setAttribute("id", "title");

如第 14 章所见,部分常用属性(如id、href、value)在 DOM 中同时以对象属性的形式存在,可以直接赋值,效果与setAttribute()相同:

// Define the id attribute of the first title document.querySelector("h3").id = "title";

两种写法等价。对于id这类常见属性,直接赋属性值更简洁;setAttribute()则胜在通用——它可以处理任意属性,包括data-*自定义属性等。

用 classList 增删类名

一个元素可以同时拥有多个 class。classList属性返回该元素的类名集合,并提供了add()/remove()等方法。下面代码把第一个<h3>的beginning类移除、再加上title类,并把元素打印到控制台:

const titleElement = document.querySelector("h3"); // Grab the first h3 titleElement.classList.remove("beginning"); // Remove the class "beginning" titleElement.classList.add("title"); // Add a class called "title" console.log(titleElement);

上一章还介绍过classList的查询能力(contains()判断是否包含某类、length与下标访问遍历类名);本章的add()/remove()则是它的写入能力。此外classList还内置toggle()等方法,可结合 第 14 章 的内容一并掌握。

添加新元素:三步走

修改已有元素之外,更常见的是凭空创建一个新元素并插入页面。整个过程可以拆成三步:

  1. 创建新元素;
  2. 设置元素属性(ID、类、文本内容等);
  3. 把新元素插入DOM 树。

例如,给语言列表添加一门 "Python":

const pythonElement = document.createElement("li"); // Create an "li" element pythonElement.id = "python"; // Define element ID pythonElement.textContent = "Python"; // Define its text content document.getElementById("languages").appendChild(pythonElement); // Insert the new element into the DOM

下面逐一步骤拆解。

第 1 步:createElement() 创建元素

createElement()方法用于创建元素节点。它在document对象上调用,参数是新元素的标签名,返回值是创建好的元素对象(DOM 中它尚未挂载到任何位置,只是一个游离节点):

const pythonElement = document.createElement("li"); // Create an li element

第 2 步:设置元素属性

元素创建后,就可以利用前面学的 DOM 属性给它"填充细节"——ID、class、文本内容等等。示例中把新元素的 ID 设为"python"、文本内容设为"Python":

// ... pythonElement.id = "python"; // Define element ID pythonElement.textContent = "Python"; // Define its text content

这里也可以改用setAttribute()或classList.add(),取决于你想设置的属性类型。

第 3 步:appendChild() 插入 DOM

把新节点挂到 DOM 树上的技术有多种,最常用的是appendChild():在未来父元素上调用它,新节点会被追加为该父元素最后一个子节点:

// ... document.getElementById("languages").appendChild(pythonElement); // Insert the new element into the DOM

在我们的例子中,pythonElement成为<ul id="languages">的新子节点,排在 C++、Java、C#、PHP 之后。

添加元素的更多变体

appendChild()是"追加到末尾"的标准方案,但实际需求往往更复杂:有时只想加纯文本、有时要插到指定位置。本章提供了三种补充方案。

createTextNode():添加纯文本节点

不想用textContent属性,也可以先创建文本节点再挂载。createTextNode()创建文本节点,再用appendChild()把它挂到元素上。下面的代码用这种方式在列表末尾插入 Ruby:

const rubyElement = document.createElement("li"); // Create an "li" element rubyElement.id = "ruby"; // Define element ID rubyElement.appendChild(document.createTextNode("Ruby")); // Define its text content document.getElementById("languages").appendChild(rubyElement); // Insert the new element into the DOM

注意这里的双重appendChild():第一个把文本节点挂进<li>,第二个把<li>挂进<ul>。回想第 13 章提到的"文本节点是 DOM 树中与元素节点平级的一类节点"(见 chapter13.md),createTextNode()正是程序化生成这类节点的方法。

insertBefore():插到指定节点之前

当"追加到末尾"不满足需求时,可以用insertBefore()。在父元素上调用,参数依次是新节点和参照节点,新节点会被插入到参照节点之前。例如把 Perl 插到 PHP 前面:

const perlElement = document.createElement("li"); // Create an "li" element perlElement.id = "perl"; // Define element ID perlElement.textContent = "Perl"; // Define its text content // Insert the new element before the "PHP" node document.getElementById("languages").insertBefore(perlElement, document.getElementById("php"));

insertBefore(newNode, referenceNode)与appendChild(newNode)的参数顺序相反,使用时务必区分:前者把节点放在参照节点之前,后者把节点放在父元素子节点列表的末尾。

insertAdjacentHTML():精确控制插入位置

insertAdjacentHTML()可以更精确地指定插入位置。调用方式是在已有元素上调用,传入两个参数:位置和一段HTML 字符串。位置取值共有四种:

位置含义
beforebegin插入到该元素之前(作为其兄弟节点)
afterbegin插入到该元素内部、其第一个子节点之前
beforeend插入到该元素内部、其最后一个子节点之后
afterend插入到该元素之后(作为其兄弟节点)

以现有<p>标签为例,四个位置对应关系如下:

<!-- beforebegin --> <p> <!-- afterbegin --> foo <!-- beforeend --> </p> <!-- afterend -->

实际使用中,位置参数不区分大小写(文档示例用的是"afterBegin")。下面的代码把 JavaScript 插入到语言列表的顶部(即<ul>第一个子节点之前):

// Add an element to the beginning of a list document.getElementById('languages').insertAdjacentHTML("afterBegin", '<li id="javascript">JavaScript</li>');

与createElement()+appendChild()相比,insertAdjacentHTML()直接接收 HTML 字符串、一步完成插入,代码更简短;代价与innerHTML类似——字符串拼接过多会降低可读性,且需要留意 HTML 解析带来的开销与安全边界。

替换与删除节点

replaceChild():替换节点

replaceChild()用新节点替换父元素的某个子节点,参数顺序是(新节点, 被替换节点)。下面的代码把 Perl 替换成 Lisp:

const lispElement = document.createElement("li"); // Create an li element lispElement.id = "lisp"; // Define its ID lispElement.textContent = "Lisp"; // Define its text content // Replace the element identified by "perl" with the new element document.getElementById("languages").replaceChild(lispElement, document.getElementById("perl"));

参数顺序容易记混:新节点在前、旧节点在后,与insertBefore(newNode, referenceNode)的"新节点在前"风格一致。

removeChild():删除节点

删除节点使用removeChild(),参数是要被移除的节点(通常先用选择方法拿到它):

// Remove the element with the "lisp" id document.getElementById("languages").removeChild(document.getElementById("lisp"));

执行后列表恢复为 C++、Java、C#、PHP、C、Ruby、Perl(已被替换为 Lisp 又被删除)、JavaScript 的中间状态——这正是各示例顺序执行时页面呈现的效果。

修改元素样式

JavaScript 不仅能改页面结构,还能改元素的视觉样式。下面是样式部分的示例 HTML 与配套 CSS。

示例 HTML(三个段落,第二个通过内联style属性设为绿色):

<p>First</p> <p style="color: green;">Second</p> <p id="para">Third</p>

配套 CSS 样式表(#para的文本为蓝色且倾斜):

#para { font-style: italic; color: blue; }

style 属性:修改内联样式

每个 DOM 元素都有一个style属性,它返回一个代表元素内联style属性的对象;对象上的各属性与 CSS 属性一一对应。给这些属性赋值,就等于直接修改元素的样式。例如选中第一个段落并修改其文字颜色与外边距:

const paragraphElement = document.querySelector("p"); paragraphElement.style.color = "red"; paragraphElement.style.margin = "50px";

复合 CSS 属性的 camelCase 命名

部分 CSS 属性由多个单词组成(如background-color)。在 JavaScript 中访问这类属性时,需要去掉连字符并把后续单词首字母大写(camelCase 驼峰命名法)。下面的代码把同一个段落的font-family与background-color分别设为 Arial 和黑色:

// ... paragraphElement.style.fontFamily = "Arial"; paragraphElement.style.backgroundColor = "black";

这种命名约定在本书此前章节中已经出现过:CSS 的background-color对应 JS 的backgroundColor,font-family对应fontFamily,规律完全一致。各 CSS 属性对应的 JavaScript 属性名,可查阅 Mozilla Developer Network 的 CSS Properties Reference 文档。

style 属性的局限:读不到样式表里的样式

style属性只反映元素内联style属性中声明的样式,无法读取定义在 CSS 样式表中的样式。试着读取三个段落的文字颜色:

const paragraphElements = document.getElementsByTagName("p"); console.log(paragraphElements[0].style.color); // "red" console.log(paragraphElements[1].style.color); // "green" console.log(paragraphElements[2].style.color); // Show an empty string

前两个段落分别返回"red"和"green"——因为它们的内联样式被 JavaScript 或 HTML 显式设置了;第三个段落的蓝色来自外部 CSS 样式表(#para规则),style属性对它一无所知,返回空字符串。

getComputedStyle():读取最终计算样式

要读取元素最终生效的样式(包括样式表规则、继承结果),应使用getComputedStyle()。它以 DOM 节点为参数,返回一个代表该元素计算样式(computed style)的对象,之后即可读取各 CSS 属性:

const paragraphStyle = getComputedStyle(document.getElementById("para")); console.log(paragraphStyle.fontStyle); // "italic" console.log(paragraphStyle.color); // color blue in RGB values

这里的蓝色以 RGB 形式呈现:红、绿、蓝三个通道的取值都在 0 到 255 之间。

需要强调的是:getComputedStyle()返回的对象是只读的——它用于"读取"元素当前实际样式;要"修改"样式,依然靠style属性或classList操作类名(让 CSS 规则生效)。

DOM 操作与性能

每次通过 JavaScript 更新 DOM,浏览器都需要重新计算页面的显示效果(包括重新布局与重绘)。频繁的 DOM 操作会导致页面卡顿、性能下降,因此应尽可能减少 DOM 的访问与更新次数。

一个典型的性能优化习惯是:先把新元素的属性设置好,再一次性插入 DOM。对比下面两种写法:

// Bad: DOM is updated multiple times const newNode = document.createElement(...); // Create new element parentNode.appendChild(newNode); // Add it to the DOM newNode.id = ...; // Set some element properties newNode.textContent = "..."; // ... // Better: DOM is updated only once const newNode = document.createElement(...); // Create new element newNode.id = ...; // Set some element properties newNode.textContent = "..."; // ... parentNode.appendChild(newNode); // Add it to the DOM

左侧写法先插入、后设置属性,浏览器被迫在元素插入后再次为属性变化重新计算渲染;右侧写法先在"游离状态"下把所有属性配齐,再一次性挂载,DOM 只被更新一次。这也正是本章"添加新元素三步走"(先创建、再设置、后插入)的顺序如此安排的深层原因——它不仅是逻辑清晰,更是性能友好的实践。

快速回顾(TL;DR)

本章知识要点一览:

  • 修改一个 DOM 元素的信息,可以使用innerHTML、textContent、classList等属性,以及setAttribute()方法;
  • 创建新的 DOM 节点,用createTextNode()(文本节点)与createElement()(元素节点);
  • appendChild()把新节点插入为某元素的最后一个子节点;
  • insertBefore()与insertAdjacentHTML()是插入内容的另外两种方式,分别支持"插到指定节点之前"与"四种精确位置";
  • replaceChild()替换已有节点,removeChild()删除节点;
  • JavaScript 的style属性代表 DOM 节点的内联style属性,可用于修改样式;
  • 多单词 CSS 属性在 JavaScript 中写成camelCase,例如font-family→fontFamily;
  • style属性不适合用来读取元素样式,读取请使用getComputedStyle();
  • DOM 操作应克制进行,避免拖慢页面性能。

实战练习

以下是本章配套练习,建议在浏览器控制台或独立 HTML 页面中完成,逐一验证输出结果。

练习 1:添加一个段落

在"语言列表"示例的基础上,向页面添加一个段落(<p>),段落内包含一个指向编程语言列表页面(https://en.wikipedia.org/wiki/List_of_programming_languages)的超链接(<a>)。提示:用createElement()分别创建<p>与<a>,设置a.href与文本内容,再通过appendChild()依次挂载。

练习 2:报纸列表

给定如下 HTML 页面:

<h3>Some newspapers</h3> <div id="content"></div>

编写程序,把下面数组中定义的报纸以可点击链接的形式显示在页面上:

// Newspaper list const newspapers = ["https://www.nytimes.com", "https://www.washingtonpost.com", "http://www.economist.com"];

思路:遍历数组,为每项创建<a>元素(textContent用报纸名、href用 URL),再插入#content。注意观察用innerHTML拼接与用createElement()+appendChild()两种写法的差异。

练习 3:迷你词典

给定如下 HTML 页面:

<h3>A mini-dictionary</h3> <div id="content"></div>

编写程序,把下面数组中的词条以"术语 + 定义"列表的形式显示在页面上:

const words = [{ term: "Procrastination", definition: "Avoidance of doing a task that needs to be accomplished" }, { term: "Tautology", definition: "logical argument constructed in such a way that it is logically irrefutable" }, { term: "Oxymoron", definition: "figure of speech that juxtaposes elements that appear to be contradictory" }];

要求使用 HTML 的<dl>(描述列表)标签组织内容(可参考 MDN 上关于<dl>标签的文档),并且每个术语用<strong>标签加粗强调。<dl>内通常包含<dt>(术语)与<dd>(定义)两类子元素,注意createElement()只能逐个创建标签,需要你为每个词条创建两个子元素。

练习 4:更新颜色

给定包含三个标题与三个正文段落的 HTML 页面(每对<h1>+<div>构成一个小节)。编写程序:

  1. 首先通过prompt()询问用户新的文字颜色;
  2. 再通过prompt()询问新的背景颜色;
  3. 页面中所有段落随之更新。

思路:用querySelectorAll()或getElementsByTagName()选中所有<div>,遍历并为每个元素设置style.color与style.backgroundColor——注意background-color在 JavaScript 中要写成backgroundColor。

练习 5:获取元素的信息

给定如下 HTML 与 CSS:

<div id="content">ABC <br>Easy as <br>One, two, three </div> <div id="infos"></div>
#content { float: right; margin-top: 100px; margin-right: 50px; }

编写程序,在#infos中显示#content元素的高度与宽度。这里无法用style属性读取(尺寸由 CSS 样式表与内容共同决定),需要借助getComputedStyle()或元素的offsetWidth/offsetHeight等布局属性来获取实际渲染尺寸。

完成以上练习后,你就完整走通了"选择元素 → 修改内容/属性/类名 → 创建与插入新节点 → 替换与删除 → 修改样式 → 读取计算样式"这条 DOM 动态化主链路。接下来的 第 16 章 将把这一切与事件监听结合,让页面真正响应用户交互,进入"事件驱动"的交互式 Web 开发阶段。

  • 教程
  • 文档

【免费下载链接】thejsway

The JavaScript Way book

项目地址:https://gitcode.com/gh_mirrors/th/thejsway
点击查看免费下载
上一篇:智能视频格式转换终极方案:解锁B站缓存视频的全平台播放能力
下一篇:英雄联盟Akari助手:3大核心功能彻底改变你的游戏体验

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 10:21:57

避开高价坑从零搭建phpcms网站备份方案

避开高价坑从零搭建phpcms网站备份方案 找建站公司怕被坑高价,这是很多老板心里的刺。别被那些“全托管”“终身维护”的漂亮话术忽悠了,真正决定网站生死的是底层数据的完整性。…

作者头像 李华
网站建设 2026/9/27 10:21:50

对象存储oss怎么用?

阿里云对象存储&#xff08;OSS, Object Storage Service&#xff09;&#xff08;https://www.aliyun.com/product/oss&#xff09;是阿里云提供的海量、安全、低成本、高持久的云存储服务。它适合存放图片、视频、日志、备份数据等非结构化数据。 以下是 OSS 的核心概念、使…

作者头像 李华
网站建设 2026/9/27 10:21:36

新闻采集源码wordpress速查手册:报价拆解与域名避坑指南

新闻采集源码wordpress速查手册:报价拆解与域名避坑指南 域名解析报错,服务器SSL证书过期,后台登录直接白屏。这三个场景,是不是让你抓狂?很多老板以为买套“新闻采集源码wordpress”就能自动赚钱,结果卡在部署环节,对着控制台发呆。…

作者头像 李华
网站建设 2026/9/27 10:21:26

CAN数据帧和过滤器

一、can通信的数据帧起始位 识别码 RTR 1位(0) 11位(每个设备都有属于自己的识别码) 1位(数据帧0&#xff0c;远程请求帧1)控制码&#xff1a;IDE …

作者头像 李华
网站建设 2026/9/27 10:21:13

STM32实验室消防预警系统:开源硬件+三级阈值+嘉立创实测

1. 项目概述&#xff1a;一个能真正跑在实验室里的消防预警系统你有没有遇到过这样的场景&#xff1a;实验室里几台恒温箱、烘箱、电炉同时开着&#xff0c;空气里飘着淡淡的焦糊味&#xff0c;但没人察觉&#xff1b;或者深夜值班时&#xff0c;烟雾传感器误报&#xff0c;警报…

作者头像 李华
网站建设 2026/9/27 10:20:21

清远网站开发图解步骤:3招搞定黑挂马与SEO排名

清远网站开发图解步骤:3招搞定黑挂马与SEO排名 昨天刚帮清远一家做陶瓷出口的老板处理完紧急事故。他的网站突然打不开,浏览器弹出红色警告,页面底部赫然出现几个赌博和色情链接的乱码。他急得直拍桌子:“我明明开了防火墙,怎么还被黑挂了马?现在流量全掉没了,这单生意还要不要了?”…

作者头像 李华