🚀 揭秘网页"魔法":JavaScript操作DOM,让你的页面"活"起来!
嘿,各位未来的前端魔法师们!🧙♀️ 你们有没有想过,那些酷炫的网页特效、动态更新的内容,甚至你点击一个按钮就能"变"出新东西,这些"魔法"是怎么实现的呢?
答案就是------JavaScript操作DOM!
DOM,全称Document Object Model(文档对象模型),听起来是不是有点高大上?别担心,今天咱们就用最接地气的方式,把它扒个精光,让你彻底搞懂这个前端开发的"核心秘籍"!
想象一下,你的网页就像一个乐高积木搭建的城堡。HTML是各种积木块,CSS是给积木块涂上颜色、摆好造型的说明书,而JavaScript,就是那个能让积木块动起来、甚至能凭空"变"出新积木的"小手"!而DOM,就是这个"乐高城堡"的"设计图",它把所有的积木块(HTML元素)都组织成了一个有层次、有关系的"家族树"。
准备好了吗?系好安全带,咱们这就出发,一起探索JavaScript操作DOM的奇妙世界!
🌟 什么是DOM?- 理解网页的"骨架"
1.1 DOM的基本概念:网页的"身份证"和"操作手册"
DOM,全称 Document Object Model,直译过来就是"文档对象模型"。听起来是不是有点抽象?没关系,咱们来个接地气的比喻。
想象一下,你家里的户口本,上面详细记录了你家每个成员的信息:姓名、性别、年龄、住址等等。每个成员之间还有亲属关系:爸爸、妈妈、儿子、女儿......
DOM就像是网页的"户口本" !它把整个HTML文档解析成了一个由各种"对象"组成的"家族"。每个HTML标签,比如<p>
、<div>
、<img>
,都会被DOM"登记"成一个"对象"(或者叫"节点")。这些对象不仅包含了标签本身的信息(比如它的ID、类名、内容),还记录了它们在网页中的"家庭关系":谁是爸爸(父节点),谁是儿子(子节点),谁是兄弟姐妹(兄弟节点)。
而"模型",则意味着它提供了一套标准的方法(API),让你能够通过JavaScript这只"小手",去访问、修改、添加或删除这些"户口本"上的"成员"信息。所以,DOM不仅是网页的"身份证",更是我们操作网页的"操作手册"!
1.2 DOM树结构:网页的"家族树"
既然DOM把网页组织成了"家族",那这个"家族"自然就有它的"家族树"。我们通常称之为DOM树。
就像你家的族谱一样,DOM树也是一个层级分明的结构。最顶端的是document
对象,它代表了整个HTML文档,是所有元素的"老祖宗"。然后是<html>
元素,它是document
的"大儿子"。接着是<head>
和<body>
,它们是<html>
的"双胞胎儿子"。再往下,<body>
里面可能有很多<div>
、<p>
、<img>
等等,它们都是<body>
的"子孙"。
这种树状结构非常直观,它清晰地展示了网页中各个元素之间的父子、兄弟关系。理解了DOM树,你就掌握了网页的"骨架",知道每个元素在网页中的"位置"和"身份"。
比如,你想找到你家"大儿子"的"小儿子",你就能顺着族谱一层一层地找下去。在DOM里也一样,你想找到某个特定元素,就可以顺着DOM树的层级关系去"遍历"。

