2.1 DOM 概念与节点树

DOM = Document Object Model(文档对象模型)

大白话:浏览器把 HTML 页面变成了一棵"树"——每个 HTML 标签、每段文字、每个属性都是树上的一个"节点"。JavaScript 通过这棵树来找到、读取、修改页面上的任何内容。

2.1.1 DOM 节点树层级结构

想象一下,整个 HTML 文档被浏览器变成了这样一棵"倒着长的树":

文档(document) └── 根节点 <html> ├── <head> 分支 │ ├── <meta> │ ├── <title> │ └── 文本节点:"页面标题" └── <body> 分支 ├── <h1> │ └── 文本节点:"你好世界" ├── <p> │ └── 文本节点:"这是一个段落" └── <div> ├── 文本节点:"内容" └── ...
  • 文档节点(document):整棵树的根,代表整个页面
  • 根节点<html>):树的"树干",下面分两个大分支
  • 元素节点:每个 HTML 标签(<head><body><h1><p> 等)
  • 文本节点:标签里面包着的文字
  • 属性节点:标签上的属性(如 class="red"id="main"
  • 注释节点<!-- 注释内容 -->

2.1.2 四大节点详解

四大节点表:
节点类型 大白话 举例
元素节点 HTML 标签本身 <h1><p><div>
文本节点 标签里的文字 "你好世界"、"这是一个段落"
属性节点 标签上的属性 class="red"id="main"href="#"
注释节点 HTML 注释 <!-- 这里是注释 -->

2.1.3 head 和 body 常用标签节点

<head> 分支下的常用节点

<head> ├── <meta charset="UTF-8"> ← 字符编码 ├── <meta name="viewport" ...> ← 移动端适配 ├── <title>页面标题</title> ← 标题(含文本节点) └── <link rel="stylesheet" href=""> ← 外部CSS

<body> 分支下的常用节点

<body> ├── <h1>一级标题</h1> ← 标题(含文本节点) ├── <p>段落文字</p> ← 段落 ├── <div>容器</div> ← 容器 ├── <ul>/<ol>/<li>列表</li></ul> ← 列表 ├── <a href="#">链接</a> ← 链接(含属性节点) ├── <img src="..." alt="..."> ← 图片(含属性节点) └── <input type="text"> ← 输入框
💡 记住:元素节点是最常用的操作对象——找到它,然后改它的内容、样式或属性。

2.2 获取元素节点

想操作页面元素,第一步是找到它。JavaScript 提供了几种"找人"的方法。

最常用的三种方法

获取元素三种方法表:
方法 用法 返回
getElementById('id名') 通过 id 找元素 一个元素
getElementsByTagName('标签名') 通过标签名找 多个元素(类数组)
getElementsByClassName('类名') 通过类名找 多个元素(类数组)
🎯 交互演示:点击按钮获取不同方式的元素

这是一个段落,有 id 属性。

这是第一个段落(class="text")

这是第二个段落(class="text")

  • 项目1
  • 项目2
  • 项目3
点击按钮查看获取结果...
1<h1>获取页面元素</h1> 2<p id="intro">这是一个段落,有 id 属性。</p> 3<p class="text">这是第一个段落(class="text")</p> 4<p class="text">这是第二个段落(class="text")</p> 5<ul> 6 <li>项目1</li> 7 <li>项目2</li> 8 <li>项目3</li> 9</ul> 10 11<script> 12 // 1. 通过 id 获取(最常用) 13 let intro = document.getElementById('intro'); 14 console.log(intro); 15 16 // 2. 通过标签名获取(返回一个集合) 17 let paragraphs = document.getElementsByTagName('p'); 18 console.log(paragraphs.length); // 3 19 console.log(paragraphs[0]); // 第一个 <p> 20 21 // 3. 通过类名获取 22 let textItems = document.getElementsByClassName('text'); 23 console.log(textItems.length); // 2 24 25 // 4. 获取所有 <li> 26 let items = document.getElementsByTagName('li'); 27 console.log('共有' + items.length + '个项目'); 28</script>
⚠️ 新手易错getElementById 是单数(Element),只返回一个;getElementsByTagName 是复数(Elements),返回多个。方法名必须写对,大小写敏感。
⚠️ 新手易错:通过 getElementsByTagNamegetElementsByClassName 获取到的是动态集合,如果页面元素变化,这个集合也会自动更新。

2.3 修改元素内容与属性

找到元素之后,就可以改它的内容或属性了。

2.3.1 修改内容

innerHTML 获取或设置元素里的 HTML 内容(可以包含标签)

innerText 获取或设置元素里的纯文本(标签会被忽略)

textContent 类似 innerText,但保留所有文本

🎯 交互演示:点击按钮修改文本和HTML内容

这是原始内容

当前内容:这是原始内容
innerHTML vs innerText 对比表:
属性 能识别 HTML 标签? 场景
innerHTML ✅ 能 需要插入带格式的内容
innerText ❌ 不能 只想改文字,防止注入
1<p id="message">原来的内容</p> 2<div id="info">这是 <strong>重要</strong> 信息</div> 3 4<script> 5 // 1. 修改纯文本 6 let msg = document.getElementById('message'); 7 msg.innerText = '这是新的内容!'; 8 console.log(msg.innerText); // 这是新的内容! 9 10 // 2. innerHTML 可以插入 HTML 标签 11 let info = document.getElementById('info'); 12 console.log(info.innerHTML); // 这是 <strong>重要</strong> 信息 13 console.log(info.innerText); // 这是 重要 信息(不带标签) 14 15 // 修改成带标签的内容 16 info.innerHTML = '这是 <span style="color:red;">更新后</span> 的信息'; 17</script>
⚠️ 新手易错innerHTML 如果用户输入的内容直接赋值进去,有安全风险。如果用 innerText,所有内容都会被当作纯文本,不会解析标签,更安全。

2.3.2 修改属性

常用属性操作表:
属性 示例 说明
href link.href = '...' 修改链接地址
src img.src = '...' 修改图片路径
alt img.alt = '...' 修改替代文字
title img.title = '...' 修改悬停提示
id div.id = 'newId' 修改 id
className div.className = 'newClass' 修改 class(注意不是 class
🎯 交互演示:修改链接和图片属性

百度一下 | href = https://www.baidu.com

示例图片
当前状态:未修改
1<a id="link" href="https://www.baidu.com">百度一下</a> 2<img id="photo" src="https://via.placeholder.com/150" alt="示例图片"> 3 4<script> 5 // 1. 修改链接的 href 属性和文本 6 let link = document.getElementById('link'); 7 link.href = 'https://www.google.com'; 8 link.innerText = '谷歌搜索'; 9 10 // 2. 修改图片的 src 和 alt 11 let photo = document.getElementById('photo'); 12 photo.src = 'https://via.placeholder.com/200'; 13 photo.alt = '新图片'; 14 photo.title = '鼠标悬停提示'; 15</script>
⚠️ 新手易错:修改 class 属性时,要用 className 而不是 class。因为 class 是 JavaScript 的保留字,所以 DOM 中用 className 代替。

2.4 样式操作

修改元素的 CSS 样式有三种方式。

方式一:直接修改 style 属性

🎯 交互演示:直接修改 style 属性

这段文字会变色 🎨

当前状态:默认样式
CSS 属性名称转换表:
CSS 属性 JavaScript 写法(驼峰命名)
background-color backgroundColor
font-size fontSize
margin-top marginTop
border-radius borderRadius
💡 规则:CSS 中的 - 去掉,后面首字母大写。background-colorbackgroundColor

方式二:通过 className 切换样式

元素.className = '类名' 可以整体替换元素的 class 属性。

1<style> 2 .highlight { 3 background-color: yellow; 4 font-weight: bold; 5 padding: 10px; 6 border: 2px solid orange; 7 } 8 .normal { 9 background-color: white; 10 padding: 10px; 11 } 12</style> 13 14<p id="text" class="normal">点击按钮会让这段文字高亮</p> 15<button onclick="toggleHighlight()">切换高亮</button> 16 17<script> 18 function toggleHighlight() { 19 let text = document.getElementById('text'); 20 if (text.className === 'normal') { 21 text.className = 'highlight'; 22 } else { 23 text.className = 'normal'; 24 } 25 } 26</script>

方式三:通过 classList 添加/移除类(推荐)

classList 提供了更灵活的类操作:

元素.classList.add('类名')   // 添加一个类

元素.classList.remove('类名')   // 移除一个类

元素.classList.toggle('类名')   // 切换(有就删,没有就加)

🎯 交互演示:classList add/remove/toggle
这是一个盒子(当前 class:空)
当前 class:""(空)
三种样式操作方式对比表:
方式 代码 适用场景
style 属性 div.style.color = 'red' 改变单个样式属性
className div.className = 'highlight' 整体切换预定义的样式类
classList div.classList.add('active') 推荐,灵活不覆盖原有类
⚠️ 新手易错:通过 style 设置 CSS 属性时,属性名用驼峰命名,不能带 -backgroundColor 不是 background-color
⚠️ 新手易错className = 'highlight'覆盖元素原来的所有 class。如果原来有 class="normal big",赋值后变成 class="highlight"。要用 classList.add() 来追加。

2.5 节点增删改

除了修改已有元素,JavaScript 还可以创建新元素、插入页面、删除元素

2.5.1 创建和插入节点

document.createElement('标签名') — 创建新元素

新元素.innerText = '...' — 添加内容

父元素.appendChild(新元素) — 放到最后

父元素.insertBefore(新元素, 参照元素) — 放到某个元素前面

2.5.2 删除节点

父元素.removeChild(子元素) — 删除子元素

元素.remove() — 直接删除自己(推荐)

2.5.3 替换节点

父元素.replaceChild(新元素, 旧元素)

🎯 交互演示:添加、删除、替换节点
  • 项目 A
  • 项目 B
  • 项目 C
当前列表:A, B, C
节点操作速查表:
操作 方法 说明
创建元素 document.createElement('标签') 创建新元素
添加内容 元素.innerText = '...' 设置文字
插入末尾 父元素.appendChild(子元素) 加到最后一个
插入前面 父元素.insertBefore(新, 参照) 加在某个元素前
删除子元素 父元素.removeChild(子元素) 删除指定的子元素
删除自己 元素.remove() 直接删除自己
替换子元素 父元素.replaceChild(新, 旧) 用新元素替换旧元素
1<ul id="list"> 2 <li>项目1</li> 3 <li>项目2</li> 4</ul> 5<button onclick="addItem()">添加项目</button> 6 7<script> 8 function addItem() { 9 // 1. 创建新的 <li> 元素 10 let newItem = document.createElement('li'); 11 12 // 2. 设置内容 13 newItem.innerText = '新项目'; 14 15 // 3. 获取父元素 16 let list = document.getElementById('list'); 17 18 // 4. 把新元素添加到列表末尾 19 list.appendChild(newItem); 20 } 21</script>
⚠️ 新手易错createElement 只是创建了"虚拟"的元素,还没放到页面上。必须用 appendChildinsertBefore 等方法插入到 DOM 树才能看到。
⚠️ 新手易错removeChild 需要先拿到父元素,再指定要删哪个子元素。而 元素.remove() 更直接,不需要知道父元素是谁。

2.6 基础事件

事件就是用户的操作(点击按钮、鼠标滑过、页面加载……),JavaScript 可以"监听"这些事件,然后做出响应。

2.6.1 点击事件(onclick)

最常用的事件——用户点击某个元素时触发。

方式一:在 HTML 中绑定(行内) <button onclick="alert('点')">点我</button>

方式二:在 JS 中绑定(推荐) btn.onclick = function() { ... };

2.6.2 页面载入事件(onload)

window.onload = function() { ... }; — 页面完全加载后执行。

如果你把 <script> 放在 <head> 里,脚本执行时 <body> 还没解析完,getElementById 就找不到元素。window.onload 保证等整个页面准备好了再执行

2.6.3 其他常见事件

🎯 交互演示:事件体验盒子(鼠标移入/移出/点击)
鼠标移过来
📡 事件记录:
事件类型表:
事件 触发时机 示例
onclick 点击元素 btn.onclick = fn
ondblclick 双击元素 btn.ondblclick = fn
onmouseover 鼠标移到元素上 div.onmouseover = fn
onmouseout 鼠标离开元素 div.onmouseout = fn
onchange 表单内容改变 input.onchange = fn
onfocus 元素获得焦点 input.onfocus = fn
onblur 元素失去焦点 input.onblur = fn
1<div id="box">鼠标移过来</div> 2<p id="log">事件记录:</p> 3 4<script> 5 let box = document.getElementById('box'); 6 let log = document.getElementById('log'); 7 8 // 鼠标移入 9 box.onmouseover = function() { 10 box.style.backgroundColor = '#e74c3c'; 11 box.innerText = '鼠标进来了'; 12 log.innerText = '事件记录:鼠标移入'; 13 }; 14 15 // 鼠标移出 16 box.onmouseout = function() { 17 box.style.backgroundColor = '#3498db'; 18 box.innerText = '鼠标移走了'; 19 log.innerText = '事件记录:鼠标移出'; 20 }; 21 22 // 点击 23 box.onclick = function() { 24 box.innerText = '点击!'; 25 log.innerText = '事件记录:点击了盒子'; 26 }; 27</script>
⚠️ 新手易错:事件绑定时写的是函数名或函数体,不能加括号调用。btn.onclick = myFunc ✅(把函数本身赋值),btn.onclick = myFunc() ❌(这样是直接把函数执行结果赋值,不是绑定事件)。
⚠️ 新手易错window.onload 只能绑定一个函数。如果再写一次 window.onload = function(){},会覆盖前一个。如果需要在页面加载后执行多个操作,可以把所有操作放在一个 onload 函数里。

2.7 简易实战

综合运用前面学到的知识,完成几个能直接运行的交互小项目。

📋 实战一:待办事项列表

点击按钮添加新的待办事项,点击事项可以删除它。

💡 试试输入内容后点击"添加",或按回车键
1<div class="todo-input"> 2 <input type="text" id="todoInput" placeholder="输入待办事项"> 3 <button onclick="addTodo()">添加</button> 4</div> 5<div id="todoList"></div> 6 7<script> 8 function addTodo() { 9 let input = document.getElementById('todoInput'); 10 let text = input.value.trim(); 11 12 if (text === '') { 13 alert('请输入内容'); 14 return; 15 } 16 17 addTodoItem(text); 18 input.value = ''; 19 } 20 21 function addTodoItem(text) { 22 let list = document.getElementById('todoList'); 23 let item = document.createElement('div'); 24 item.className = 'todo-item'; 25 26 let textSpan = document.createElement('span'); 27 textSpan.innerText = text; 28 29 let deleteBtn = document.createElement('button'); 30 deleteBtn.innerText = '删除'; 31 deleteBtn.onclick = function() { 32 list.removeChild(item); 33 }; 34 35 item.appendChild(textSpan); 36 item.appendChild(deleteBtn); 37 list.appendChild(item); 38 } 39</script>

🔢 实战二:点击计数器

每点一次按钮,数字加 1。点击归零可以重置。

0
当前计数:0
1<div class="count" id="counter">0</div> 2<button class="btn-add" onclick="increment()">+1</button> 3<button class="btn-reset" onclick="resetCount()">归零</button> 4 5<script> 6 let count = 0; 7 8 function increment() { 9 count++; 10 document.getElementById('counter').innerText = count; 11 } 12 13 function resetCount() { 14 count = 0; 15 document.getElementById('counter').innerText = count; 16 } 17</script>

🖼️ 实战三:简易图片轮播(手动切换)

点击"上一张"或"下一张"切换图片。

轮播图

第 1 / 5 张

1<div class="slider"> 2 <img id="sliderImg" src="..." alt="轮播图"> 3</div> 4<div class="controls"> 5 <button onclick="prevImage()">◀ 上一张</button> 6 <button onclick="nextImage()">下一张 ▶</button> 7</div> 8<p id="imageLabel">第 1 / 5 张</p> 9 10<script> 11 let images = [ 12 '图片1.jpg', 13 '图片2.jpg', 14 '图片3.jpg', 15 '图片4.jpg', 16 '图片5.jpg' 17 ]; 18 let currentIndex = 0; 19 let imgElement = document.getElementById('sliderImg'); 20 let label = document.getElementById('imageLabel'); 21 22 function updateImage() { 23 imgElement.src = images[currentIndex]; 24 label.innerText = '第 ' + (currentIndex + 1) + ' / ' + images.length + ' 张'; 25 } 26 27 function nextImage() { 28 currentIndex++; 29 if (currentIndex >= images.length) { 30 currentIndex = 0; 31 } 32 updateImage(); 33 } 34 35 function prevImage() { 36 currentIndex--; 37 if (currentIndex < 0) { 38 currentIndex = images.length - 1; 39 } 40 updateImage(); 41 } 42</script>
实战项目总结表:
项目 用到的知识点
待办事项 获取元素、创建元素、事件绑定、增删节点
点击计数器 变量、事件绑定、修改元素内容
图片轮播 数组、事件绑定、修改属性(src)、条件判断