第6天:用 JS 操作网页(DOM 与事件)
学习时间:约 1.5 小时
前置知识:第5天的 JS 对象(知道对象是什么、怎么读写属性就够了)
目录
- 桥接:从第5天到第6天
- 环境准备:怎么跑今天代码
- [DOM 是什么](#DOM 是什么)
- 获取页面元素
- 操作页面元素
- 事件:让页面有反应
- 创建和删除元素
- 练习
- [选学:ES6 模块](#选学:ES6 模块)
1. 桥接:从第5天到第6天
1.1 先回忆一下第5天
第5天你在学 操作 JS 对象:
js
// 第5天:你在内存里操作对象
const user = { name: '小明', age: 25 }
user.name = '大明' // 改属性
user.email = 'xm@test.com' // 加属性
console.log(user.name) // 读属性
这些对象存在 内存 里,你看不见它,只有 console.log 才能看到内容。
1.2 第6天要学什么
今天学的是 操作网页元素:
js
// 第6天:你要操作页面上的元素
const title = document.querySelector('h1') // 找到 <h1> 标签
title.textContent = '新标题' // 改它显示的文本
title.style.color = 'red' // 改它的颜色
这些元素 显示在浏览器页面上,你改完马上就能看到效果。
1.3 一句话总结区别
| 第5天 | 第6天 |
|---|---|
| 操作内存里的 JS 对象 | 操作页面上的 HTML 标签 |
user.name = '大明' |
title.textContent = '新标题' |
改完看不见,要 console.log |
改完页面直接变化 |
| 纯 JS 语法 | JS + 浏览器 API(DOM) |
今天的核心就是:用 JS 找到页面上的标签,然后修改它。
2. 环境准备:怎么跑今天代码
2.1 你需要两个文件
今天的代码需要 一个 HTML 文件 + 一个 JS 文件:
day6-js-dom/
├── index.html ← 页面结构
└── app.js ← 你的 JS 代码
2.2 创建 index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>第6天练习</title>
</head>
<body>
<h1 id="title">欢迎</h1>
<p class="text">这是一个段落。</p>
<button id="btn">点击我</button>
<!-- 重点:引入 JS 文件 -->
<script src="app.js"></script>
</body>
</html>
关键行 :<script src="app.js"></script>------这个标签把 JS 文件连接到 HTML 页面。必须放在 </body> 前面(等所有标签加载完再执行 JS)。
2.3 创建 app.js
js
// 随便写一行,验证连接成功
console.log('JS 文件已连接!')
2.4 运行方法
- 在 VS Code 里 :右键
index.html→ "Open with Live Server"(最方便,自动刷新) - 或者直接双击
index.html文件(用浏览器打开) - 按 F12 打开控制台(Console),看
console.log的输出
2.5 快速测试
把 app.js 改成下面的内容,刷新页面看效果:
js
// 找到 <h1> 标签
const title = document.querySelector('#title')
// 修改它的文字
title.textContent = 'Hello DOM!'
// 改颜色
title.style.color = 'blue'
如果页面没反应 :检查
index.html里有没有<script src="app.js"></script>,文件名拼写对不对,控制台报不报错。
3. DOM 是什么
3.1 最简单的理解
打开浏览器 F12 → Elements 面板,你看到的 HTML 结构就是 DOM。
DOM = 浏览器把 HTML 标签变成的一棵 JS 对象树。
比如这段 HTML:
html
<div id="app">
<h1>标题</h1>
<p class="text">段落</p>
</div>
浏览器会把它变成一棵树:
document(根对象)
└── div#app
├── h1 → "标题"
└── p.text → "段落"
每个 HTML 标签(<div>、<h1>、<p>)在 JS 里都是一个 对象。
3.2 用第5天的知识来理解
| 第5天的 JS 对象 | 第6天的 DOM 对象 |
|---|---|
你自己 {} 创建的 |
浏览器自动创建的 |
const user = { name: '小明' } |
<h1 id="title">标题</h1> |
user.name 读属性 |
title.textContent 读文本 |
user.name = '大明' |
title.textContent = '新标题' |
DOM 对象的本质就是对象 ------你在第5天学的增删改查,在 DOM 对象上一样能用,只是属性名不同(textContent 代替原来的 name)。
3.3 入口对象:document
js
console.log(document) // 整个页面的根对象
console.log(document.title) // 页面标题(<title> 的内容)
console.log(document.body) // <body> 标签
document 是浏览器给你的 全局对象 ,就像 console 一样,不用声明就能用。
4. 获取页面元素
在操作一个标签之前,先要 找到它。今天只学一个方法就够了:
4.1 querySelector --- CSS 选择器方式(推荐,一招吃遍天)
html
<div id="app">
<h1 class="title">标题</h1>
<p class="text">第一段</p>
<p class="text">第二段</p>
</div>
js
// 通过 ID 获取(用 #)
const app = document.querySelector('#app')
// 通过 class 获取(用 .)
const title = document.querySelector('.title')
// 通过标签名获取
const h1 = document.querySelector('h1')
// 复杂选择器(和 CSS 一样)
const firstText = document.querySelector('#app > p.text')
特点 :参数是 CSS 选择器,返回 第一个匹配 的元素,找不到返回 null。
4.2 querySelectorAll --- 获取所有匹配
js
// 获取所有 class="text" 的元素
const allTexts = document.querySelectorAll('.text')
console.log(allTexts) // NodeList [p.text, p.text]
console.log(allTexts.length) // 2
// 遍历(和数组差不多)
allTexts.forEach(el => {
console.log(el.textContent)
})
特点:返回所有匹配的元素(NodeList,类似数组),找不到返回空列表(不是 null)。
4.3 实战:验证你是否获取到了
js
const app = document.querySelector('#app')
// 如果选择器写错了,返回 null
console.log(app) // 写对了 → <div id="app">...</div>
// 写错了 → null
// 一定要判空!
if (app) {
console.log('找到了 app 元素')
} else {
console.log('没找到,检查选择器')
}
4.4 新手最容易犯的错
js
// ❌ 在 DOM 还没加载完就获取元素
const btn = document.querySelector('#btn') // null
btn.textContent = '按钮' // TypeError!
// ✅ 方案1:把 <script> 放在 </body> 前面(前面已讲)
// ✅ 方案2:等 DOM 加载完再执行
document.addEventListener('DOMContentLoaded', () => {
const btn = document.querySelector('#btn')
btn.textContent = '按钮' // 现在 safe 了
})
5. 操作页面元素
找到元素后,怎么改它?下面是最常用的 5 种操作。
5.1 改文本:textContent
html
<p id="msg">旧文本</p>
js
const msg = document.querySelector('#msg')
// 读文本
console.log(msg.textContent) // '旧文本'
// 改文本
msg.textContent = '新文本'
// 页面上立即变成"新文本"
5.2 改样式:style
html
<div id="box">我是一个盒子</div>
js
const box = document.querySelector('#box')
// 改行内样式(注意:CSS 属性名要转成驼峰)
box.style.color = 'red'
box.style.backgroundColor = '#f0f0f0' // 原来是 background-color
box.style.fontSize = '20px' // 原来是 font-size
box.style.padding = '20px'
box.style.borderRadius = '8px'
CSS 属性名 → JS 的转换规则:
| CSS | JS |
|---|---|
background-color |
style.backgroundColor |
font-size |
style.fontSize |
border-radius |
style.borderRadius |
z-index |
style.zIndex |
去掉横线,横线后的字母大写。
5.3 改 class:classList(推荐)
html
<div id="box" class="box active">盒子</div>
js
const box = document.querySelector('#box')
// 查看所有 class
console.log(box.className) // 'box active'
// classList 更灵活(像 Set<String>)
box.classList.add('hidden') // 添加 class
box.classList.remove('active') // 删除 class
box.classList.toggle('visible') // 有就删,没有就加(切换)
// 检查有没有某个 class
console.log(box.classList.contains('active')) // false(刚删了)
实际场景 :用 toggle 实现开关效果
js
// 每次点击按钮,切换 box 的 highlight 样式
const btn = document.querySelector('#btn')
const box = document.querySelector('#box')
btn.addEventListener('click', () => {
box.classList.toggle('highlight')
})
配合 CSS:
css
.highlight {
background-color: yellow;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
}
5.4 改属性
html
<input id="input" type="text" placeholder="请输入姓名" />
<img id="avatar" src="old.jpg" alt="头像" />
js
const input = document.querySelector('#input')
const img = document.querySelector('#avatar')
// 直接读写属性
input.placeholder = '请输入新姓名'
input.value = '小明' // 设置输入框的值
img.src = 'new.jpg' // 换图片
img.alt = '新头像'
// 自定义属性用 dataset(HTML 写 data-xxx)
// <button data-id="123" data-type="primary">按钮</button>
const btn = document.querySelector('button')
console.log(btn.dataset.id) // '123'
console.log(btn.dataset.type) // 'primary'
5.5 操作速查
| 想做什么 | 代码 |
|---|---|
| 改文字 | el.textContent = '新文字' |
| 改样式 | el.style.color = 'red' |
| 加 class | el.classList.add('active') |
| 删 class | el.classList.remove('active') |
| 切换 class | el.classList.toggle('active') |
| 改属性 | el.placeholder = '新提示' |
| 读用户输入 | input.value |
6. 事件:让页面有反应
改样式、改文本都是你主动操作的。但如果想 用户点了按钮才执行代码,就需要事件。
6.1 什么是事件
事件 = 用户做了某个动作 → 你写的代码自动执行。
js
// 翻译:当用户点击按钮时,执行这个函数
btn.addEventListener('click', () => {
console.log('按钮被点了')
})
这个模式叫 事件监听------你在"等"用户做某个动作,用户做了,你的代码就跑起来。
6.2 三种最常见的事件
html
<button id="btn">点我</button>
<input id="input" type="text" placeholder="输入..." />
<form id="form">
<button type="submit">提交</button>
</form>
js
// 1️⃣ 点击事件(最常用)
const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
console.log('按钮被点击了')
btn.textContent = '已点击' // 改了按钮文字
btn.style.background = 'green'
})
// 2️⃣ 输入事件(输入框内容变化时触发)
const input = document.querySelector('#input')
input.addEventListener('input', (e) => {
// e.target 是触发事件的元素,这里就是 input
console.log('当前输入:', e.target.value)
})
// 3️⃣ 表单提交事件
const form = document.querySelector('#form')
form.addEventListener('submit', (e) => {
e.preventDefault() // 阻止页面刷新(表单默认行为)
console.log('提交了,但不刷新页面')
})
6.3 事件对象(e)
事件处理函数会自动收到一个 事件对象 (通常叫 e 或 event):
js
btn.addEventListener('click', (e) => {
console.log(e.type) // 'click' --- 事件类型
console.log(e.target) // 你点的那个元素
console.log(e.currentTarget) // 绑定监听的元素(大多数时候和 target 一样)
e.preventDefault() // 阻止默认行为(比如链接跳转、表单刷新)
e.stopPropagation() // 阻止事件冒泡(后面讲)
})
记住两个最常用的:
| 方法/属性 | 作用 | 常用场景 |
|---|---|---|
e.target |
用户点的到底是哪个元素 | 事件委托(后面讲) |
e.preventDefault() |
阻止浏览器的默认行为 | 表单提交时不刷新页面 |
6.4 完整例子:点击按钮改内容
html
<div id="app">
<p id="display">原始内容</p>
<button id="changeBtn">点击修改</button>
</div>
js
const display = document.querySelector('#display')
const changeBtn = document.querySelector('#changeBtn')
changeBtn.addEventListener('click', () => {
display.textContent = '内容已被修改!'
display.style.color = 'red'
})
效果:点按钮 → 段落文字变了 + 变红色。
6.5 addEventListener vs onclick
js
// 方式 1:onclick(不推荐)
btn.onclick = () => console.log('第一次')
btn.onclick = () => console.log('第二次') // ❌ 覆盖了第一次
// 方式 2:addEventListener(推荐)
btn.addEventListener('click', () => console.log('第一次'))
btn.addEventListener('click', () => console.log('第二次')) // ✅ 两个都执行
永远用 addEventListener ,不要用 onclick/oninput 等 on 属性写法。
7. 创建和删除元素
7.1 创建新元素
js
// 创建一个 <p> 标签(还没放到页面上)
const p = document.createElement('p')
p.textContent = '我是新创建的段落'
p.className = 'new-text'
p.style.color = 'blue'
createElement 只是创建了元素,还在内存里,没显示在页面上。
7.2 把元素放到页面上
html
<ul id="list">
<li>第一项</li>
</ul>
js
const list = document.querySelector('#list')
// 创建新 li
const newLi = document.createElement('li')
newLi.textContent = '新添加的项'
// 追加到 ul 末尾(最常用)
list.appendChild(newLi)
// 页面多了:<li>新添加的项</li>
7.3 在指定位置插入
html
<ul id="list">
<li id="first">第一项</li>
<li id="second">第二项</li>
</ul>
js
const list = document.querySelector('#list')
const first = document.querySelector('#first')
const newLi = document.createElement('li')
newLi.textContent = '插入的项'
// 在 first 之前插入
list.insertBefore(newLi, first)
// 在 first 之后插入(更直观)
first.after(newLi)
// 在 first 之前插入
first.before(newLi)
7.4 删除元素
js
// 方式 1:元素自己删除(推荐,更简洁)
const el = document.querySelector('.some-class')
el.remove()
// 方式 2:通过父元素删除
const parent = document.querySelector('#list')
const child = document.querySelector('#first')
parent.removeChild(child)
7.5 完整例子:动态添加列表项
html
<div id="app">
<input id="input" type="text" placeholder="输入内容" />
<button id="addBtn">添加</button>
<ul id="list"></ul>
</div>
js
const input = document.querySelector('#input')
const addBtn = document.querySelector('#addBtn')
const list = document.querySelector('#list')
addBtn.addEventListener('click', () => {
const text = input.value
// 空输入不添加
if (text === '') return
// 创建 li
const li = document.createElement('li')
li.textContent = text
// 添加到列表
list.appendChild(li)
// 清空输入框
input.value = ''
})
这是今天第一个能跑起来的完整例子。自己敲一遍,看看效果。
8. 练习
练习 1:计数器
新建 counter.html 和 counter.js,用下面这个 HTML 结构:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>计数器</title>
</head>
<body>
<div id="app">
<button id="minus">-</button>
<span id="count">0</span>
<button id="plus">+</button>
</div>
<script src="counter.js"></script>
</body>
</html>
要求(JS 文件):
- 点击
+按钮,数字加 1 - 点击
-按钮,数字减 1 - 数字不能小于 0
提示:
js
const countEl = document.querySelector('#count')
// 读当前数字(textContent 是字符串,要转数字)
let count = Number(countEl.textContent)
练习 2:留言板
新建 message.html 和 message.js:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>留言板</title>
</head>
<body>
<div id="app">
<input id="msgInput" type="text" placeholder="写留言..." />
<button id="sendBtn">发送</button>
<ul id="msgList"></ul>
</div>
<script src="message.js"></script>
</body>
</html>
要求:
- 点击"发送"按钮,把输入内容添加到列表
- 输入为空时不能添加
- 点击列表项可以删除它(提示:
el.remove())
练习 3:开关灯
在练习 1 或 2 的基础上加一个功能:切换深色/浅色模式
html
<button id="themeToggle">切换深色模式</button>
js
const toggle = document.querySelector('#themeToggle')
toggle.addEventListener('click', () => {
// 在 body 上切换一个 class
document.body.classList.toggle('dark-mode')
})
CSS(写在 HTML 的 <style> 里):
css
.dark-mode {
background-color: #333;
color: white;
}
.dark-mode button {
background-color: #555;
color: white;
}
9. 选学:ES6 模块
这部分今天 可以跳过。DOM 操作不需要它,等开始用 Vue 时再回来学完全来得及。
9.1 模块解决什么问题
当一个 JS 文件太长时,你会想拆成多个文件:
js
// 不拆分:一个文件几千行,找东西很难
// 拆分后:按功能拆成小文件,各管各的
模块就是 JS 的 文件拆分方案。
9.2 导出和导入
js
// ===== utils.js =====
export const PI = 3.14159
export function add(a, b) {
return a + b
}
// ===== app.js =====
import { add, PI } from './utils.js'
console.log(add(1, 2)) // 3
console.log(PI) // 3.14159
9.3 要在 HTML 里开启模块模式
html
<script type="module" src="app.js"></script>
普通 script vs module script:
普通 <script> |
<script type="module"> |
|---|---|
不支持 import |
支持 import/export |
| 变量在全局作用域 | 变量在模块作用域 |
| 按顺序加载 | 自动延迟加载 |
9.4 默认导出
js
// ===== User.js =====
export default class User {
constructor(name) {
this.name = name
}
}
// ===== app.js =====
import User from './User.js'
const user = new User('小明')
9.5 先别深究
你只需要知道:
- JS 支持
import/export来拆分文件 - 用了模块的 script 要加
type="module" - 现在用不上,等学 Vue 时会大量用到
附:今日速查
js
// === 获取元素 ===
document.querySelector('#id') // 获取单个(用 CSS 选择器)
document.querySelectorAll('.cls') // 获取所有匹配
// === 操作元素 ===
el.textContent = '新文本' // 改文本
el.style.color = 'red' // 改样式
el.classList.add('active') // 添加 class
el.classList.remove('active') // 删除 class
el.classList.toggle('active') // 切换 class
el.value = '新值' // 改输入框内容
// === 创建和删除 ===
document.createElement('p') // 创建元素
parent.appendChild(child) // 追加到末尾
el.remove() // 删除自己
// === 事件 ===
el.addEventListener('click', fn) // 添加点击事件
el.addEventListener('input', fn) // 输入框内容变化
e.target // 用户点的元素
e.preventDefault() // 阻止默认行为