1.3 为什么要操作DOM?- 让网页"活"起来的"魔法"
你可能会问,既然HTML和CSS已经能把网页"画"出来了,为什么还要JavaScript来操作DOM呢?
原因很简单:让网页"活"起来!
想象一下,你打开一个电商网站,鼠标移到商品图片上,图片会放大;点击"加入购物车",购物车图标旁边会显示商品数量增加;填写表单时,输入错误会有提示......这些动态交互,都不是HTML和CSS能单独完成的,它们都需要JavaScript来"指挥"DOM。
通过操作DOM,我们可以实现:
- 动态修改内容:比如,点击一个按钮,网页上的文字从"你好"变成"欢迎回来"。
- 动态改变样式:比如,鼠标移到图片上,图片边框变色,字体变大。
- 动态添加/删除元素:比如,点击"加载更多",页面上出现新的商品列表;点击"删除",某个评论消失。
- 响应用户操作:比如,用户点击、输入、滚动等行为,都能触发JavaScript代码,从而改变网页。
所以,JavaScript操作DOM,就像是给网页注入了"灵魂",让它从一个静态的"画报",变成了一个能与用户"交流"、"互动"的"智能"界面。掌握了DOM操作,你就掌握了前端开发的"魔法",能够创造出各种令人惊叹的网页效果!
🔍 获取DOM元素 - 找到你要的"目标"
既然DOM是网页的"户口本",那我们想操作某个"成员",首先得能"找到"它。JavaScript提供了多种"找人"的方法,就像现实生活中,你可以通过身份证号、班级、职业等方式找到一个人一样。
2.1 "身份证"找人:getElementById()
这是最直接、最有效率的"找人"方式。每个HTML元素都可以有一个唯一的id
属性,就像每个公民都有一个唯一的身份证号一样。当你明确知道某个元素的id
时,getElementById()
就是你的不二选择。
语法:
javascript
document.getElementById('你的元素ID');
生活例子: 就像你在茫茫人海中,只要知道一个人的身份证号,就能精准地找到他,不会有重名的情况。
代码解释:
html
<!-- index.html -->
<div id="myDiv">这是一个需要被找到的Div</div>
<p id="myParagraph">这是一段文字</p>
<script>
const myDiv = document.getElementById('myDiv');
console.log(myDiv); // 输出:<div id="myDiv">这是一个需要被找到的Div</div>
const myParagraph = document.getElementById('myParagraph');
console.log(myParagraph); // 输出:<p id="myParagraph">这是一段文字</p>
</script>
注意: id
在HTML文档中必须是唯一的!如果存在多个相同id
的元素,getElementById()
只会返回第一个。
2.2 "班级"找人:getElementsByClassName()
有时候,你可能想找到"一个班"的学生,而不是某个特定的学生。这时,getElementsByClassName()
就派上用场了。它通过元素的class
属性来查找。
语法:
javascript
document.getElementsByClassName('你的类名');
生活例子: 就像你想找到"三年二班"的所有同学,你不需要知道每个人的名字,只要知道他们都属于"三年二班"这个集体。
代码解释:
html
<!-- index.html -->
<ul>
<li class="item">苹果</li>
<li class="item">香蕉</li>
<li class="item">橘子</li>
</ul>
<script>
const items = document.getElementsByClassName('item');
console.log(items); // 输出一个HTMLCollection,包含所有class为"item"的<li>元素
// 遍历这些元素
for (let i = 0; i < items.length; i++) {
console.log(items[i].textContent); // 苹果, 香蕉, 橘子
}
</script>
注意: getElementsByClassName()
返回的是一个HTMLCollection
(HTML集合),它看起来像数组,但并不是真正的数组,不能直接使用数组的所有方法(比如forEach
)。如果你需要使用数组方法,可以将其转换为数组:Array.from(items)
。
2.3 "职业"找人:getElementsByTagName()
如果你想找到网页中所有"程序员"(比如所有<p>
标签),getElementsByTagName()
就能帮你做到。
语法:
javascript
document.getElementsByTagName('你的标签名');
生活例子: 就像你想找到一个公司里所有"销售",你只需要知道他们的"职业"是销售,而不需要知道他们的具体名字。
代码解释:
html
<!-- index.html -->
<h1>标题1</h1>
<p>第一段文字</p>
<p>第二段文字</p>
<script>
const paragraphs = document.getElementsByTagName('p');
console.log(paragraphs); // 输出一个HTMLCollection,包含所有<p>元素
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.color = 'blue'; // 将所有段落文字颜色设为蓝色
}
</script>
2.4 "万能搜索器":querySelector()
和 querySelectorAll()
如果你觉得上面这些方法有点"专一",那querySelector()
和querySelectorAll()
就是前端界的"万能搜索器"!它们可以使用CSS选择器来查找元素,功能非常强大。
语法:
javascript
document.querySelector('CSS选择器'); // 返回第一个匹配的元素
document.querySelectorAll('CSS选择器'); // 返回所有匹配的元素
生活例子: 就像你在网上购物,你可以输入"红色T恤"(标签名+属性),"打折商品"(类名),"品牌A的最新款手机"(组合选择器)等等,它都能帮你找到。
代码解释:
html
<!-- index.html -->
<div id="container">
<p class="text highlight">第一段高亮文字</p>
<p class="text">第二段文字</p>
</div>
<script>
// 找到第一个class为"text"的<p>元素
const firstText = document.querySelector('.text');
console.log(firstText.textContent); // 输出:第一段高亮文字
// 找到id为"container"下的所有<p>元素
const allParagraphsInContainer = document.querySelectorAll('#container p');
allParagraphsInContainer.forEach(p => {
console.log(p.textContent); // 输出:第一段高亮文字, 第二段文字
});
// 找到同时有class "text" 和 "highlight" 的元素
const highlightedText = document.querySelector('.text.highlight');
console.log(highlightedText.textContent); // 输出:第一段高亮文字
</script>
注意: querySelectorAll()
返回的是一个NodeList
(节点列表),它也像数组,但同样不是真正的数组。不过,NodeList
可以使用forEach
方法进行遍历。
小结:
方法 | 描述 | 返回值类型 | 适用场景 |
---|---|---|---|
getElementById() |
通过元素的唯一ID查找元素 | 单个Element对象 | 精准查找,ID必须唯一 |
getElementsByClassName() |
通过类名查找元素 | HTMLCollection |
查找具有相同类名的多个元素 |
getElementsByTagName() |
通过标签名查找元素 | HTMLCollection |
查找所有相同标签的元素 |
querySelector() |
通过CSS选择器查找第一个匹配的元素 | 单个Element对象 | 灵活查找,适用于各种复杂的选择器 |
querySelectorAll() |
通过CSS选择器查找所有匹配的元素 | NodeList |
灵活查找,适用于各种复杂的选择器,返回多个元素 |
选择哪种方法取决于你的"找人"需求。如果目标明确且唯一,getElementById()
最快;如果需要批量操作,getElementsByClassName()
或getElementsByTagName()
;如果需要复杂的组合查找,querySelector()
和querySelectorAll()
则是你的利器。
🎨 操作元素内容 - 给元素"换装"
找到元素之后,我们就可以开始"装修"它了!最常见的操作就是修改元素的内容。想象一下,你买了一件新衣服,想在上面印上自己的名字或者一句slogan,这就是修改内容。
在JavaScript中,主要有两种方式来修改或获取元素的内容:innerHTML
和textContent
。
3.1 innerHTML
vs textContent
:"带装修"还是"毛坯房"?
innerHTML
:"带装修"的房子
innerHTML
属性可以获取或设置元素内部的HTML内容,包括所有的HTML标签、文本、注释等。当你设置innerHTML
时,浏览器会解析你传入的字符串,并将其作为HTML结构渲染出来。
生活例子: 就像你买了一套"精装修"的房子,里面不仅有墙壁、地板,还有各种家具、电器,你直接拎包入住。如果你想重新装修,你可以把旧家具扔掉,换上新的,甚至重新隔断房间。
代码解释:
html
<!-- index.html -->
<div id="myHouse">
<p>旧沙发</p>
<span>旧电视</span>
</div>
<script>
const myHouse = document.getElementById("myHouse");
// 获取内容
console.log(myHouse.innerHTML);
// 输出:<p>旧沙发</p>
// <span>旧电视</span>
// 设置内容(带HTML标签)
myHouse.innerHTML = "<h2>新沙发和新电视</h2><img src=\"new_tv.jpg\" alt=\"新电视\">";
// 页面上的myHouse div内部会被替换成一个h2标题和一张图片
// 设置内容(包含恶意脚本)
// myHouse.innerHTML = "<img src=x onerror=alert(\'你被攻击了!\')>";
// 注意:innerHTML直接插入HTML,存在XSS(跨站脚本攻击)风险,慎用!
</script>
textContent
:"毛坯房"
textContent
属性则只获取或设置元素的纯文本内容,它会忽略所有的HTML标签,只保留文本。当你设置textContent
时,浏览器会将其视为纯文本,即使你传入了HTML标签,也会被当作普通文本显示出来,而不会被解析。
生活例子: 就像你买了一套"毛坯房",里面只有光秃秃的墙壁,没有任何装修。如果你想在墙上写字,你直接写上去就行,不会被当作家具或者电器。
代码解释:
html
<!-- index.html -->
<div id="myRoom">
<p>你好,<b>世界</b>!</p>
</div>
<script>
const myRoom = document.getElementById("myRoom");
// 获取内容
console.log(myRoom.textContent); // 输出:你好,世界! (<b>标签被忽略)
// 设置内容(即使有HTML标签,也会被当作纯文本显示)
myRoom.textContent = "<h2>这是一个标题</h2>";
// 页面上会显示:<h2>这是一个标题</h2>,而不是一个真正的h2标题
</script>
总结:
属性 | 描述 | 是否解析HTML标签 | 安全性 | 适用场景 |
---|---|---|---|---|
innerHTML |
获取或设置元素内部的HTML内容(包括标签) | 是 | 较低 | 需要插入HTML结构或动态生成复杂内容时 |
textContent |
获取或设置元素内部的纯文本内容(忽略标签) | 否 | 较高 | 只需处理文本,或防止XSS攻击时 |
3.2 修改元素内容的实际应用:"动态公告牌"和"实时计数器"
应用一:动态公告牌
想象一下,你有一个网页上的公告牌,需要根据不同的情况显示不同的通知。你可以用innerHTML
来动态更新它。
html
<!-- index.html -->
<div id="announcement" style="border: 1px solid #ccc; padding: 10px;">
<p>欢迎来到我们的网站!</p>
</div>
<button onclick="updateAnnouncement()">更新公告</button>
<script>
function updateAnnouncement() {
const announcementDiv = document.getElementById("announcement");
const messages = [
"<p style=\"color: red;\">紧急通知:系统维护中!</p>",
"<p>最新活动:全场八折优惠!</p>",
"<p>祝您购物愉快!</p>"
];
const randomIndex = Math.floor(Math.random() * messages.length);
announcementDiv.innerHTML = messages[randomIndex];
}
</script>
应用二:实时计数器
如果你想制作一个简单的点击计数器,textContent
就非常适合,因为它只处理纯文本数字。
html
<!-- index.html -->
<button id="clickMe">点击我</button>
<p>你点击了 <span id="count">0</span> 次。</p>
<script>
const clickButton = document.getElementById("clickMe");
const countSpan = document.getElementById("count");
let clickCount = 0;
clickButton.addEventListener("click", function() {
clickCount++;
countSpan.textContent = clickCount; // 只更新纯文本数字
});
</script>
通过这两个简单的例子,你就能体会到innerHTML
和textContent
在实际开发中的妙用啦!记住,选择哪个属性,取决于你是否需要解析HTML标签,以及对安全性的考量。
⚙️ 操作元素属性 - 调整元素的"设置"
每个HTML元素除了内容,还有各种"属性",比如图片的src
(来源)、链接的href
(跳转地址)、输入框的value
(值)等等。这些属性就像是元素的"设置",我们可以通过JavaScript来读取、修改这些"设置",从而改变元素的行为或外观。
4.1 getAttribute()
和 setAttribute()
:属性的"读写器"
就像你手机的设置一样,你可以查看当前铃声是哪首(获取属性),也可以把它改成你喜欢的歌曲(设置属性)。在DOM操作中,getAttribute()
和setAttribute()
就是专门用来"读写"元素属性的方法。
getAttribute()
:"读取"属性值
语法:
javascript
element.getAttribute("属性名");
生活例子: 就像你想知道你家门牌号是多少,你直接去看门牌就行了。
代码解释:
html
<!-- index.html -->
<img id="myImage" src="old_image.jpg" alt="旧图片">
<a id="myLink" href="old_link.html">旧链接</a>
<script>
const myImage = document.getElementById("myImage");
const myLink = document.getElementById("myLink");
console.log(myImage.getAttribute("src")); // 输出:old_image.jpg
console.log(myLink.getAttribute("href")); // 输出:old_link.html
</script>
setAttribute()
:"设置"属性值
语法:
javascript
element.setAttribute("属性名", "新的属性值");
生活例子: 就像你想把门牌号换成新的,你直接把新的门牌挂上去。
代码解释:
html
<!-- index.html -->
<img id="myImage" src="old_image.jpg" alt="旧图片">
<a id="myLink" href="old_link.html">旧链接</a>
<script>
const myImage = document.getElementById("myImage");
const myLink = document.getElementById("myLink");
myImage.setAttribute("src", "new_image.jpg"); // 将图片src属性改为new_image.jpg
myImage.setAttribute("alt", "新图片"); // 修改图片的alt属性
myLink.setAttribute("href", "new_link.html"); // 将链接href属性改为new_link.html
myLink.setAttribute("target", "_blank"); // 让链接在新标签页打开
</script>
4.2 常用属性操作:"快捷方式"与"注意事项"
除了getAttribute()
和setAttribute()
这种通用的方法,对于一些常用的属性,JavaScript还提供了更"快捷"的直接访问方式,就像你不用每次都去"设置"里找音量调节,直接按手机侧面的音量键一样。
直接访问属性:
很多HTML元素的属性可以直接通过element.属性名
的方式来访问和修改。例如:
element.id
:获取或设置元素的id
。element.className
:获取或设置元素的class
(注意是className
而不是class
,因为class
是JavaScript的保留字)。element.src
:获取或设置<img>
标签的src
。element.href
:获取或设置<a>
标签的href
。element.value
:获取或设置表单元素(如<input>
、<textarea>
、<select>
)的value
。
代码解释:
html
<!-- index.html -->
<input type="text" id="myInput" value="默认值">
<button id="changeBtn">改变输入框值</button>
<script>
const myInput = document.getElementById("myInput");
const changeBtn = document.getElementById("changeBtn");
console.log(myInput.value); // 输出:默认值
changeBtn.addEventListener("click", function() {
myInput.value = "我被改变了!"; // 直接修改value属性
console.log(myInput.value); // 输出:我被改变了!
});
</script>
style
属性:直接修改样式
element.style
可以让你直接修改元素的内联样式。这就像你直接给衣服的某个部位涂上颜色。
语法:
javascript
element.style.css属性名 = "值";
注意: CSS属性名如果是带连字符的(如background-color
),在JavaScript中需要转换为驼峰命名法(backgroundColor
)。
代码解释:
html
<!-- index.html -->
<div id="myBox" style="width: 100px; height: 100px; background-color: red;"></div>
<button id="changeColorBtn">变色</button>
<script>
const myBox = document.getElementById("myBox");
const changeColorBtn = document.getElementById("changeColorBtn");
changeColorBtn.addEventListener("click", function() {
myBox.style.backgroundColor = "blue"; // 将背景色改为蓝色
myBox.style.width = "200px"; // 宽度变为200px
});
</script>
classList
属性:管理元素的类名
classList
是一个非常方便的属性,它提供了一系列方法来添加、删除、切换元素的类名,而不需要手动操作className
字符串。这就像你给衣服贴标签,可以很方便地添加、撕掉或切换标签。
常用方法:
add()
:添加一个或多个类名。remove()
:移除一个或多个类名。toggle()
:如果类名存在则移除,不存在则添加。contains()
:检查是否包含某个类名。
代码解释:
html
<!-- index.html -->
<style>
.highlight { background-color: yellow; }
.bold { font-weight: bold; }
</style>
<p id="myText">这是一段文字。</p>
<button id="toggleHighlight">切换高亮</button>
<button id="addBold">添加粗体</button>
<script>
const myText = document.getElementById("myText");
const toggleHighlightBtn = document.getElementById("toggleHighlight");
const addBoldBtn = document.getElementById("addBold");
toggleHighlightBtn.addEventListener("click", function() {
myText.classList.toggle("highlight"); // 切换highlight类
});
addBoldBtn.addEventListener("click", function() {
myText.classList.add("bold"); // 添加bold类
});
console.log(myText.classList.contains("highlight")); // 检查是否包含highlight类
</script>
总结:
属性/方法 | 描述 | 适用场景 |
---|---|---|
getAttribute() |
获取元素的指定属性值 | 读取不常用或自定义属性 |
setAttribute() |
设置元素的指定属性值 | 设置不常用或自定义属性 |
element.属性名 |
直接访问和修改常用属性 | 快速操作如id , src , href , value 等 |
element.style.prop |
直接修改元素的内联CSS样式 | 动态改变单个样式属性 |
classList |
方便地管理元素的CSS类名 | 批量添加/移除/切换样式类,推荐使用 |
灵活运用这些方法,你就能像一个经验丰富的"装修工"一样,随心所欲地调整网页元素的"设置",让它们呈现出你想要的效果!
🎭 操作元素样式 - 让页面更美观
网页的"颜值"很重要!通过JavaScript操作DOM,我们不仅能改变内容和属性,还能像给模特换衣服一样,动态地改变元素的样式,让页面看起来更酷、更炫、更符合用户需求。
5.1 style
属性直接修改:"私人定制"的内联样式
最直接的方式就是通过元素的style
属性来修改内联样式。这就像你给一件衣服直接涂上颜色,效果立竿见影,但只对当前这件衣服有效。
语法:
javascript
element.style.css属性名 = "值";
生活例子: 想象你是一个画家,直接在画布上涂抹颜色,想让哪里红就哪里红,想让哪里蓝就哪里蓝。
代码解释:
html
<!-- index.html -->
<div id="box" style="width: 100px; height: 100px; background-color: red;"></div>
<button onclick="changeStyle()">变个样</button>
<script>
function changeStyle() {
const box = document.getElementById("box");
box.style.backgroundColor = "blue"; // 背景变蓝色
box.style.width = "200px"; // 宽度变200px
box.style.border = "5px solid green"; // 添加绿色边框
// 注意:CSS属性名如果是带连字符的(如font-size),在JS中要用驼峰命名法(fontSize)
box.style.fontSize = "20px";
}
</script>
优点: 简单直接,优先级高。 缺点: 样式和行为混杂,不利于维护;只影响单个元素,不适合批量或全局样式管理。
5.2 className
操作:"换套衣服",批量改变样式
如果你想给多个元素应用相同的样式,或者在不同状态下切换元素的样式,直接修改style
属性就显得很笨拙了。这时候,className
就派上用场了。它允许你通过修改元素的class
属性来应用预先定义好的CSS类。
生活例子: 就像你给一个班的学生统一换上校服,只需要告诉他们"穿校服",而不需要一个个去给他们量体裁衣。
代码解释:
html
<!-- index.html -->
<style>
.active { background-color: yellow; border: 2px solid orange; }
.inactive { background-color: lightgray; border: 1px solid gray; }
</style>
<div id="statusBox" class="inactive">当前状态:不活跃</div>
<button onclick="toggleStatus()">切换状态</button>
<script>
function toggleStatus() {
const statusBox = document.getElementById("statusBox");
if (statusBox.className === "inactive") {
statusBox.className = "active";
statusBox.textContent = "当前状态:活跃";
} else {
statusBox.className = "inactive";
statusBox.textContent = "当前状态:不活跃";
}
}
</script>
优点: 样式和行为分离,易于维护;可以批量管理样式。 缺点: 如果元素有多个类名,直接修改className
会覆盖掉所有原有类名,操作不便。
5.3 classList
的强大功能:"智能衣柜",轻松管理多套穿搭
为了解决className
操作多个类名不方便的问题,HTML5引入了classList
属性。它提供了一系列方法,让你像管理"智能衣柜"一样,轻松地添加、删除、切换元素的类名,而不用担心覆盖问题。
生活例子: 你的智能衣柜里有"运动装"、"正装"、"休闲装"等标签。你想穿运动装时,直接贴上"运动装"标签;想脱掉运动装时,撕掉标签;想从运动装换成正装,直接切换标签即可。
常用方法:
add('类名')
:添加一个类名。remove('类名')
:移除一个类名。toggle('类名')
:如果存在该类名则移除,不存在则添加。contains('类名')
:检查是否包含某个类名。
代码解释:
html
<!-- index.html -->
<style>
.highlight { background-color: yellow; }
.bold-text { font-weight: bold; }
.italic-text { font-style: italic; }
</style>
<p id="myParagraph" class="bold-text">这是一段文字。</p>
<button onclick="toggleHighlight()">切换高亮</button>
<button onclick="addItalic()">添加斜体</button>
<script>
const myParagraph = document.getElementById("myParagraph");
function toggleHighlight() {
myParagraph.classList.toggle("highlight"); // 切换highlight类
console.log("是否高亮:" + myParagraph.classList.contains("highlight"));
}
function addItalic() {
myParagraph.classList.add("italic-text"); // 添加italic-text类
console.log("是否斜体:" + myParagraph.classList.contains("italic-text"));
}
</script>
总结:
方法/属性 | 描述 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
element.style |
直接修改内联样式 | 简单直接,优先级高 | 样式和行为混杂,不利于维护;只影响单个元素 | 少量、临时的样式修改 |
className |
修改元素的class 属性 |
样式和行为分离,可批量管理 | 覆盖原有类名,操作不便 | 元素只有一个类名,或需要完全替换所有类名时 |
classList |
提供方法管理元素的类名 | 灵活方便,不覆盖原有类名,可管理多个类名 | 兼容性(IE9+) | 推荐使用,动态添加/移除/切换类名,管理复杂样式 |
在实际开发中,强烈推荐使用classList
来管理元素的样式。它既能保持样式和行为的分离,又提供了灵活便捷的操作方式,让你的代码更优雅、更易维护!
🏗️ 创建和删除元素 - 动态构建页面
除了修改现有元素,JavaScript操作DOM的"魔法"还能让你凭空"变"出新元素,或者"销毁"不再需要的元素。这就像你搭建乐高城堡,可以随时添加新的积木块,也可以拆掉不想要的。
6.1 createElement()
:"生产"新元素
createElement()
方法用于创建一个新的HTML元素节点。它只负责"生产",并不会把它添加到页面上,就像你生产了一个乐高积木,它还在你手里,没有放到城堡上。
语法:
javascript
document.createElement("标签名");
生活例子: 就像你想要一个新的杯子,你告诉工厂"给我生产一个杯子",工厂就生产出来了,但这个杯子还在工厂里,没到你手上。
代码解释:
html
<!-- index.html -->
<div id="container"></div>
<script>
const newDiv = document.createElement("div"); // 创建一个<div>元素
const newParagraph = document.createElement("p"); // 创建一个<p>元素
console.log(newDiv); // 输出:<div></div>
console.log(newParagraph); // 输出:<p></p>
// 此时,newDiv和newParagraph只是存在于内存中,还没有添加到页面上
</script>
6.2 appendChild()
:"添加"子元素
创建了新元素之后,我们通常需要把它添加到页面的某个位置。appendChild()
方法可以将一个节点添加到指定父节点的子节点列表的末尾。
语法:
javascript
父元素.appendChild(子元素);
生活例子: 就像你生产了一个新杯子,现在你想把它放到桌子上,你就把杯子放到桌子"里面"。
代码解释:
html
<!-- index.html -->
<div id="container">
<p>我是容器里的原有段落。</p>
</div>
<script>
const container = document.getElementById("container");
const newElement = document.createElement("span");
newElement.textContent = "我是新添加的文本!";
newElement.style.color = "red";
container.appendChild(newElement); // 将newElement添加到container的末尾
// 页面上container内部会变成:
// <p>我是容器里的原有段落。</p>
// <span>我是新添加的文本!</span>
</script>
6.3 removeChild()
:"删除"元素
当某个元素不再需要时,我们可以把它从页面上"移除"。removeChild()
方法可以从父节点中移除一个子节点。
语法:
javascript
父元素.removeChild(要移除的子元素);
生活例子: 就像你桌子上的杯子用完了,你把它从桌子上拿走。
代码解释:
html
<!-- index.html -->
<ul id="myList">
<li id="item1">项目1</li>
<li id="item2">项目2</li>
<li id="item3">项目3</li>
</ul>
<button onclick="removeItem()">删除项目2</button>
<script>
function removeItem() {
const myList = document.getElementById("myList");
const itemToRemove = document.getElementById("item2");
if (itemToRemove) { // 检查元素是否存在
myList.removeChild(itemToRemove); // 从myList中移除item2
}
}
</script>
注意: removeChild()
方法必须由父元素来调用,并传入要移除的子元素。如果你想移除一个元素本身,但不知道它的父元素,可以使用element.parentNode.removeChild(element)
。
6.4 动态构建页面:"留言板"小案例
结合createElement()
和appendChild()
,我们可以轻松实现动态添加内容的功能。比如,制作一个简单的留言板。
html
<!-- index.html -->
<div id="messageBoard">
<h2>留言板</h2>
<input type="text" id="messageInput" placeholder="请输入留言...">
<button id="addMessageBtn">发表留言</button>
<ul id="messageList"></ul>
</div>
<script>
const messageInput = document.getElementById("messageInput");
const addMessageBtn = document.getElementById("addMessageBtn");
const messageList = document.getElementById("messageList");
addMessageBtn.addEventListener("click", function() {
const messageText = messageInput.value.trim(); // 获取留言内容并去除首尾空格
if (messageText) { // 如果留言不为空
const newLi = document.createElement("li"); // 创建一个新的<li>元素
newLi.textContent = messageText; // 设置<li>的文本内容
messageList.appendChild(newLi); // 将新的<li>添加到留言列表的末尾
messageInput.value = ""; // 清空输入框
} else {
alert("留言内容不能为空!");
}
});
</script>
通过这个小案例,你是不是对JavaScript动态操作DOM有了更直观的感受呢?掌握了这些"生产"和"销毁"元素的方法,你就能随心所欲地构建和管理网页内容了!
🎯 事件处理 - 让页面"活"起来
网页不仅仅是展示内容的,它更是一个可以与用户"互动"的平台。而实现这种互动的"桥梁",就是事件。当用户点击按钮、输入文字、鼠标移动等等,这些都是"事件"。JavaScript能够"监听"这些事件,并在事件发生时执行相应的代码,从而让页面"活"起来!
7.1 什么是事件?- 网页的"信号"与"响应"
事件,简单来说,就是发生在HTML元素上的"事情"。比如:
- 你点击了一个按钮 (
click
事件)。 - 你把鼠标移到了图片上 (
mouseover
事件)。 - 你在输入框里输入了文字 (
input
事件或keydown
事件)。 - 页面加载完成了 (
load
事件)。
当这些"事情"发生时,浏览器会发出一个"信号",JavaScript就能"捕捉"到这个信号,然后执行我们预先写好的代码,这就是"响应"。
7.2 addEventListener()
:"事件监听器"的"耳朵"
在JavaScript中,我们使用addEventListener()
方法来"监听"事件。它就像给元素安装了一个"耳朵",一旦听到特定的"声音"(事件),就会立刻做出反应。
语法:
javascript
element.addEventListener("事件类型", function() { /* 事件发生时执行的代码 */ });
生活例子: 就像你给家里的门安装了一个门铃。当有人按门铃(click
事件)时,门铃就会响,你听到后(addEventListener
监听),就会去开门(执行代码)。
代码解释:
html
<!-- index.html -->
<button id="myButton">点我一下</button>
<p id="message"></p>
<script>
const myButton = document.getElementById("myButton");
const messageParagraph = document.getElementById("message");
// 监听按钮的点击事件
myButton.addEventListener("click", function() {
messageParagraph.textContent = "你点击了按钮!";
myButton.style.backgroundColor = "lightblue";
});
</script>
常见事件类型:
事件类型 | 描述 |
---|---|
click |
鼠标点击元素时触发 |
mouseover |
鼠标移到元素上时触发 |
mouseout |
鼠标从元素上移开时触发 |
keydown |
键盘按下时触发 |
keyup |
键盘松开时触发 |
change |
表单元素的值改变时触发 |
submit |
表单提交时触发 |
load |
页面或图片等资源加载完成时触发 |
7.3 事件对象:事件的"情报员"
当事件发生时,浏览器会创建一个事件对象 (通常命名为event
或e
),并把它作为参数传递给事件处理函数。这个事件对象就像一个"情报员",包含了关于这个事件的所有详细信息,比如:
event.target
:触发事件的那个元素。event.type
:事件的类型(比如click
)。event.clientX
,event.clientY
:鼠标点击时的X、Y坐标。event.keyCode
:键盘事件中按下的键的编码。event.preventDefault()
:阻止事件的默认行为(比如点击链接默认会跳转)。event.stopPropagation()
:阻止事件冒泡。
代码解释:
html
<!-- index.html -->
<a href="https://www.baidu.com" id="myLink">点击我跳转百度</a>
<input type="text" id="myInput" placeholder="在这里输入...">
<script>
const myLink = document.getElementById("myLink");
const myInput = document.getElementById("myInput");
myLink.addEventListener("click", function(event) {
event.preventDefault(); // 阻止默认跳转行为
alert("链接被点击了,但是不会跳转!");
console.log("点击的元素是:" + event.target.tagName); // 输出:A
});
myInput.addEventListener("keydown", function(event) {
console.log("你按下了键:" + event.key + ", 键码是:" + event.keyCode);
if (event.key === "Enter") {
alert("你按下了回车键!");
}
});
</script>
7.4 事件冒泡与事件捕获:事件的"传递路径"
当一个元素上的事件被触发时,这个事件并不会只停留在当前元素上,它会在DOM树中"旅行"。这个"旅行"有两种模式:事件冒泡 (Bubbling)和事件捕获(Capturing)。
事件冒泡(从里到外):
当一个元素上的事件被触发后,它会从这个元素开始,逐级向上(父元素、祖父元素...直到document
对象)"冒泡"。就像水底的气泡一样,从下往上浮。
生活例子: 你在家里不小心打碎了一个碗。你妈妈听到了声音,你奶奶也听到了声音,你爸爸也听到了声音,声音从你打碎碗的地方开始,一层一层地往上传。
事件捕获(从外到里):
与冒泡相反,事件捕获是从document
对象开始,逐级向下(子元素、孙子元素...直到目标元素)"捕获"。就像警察抓小偷,先从大范围开始搜查,然后逐渐缩小范围。
生活例子: 警察要抓一个小偷,他们会先封锁整个小区,然后进入楼栋,再进入房间,最后找到小偷。
addEventListener()
的第三个参数:
addEventListener()
方法有第三个可选参数,用于指定事件是在捕获阶段还是冒泡阶段触发。默认为false
,表示在冒泡阶段触发。
javascript
element.addEventListener("事件类型", function() { /* ... */ }, true); // 在捕获阶段触发
element.addEventListener("事件类型", function() { /* ... */ }, false); // 在冒泡阶段触发 (默认)
为什么要有这两种模式?
理解事件冒泡和捕获对于处理复杂的事件逻辑非常重要,特别是当涉及到事件委托(Event Delegation)时。
事件委托:"一个顶俩"的事件处理方式
事件委托是利用事件冒泡的特性,将子元素的事件监听器统一绑定到它们的父元素上。这样,当子元素触发事件时,事件会冒泡到父元素,由父元素来统一处理。
生活例子: 假设你是一个班主任,班里有50个学生。如果你想知道每个学生什么时候举手,你可以给每个学生都安排一个"监听员"。但这样太麻烦了!更好的办法是,你只需要自己坐在讲台上,"监听"所有学生举手这个"事件",当有学生举手时,你就能看到并处理。
优点:
- 减少内存消耗: 只需要给父元素添加一个事件监听器,而不是给每个子元素都添加。
- 动态添加元素也能生效: 对于后来动态添加的子元素,无需重新绑定事件,因为事件是绑定在父元素上的。
代码解释:
html
<!-- index.html -->
<ul id="myList">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
</ul>
<button id="addItem">添加新项目</button>
<script>
const myList = document.getElementById("myList");
const addItemButton = document.getElementById("addItem");
// 使用事件委托,将点击事件绑定到父元素ul上
myList.addEventListener("click", function(event) {
// 检查点击的是否是li元素
if (event.target.tagName === "LI") {
alert("你点击了:" + event.target.textContent);
event.target.style.color = "red";
}
});
addItemButton.addEventListener("click", function() {
const newLi = document.createElement("li");
newLi.textContent = "新项目" + (myList.children.length + 1);
myList.appendChild(newLi);
});
</script>
阻止事件冒泡:event.stopPropagation()
如果你不希望事件继续向上冒泡,可以使用event.stopPropagation()
方法。这就像你打碎碗后,立刻把声音"吸收"掉,不让妈妈和奶奶听到。
代码解释:
html
<!-- index.html -->
<div id="outer" style="padding: 20px; background-color: lightblue;">
外层Div
<button id="innerButton">内层按钮</button>
</div>
<script>
const outerDiv = document.getElementById("outer");
const innerButton = document.getElementById("innerButton");
outerDiv.addEventListener("click", function() {
console.log("外层Div被点击了!");
});
innerButton.addEventListener("click", function(event) {
console.log("内层按钮被点击了!");
event.stopPropagation(); // 阻止事件冒泡到外层Div
});
</script>
通过掌握事件处理,你就能让你的网页变得"聪明"起来,能够理解用户的意图并做出相应的反馈,从而提供更棒的用户体验!
💡 实战案例 - 综合应用
学了这么多DOM操作的"武功秘籍",是时候来一场实战演练了!通过一些综合案例,你会发现这些看似独立的知识点,组合起来能发挥出强大的威力。
8.1 制作一个简单的待办事项列表
待办事项列表(Todo List)是前端入门的经典案例,它能很好地展示DOM的增删改查以及事件处理。
功能需求:
- 输入待办事项,点击"添加"按钮后,将事项添加到列表中。
- 每个事项旁边有一个"删除"按钮,点击后可以删除该事项。
- 事项可以标记为"完成"或"未完成"(通过点击切换样式)。
HTML结构:
html
<!-- todo.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的待办事项</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; }
.container { max-width: 600px; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h1 { text-align: center; color: #333; }
.input-area { display: flex; margin-bottom: 20px; }
.input-area input { flex-grow: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px 0 0 4px; }
.input-area button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 0 4px 4px 0; cursor: pointer; }
.input-area button:hover { background-color: #0056b3; }
ul { list-style: none; padding: 0; }
li { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid #eee; }
li:last-child { border-bottom: none; }
li.completed span { text-decoration: line-through; color: #888; }
li button { background-color: #dc3545; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; }
li button:hover { background-color: #c82333; }
li span { flex-grow: 1; cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<h1>我的待办事项</h1>
<div class="input-area">
<input type="text" id="todoInput" placeholder="添加新的待办事项...">
<button id="addTodoBtn">添加</button>
</div>
<ul id="todoList"></ul>
</div>
<script>
const todoInput = document.getElementById('todoInput');
const addTodoBtn = document.getElementById('addTodoBtn');
const todoList = document.getElementById('todoList');
addTodoBtn.addEventListener('click', addTodo);
todoInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addTodo();
}
});
function addTodo() {
const todoText = todoInput.value.trim();
if (todoText === '') {
alert('待办事项不能为空!');
return;
}
const li = document.createElement('li');
const span = document.createElement('span');
span.textContent = todoText;
span.addEventListener('click', function() {
li.classList.toggle('completed');
});
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '删除';
deleteBtn.addEventListener('click', function() {
todoList.removeChild(li);
});
li.appendChild(span);
li.appendChild(deleteBtn);
todoList.appendChild(li);
todoInput.value = ''; // 清空输入框
}
</script>
</body>
</html>
代码解析:
- 获取元素: 首先获取输入框、添加按钮和待办事项列表的DOM元素。
- 添加事件监听: 给"添加"按钮和输入框的
keypress
事件(当按下回车键时)添加监听器,触发addTodo
函数。 addTodo
函数:- 获取输入框的值,并去除首尾空格。
- 如果为空,则弹出提示。
- 创建元素: 创建
<li>
(列表项)、<span>
(显示事项文本)和<button>
(删除按钮)元素。 - 设置内容和事件: 设置
<span>
的文本内容,并给<span>
添加点击事件,用于切换事项的completed
类(实现完成/未完成的样式切换)。给"删除"按钮添加点击事件,点击后从列表中移除对应的<li>
元素。 - 添加子元素: 将
<span>
和"删除"按钮添加到<li>
中。 - 添加到列表: 将
<li>
添加到<ul>
(待办事项列表)中。 - 清空输入框。
这个案例综合运用了DOM的查找、创建、修改(样式切换)、删除以及事件处理等核心操作,是巩固知识的绝佳实践!
执行结果:


总结与展望
恭喜你!🎉 经过这趟"DOM魔法"之旅,你已经掌握了JavaScript操作DOM的"核心秘籍"。从理解DOM的"家族树",到精准"找到"元素,再到"换装"、"调整设置"、"生产"、"销毁"元素,以及让页面"活"起来的事件处理,你已经具备了让网页动起来的能力!
DOM操作是前端开发的基础,也是实现各种复杂交互和动态效果的基石。虽然现在有很多前端框架(如React, Vue, Angular)帮助我们更高效地操作DOM,但理解原生DOM操作的原理,依然是成为一名优秀前端工程师的必备素养。
记住,实践是最好的老师! 动手尝试修改你喜欢的网页,用JavaScript改变它们的颜色、文字、图片,甚至添加一些小功能。你会发现,你的"魔法"正在一点点地生效!
希望这篇博客能为你打开前端开发的新世界大门。继续探索,继续创造,你的前端之路,才刚刚开始!