JavaScript WebAPI 入门详解:从 DOM 操作到事件驱动,一篇文章搞懂网页交互
往期回顾
目录
- 前言
- [一、WebAPI 是什么?](#一、WebAPI 是什么?)
- [二、API 可以理解成程序员的工具箱](#二、API 可以理解成程序员的工具箱)
- [三、WebAPI、ECMAScript、DOM、BOM 的关系](#三、WebAPI、ECMAScript、DOM、BOM 的关系)
- [四、DOM 是什么?](#四、DOM 是什么?)
- [五、DOM 树:把 HTML 页面看成一棵树](#五、DOM 树:把 HTML 页面看成一棵树)
- [六、DOM 中的文档、元素和节点](#六、DOM 中的文档、元素和节点)
- [七、获取元素:querySelector 和 querySelectorAll](#七、获取元素:querySelector 和 querySelectorAll)
- 八、事件初识:网页为什么能响应用户操作?
- 九、事件三要素:事件源、事件类型、事件处理程序
- [十、onclick 和 addEventListener 的区别](#十、onclick 和 addEventListener 的区别)
- 十一、操作元素内容:innerText、textContent、innerHTML
- 十二、操作元素属性:图片切换案例
- 十三、操作表单元素:value、checked、disabled
- 十四、全选和取消全选案例
- 十五、操作元素样式:style、className、classList
- 十六、操作节点:创建、插入和删除元素
- [十七、事件对象和 this](#十七、事件对象和 this)
- 十八、事件冒泡与事件委托
- 十九、DOMContentLoaded:等页面结构加载完再执行
- [二十、BOM 简介:操作浏览器窗口](#二十、BOM 简介:操作浏览器窗口)
- 二十一、综合案例一:猜数字小游戏
- 二十二、综合案例二:表白墙
- [二十三、综合案例三:待办事项 TodoList](#二十三、综合案例三:待办事项 TodoList)
- [二十四、WebAPI 常见坑点总结](#二十四、WebAPI 常见坑点总结)
- 全文总结
前言
前面学习 JavaScript 基础语法时,我们已经学过了:
变量,数据类型,运算符,条件语句,循环语句,数组,函数,对象,作用域
这些内容属于 JavaScript 的基础语法部分
但是,只会基础语法还不够。
因为网页不是只做数学计算,也不是只在控制台打印几行内容。
真正的网页要能和用户交互,比如:
点击按钮弹出提示,输入内容后提交,点击图片切换图片
这些功能都需要用到一个叫:
WebAPI
如果说 ECMAScript 是"内功心法",帮我们建立基本编程能力;
那么 WebAPI 就像"招式工具箱",让我们真正可以操作页面、响应用户、控制浏览器。
这篇文章就带着大家系统梳理 JavaScript WebAPI 的核心知识点。
一、WebAPI 是什么?
WebAPI 可以简单理解成:
浏览器提供给 JavaScript 使用的一批现成对象和方法。
我们写 JS 时,经常会看到这些代码:
javascript
document.querySelector('.box');
element.innerHTML = 'hello';
button.onclick = function () {};
window.alert('你好');
location.href = 'https://www.baidu.com';
这里的 document、window、location、querySelector、innerHTML 等,都不是我们自己定义的。
它们是浏览器环境提前提供好的能力。
这些能力就属于 WebAPI 的范畴。
WebAPI 主要包含两大类
text
DOM API:操作网页内容、结构和样式
BOM API:操作浏览器窗口、地址栏、历史记录等
可以简单记成:
text
DOM:管页面
BOM:管浏览器
二、API 可以理解成程序员的工具箱
API 的全称是:
Application Programming Interface
应用程序编程接口
这个名字听起来很抽象。
其实可以简单理解成:
别人已经写好的工具,我们按照规则拿来用。
比如你想在页面中找到一个按钮。
不需要自己从 HTML 源码里一个字符一个字符分析。
浏览器已经给你准备好了工具:
javascript
document.querySelector('button');
你想创建一个新元素,也不需要自己手动拼接浏览器内部结构。
浏览器提供了工具:
javascript
document.createElement('div');
你想监听按钮点击,也不用自己一直检查鼠标状态。
浏览器提供了事件机制:
javascript
button.addEventListener('click', function () {
console.log('按钮被点击了');
});
所以 API 就像工具箱。
三、WebAPI、ECMAScript、DOM、BOM 的关系
浏览器端 JavaScript 可以拆成三部分:
text
JavaScript = ECMAScript + DOM + BOM
1. ECMAScript:基础语法
ECMAScript 负责语言本身。
比如:
javascript
let age = 18;
if (age >= 18) {
console.log('成年人');
}
这里用到的是变量、判断、输出等基础语法。
2. DOM:操作页面
DOM 负责操作网页。
比如:
javascript
let title = document.querySelector('h1');
title.innerText = '新的标题';
这段代码可以修改页面中的标题。
3. BOM:操作浏览器
BOM 负责操作浏览器窗口相关内容。
比如:
javascript
alert('提示框');
console.log(location.href);
history.back();
这些能力和浏览器窗口、地址栏、历史记录有关。
四、DOM 是什么?
DOM 的全称是:
text
Document Object Model
文档对象模型
DOM 的作用是:
把 HTML 页面中的内容,转换成 JavaScript 可以操作的对象。
例如页面中有一个标题:
html
<h1>我的标题</h1>
浏览器会把这个标签转换成一个对象。
然后我们就可以用 JS 操作它:
javascript
let h1 = document.querySelector('h1');
h1.innerText = '新的标题';
这就是 DOM 的意义。
DOM 能做什么?
DOM 可以让我们操作:
text
网页内容
网页结构
网页样式
网页属性
网页事件
比如:
javascript
// 修改文本
title.innerText = 'hello';
// 修改样式
title.style.color = 'red';
// 修改属性
img.src = './new.png';
// 新增元素
document.createElement('div');
// 删除元素
parent.removeChild(child);
五、DOM 树:把 HTML 页面看成一棵树
HTML 标签本身是有层级关系的。
例如:
html
<html>
<head>
<title>页面标题</title>
</head>
<body>
<h1>我的标题</h1>
<a href="#">我的链接</a>
</body>
</html>
这段结构可以画成一棵树:
text
document
└── html
├── head
│ └── title
└── body
├── h1
└── a
这棵树就叫:
text
DOM 树
为什么要理解 DOM 树?
因为我们操作页面,本质上就是在操作这棵树。
例如:
javascript
document.querySelector('h1');
就是从 DOM 树中查找 h1 元素。
javascript
container.appendChild(newDiv);
就是把 newDiv 挂到 container 这个节点下面。
javascript
parent.removeChild(child);
就是从父节点下面移除某个子节点。
所以 DOM 操作的本质就是:
增删查改 DOM 树上的节点。
六、DOM 中的文档、元素和节点
学习 DOM 时,经常会遇到三个词:
text
document
element
node
1. 文档 document
一个网页就是一个文档。
在 JS 中,整个页面用 document 表示。
javascript
console.log(document);
可以把 document 理解成整个网页的总入口。
2. 元素 element
页面中的 HTML 标签通常称为元素。
比如:
html
div
p
h1
img
input
button
这些标签在 JS 中都会对应一个个 Element 对象。
3. 节点 node
节点是更广义的概念。
网页里的很多内容都可以称为节点:
text
元素节点
文本节点
注释节点
属性节点
文档节点
可以这样理解:
text
node 是大概念
element 是 node 的一种
七、获取元素:querySelector 和 querySelectorAll
操作元素之前,第一步通常是:
先把元素拿到手。
就像你要去找一个人,肯定是先去拿到那个人目前的位置。
1. querySelector
querySelector 用来获取匹配 CSS 选择器的第一个元素。
javascript
let element = document.querySelector('选择器');
示例:
html
<div class="box">abc</div>
<div id="title">def</div>
<h3><span><input type="text"></span></h3>
<script>
let elem1 = document.querySelector('.box');
console.log(elem1);
let elem2 = document.querySelector('#title');
console.log(elem2);
let elem3 = document.querySelector('h3 span input');
console.log(elem3);
</script>
注意:
text
.box 表示类选择器
#title 表示 id 选择器
h3 span input 表示后代选择器
也就是说,querySelector 直接复用了 CSS 选择器知识。
2. querySelectorAll
querySelectorAll 用来获取匹配选择器的所有元素。
javascript
let elements = document.querySelectorAll('div');
示例:
html
<div>第一个 div</div>
<div>第二个 div</div>
<div>第三个 div</div>
<script>
let divs = document.querySelectorAll('div');
console.log(divs);
</script>
得到的是一个类似数组的结构。
可以使用下标访问:
javascript
console.log(divs[0]);
console.log(divs[1]);
也可以循环遍历:
javascript
for (let i = 0; i < divs.length; i++) {
console.log(divs[i]);
}
3. querySelector 和 querySelectorAll 对比
| 方法 | 作用 | 返回结果 |
|---|---|---|
querySelector |
获取第一个匹配元素 | 单个元素或 null |
querySelectorAll |
获取所有匹配元素 | NodeList |
八、事件初识:网页为什么能响应用户操作?
网页之所以能动起来,很大程度上靠的是:
事件
用户在页面上的操作会产生事件。
比如:
text
点击按钮
输入文字
勾选复选框
移动鼠标
按下键盘
滚动页面
提交表单
浏览器会监听这些行为。
一旦用户做了某个操作,浏览器就会触发对应事件。
然后 JS 可以响应这个事件,执行对应逻辑。
换句话说:浏览器就像城墙上的哨兵。
用户操作页面,就像敌情出现。
一旦哨兵发现敌情,就点燃烽火台。
后方根据烽火信号,执行对应策略。
在网页里:
text
用户操作:点击按钮
浏览器发现:触发 click 事件
JS 响应:执行点击后的函数
九、事件三要素:事件源、事件类型、事件处理程序
事件有三个核心要素:
text
1. 事件源
2. 事件类型
3. 事件处理程序
1. 事件源
事件源就是谁触发了事件。
比如按钮被点击:
html
<button id="btn">点我一下</button>
这里按钮就是事件源。
2. 事件类型
事件类型就是发生了什么操作。
比如:
text
click:点击
input:输入
change:内容改变
mouseover:鼠标移入
mouseout:鼠标移出
keydown:键盘按下
submit:表单提交
3. 事件处理程序
事件处理程序就是事件发生后要执行的代码。
通常是一个函数。
javascript
let btn = document.querySelector('#btn');
btn.onclick = function () {
alert('hello world');
};
这里:
text
事件源:btn
事件类型:click
事件处理程序:function () { alert(...) }
十、onclick 和 addEventListener 的区别
注册事件有两种常见方式:
text
onclick
addEventListener
1. onclick 写法
javascript
let btn = document.querySelector('button');
btn.onclick = function () {
console.log('按钮被点击了');
};
这种写法简单,适合入门。
但它有一个问题:
javascript
btn.onclick = function () {
console.log('第一次');
};
btn.onclick = function () {
console.log('第二次');
};
第二次会覆盖第一次。
也就是说,一个事件类型上只能保留最后一次赋值。
2. addEventListener 写法
javascript
let btn = document.querySelector('button');
btn.addEventListener('click', function () {
console.log('第一次');
});
btn.addEventListener('click', function () {
console.log('第二次');
});
这种写法可以给同一个元素的同一种事件绑定多个处理函数。
所以实际开发中,更推荐addEventListener
3. 推荐写法
javascript
btn.addEventListener('click', function () {
console.log('按钮被点击了');
});
可以读成:
text
当 btn 发生 click 事件时,执行后面的函数。
十一、操作元素内容:innerText、textContent、innerHTML
获取元素后,就可以修改元素内容。
常见属性有三个:
text
innerText
textContent
innerHTML
1. innerText
innerText 获取或设置元素的可见文本内容。
html
<div>
<span>hello world</span>
</div>
<script>
let div = document.querySelector('div');
console.log(div.innerText);
div.innerText = 'hello js <span>test</span>';
</script>
注意:
text
innerText 不会把 <span> 当成标签解析
而是当成普通文本显示
2. textContent
textContent 也用于获取或设置文本内容。
它通常更接近节点的纯文本内容,不关心 CSS 是否让内容可见。
javascript
div.textContent = 'hello textContent';
入门阶段可以简单记:
text
只想设置纯文本:textContent 或 innerText
3. innerHTML
innerHTML 可以获取或设置元素内部的 HTML 结构。
html
<div>
<span>hello world</span>
</div>
<script>
let div = document.querySelector('div');
console.log(div.innerHTML);
div.innerHTML = '<span>hello js</span>';
</script>
这时 <span> 会被当成真正的 HTML 标签解析。
4. 三者对比
| 属性 | 是否识别 HTML 标签 | 常见用途 |
|---|---|---|
innerText |
不识别 | 修改可见文本 |
textContent |
不识别 | 修改纯文本 |
innerHTML |
识别 | 修改 HTML 结构 |
5. 安全提醒
如果内容来自用户输入,不要随便使用 innerHTML 拼接。
例如:
javascript
div.innerHTML = userInput;
如果 userInput 中包含恶意脚本,就可能造成安全问题。
更安全的方式是:
javascript
div.textContent = userInput;
十二、操作元素属性:图片切换案例
元素的很多 HTML 属性,在 JS 中也可以直接访问和修改。
例如图片:
html
<img src="rose.jpg" alt="这是一朵花" title="玫瑰花">
可以这样获取:
javascript
let img = document.querySelector('img');
console.log(img.src);
console.log(img.alt);
console.log(img.title);
也可以直接修改:
javascript
img.src = './rose2.png';
图片切换案例
需求:
text
点击图片,在 rose.jpg 和 rose2.png 之间切换。
代码:
html
<img src="./rose.jpg" alt="玫瑰花" title="点击切换图片">
<script>
let img = document.querySelector('img');
img.addEventListener('click', function () {
if (img.src.indexOf('rose.jpg') !== -1) {
img.src = './rose2.png';
} else {
img.src = './rose.jpg';
}
});
</script>
这里的核心逻辑是:
text
点击图片
判断当前图片路径
切换成另一张图片
十三、操作表单元素:value、checked、disabled
表单元素是网页交互中非常重要的一类元素。
常见属性包括:
text
value:表单值
checked:复选框或单选框是否选中
disabled:是否禁用
selected:下拉选项是否选中
type:input 类型
1. value:获取输入框内容
html
<input type="text" id="username" value="张三">
<script>
let input = document.querySelector('#username');
console.log(input.value);
input.value = '李四';
</script>
如果用户在页面上修改输入框内容,input.value 也会随之变化。
2. 按钮文字切换
需求:
text
点击按钮,在"播放"和"暂停"之间切换。
html
<input type="button" value="播放">
<script>
let btn = document.querySelector('input');
btn.addEventListener('click', function () {
if (btn.value === '播放') {
btn.value = '暂停';
} else {
btn.value = '播放';
}
});
</script>
3. 点击计数案例
需求:
text
输入框中有一个数字,每次点击按钮,数字 +1。
html
<input type="text" id="num" value="0">
<input type="button" id="btn" value="点我 +1">
<script>
let numInput = document.querySelector('#num');
let btn = document.querySelector('#btn');
btn.addEventListener('click', function () {
let num = Number(numInput.value);
num++;
numInput.value = num;
});
</script>
注意我们这里写了Number(numInput.value)
因为输入框里的内容默认是字符串。
如果想做数字运算,需要先转成数字。
十四、全选和取消全选案例
需求:
text
1. 点击"全选",下面所有选项都被选中
2. 取消"全选",下面所有选项都取消
3. 只要下面有一个没选中,"全选"也要自动取消
HTML 结构
html
<input type="checkbox" id="all">我全都要 <br>
<input type="checkbox" class="item">HTML <br>
<input type="checkbox" class="item">CSS <br>
<input type="checkbox" class="item">JavaScript <br>
<input type="checkbox" class="item">Vue <br>
JS 实现
html
<script>
let all = document.querySelector('#all');
let items = document.querySelectorAll('.item');
all.addEventListener('click', function () {
for (let i = 0; i < items.length; i++) {
items[i].checked = all.checked;
}
});
for (let i = 0; i < items.length; i++) {
items[i].addEventListener('click', function () {
all.checked = checkAll(items);
});
}
function checkAll(items) {
for (let i = 0; i < items.length; i++) {
if (!items[i].checked) {
return false;
}
}
return true;
}
</script>
十五、操作元素样式:style、className、classList
JS 可以修改 CSS 样式。
常见方式有三种:
text
style
className
classList
1. style:修改少量行内样式
html
<div>点击我变大</div>
<script>
let div = document.querySelector('div');
div.addEventListener('click', function () {
div.style.fontSize = '30px';
div.style.color = 'red';
div.style.backgroundColor = 'yellow';
});
</script>
2. className:整体替换类名
html
<div class="box light">这是一段话</div>
javascript
div.className = 'box dark';
className 会整体替换元素的 class。
如果原来有多个类名,要注意别把不该删的类删掉。
3. classList:更推荐的类名操作方式
现代开发中,更推荐使用 classList。
javascript
div.classList.add('dark');
div.classList.remove('light');
div.classList.toggle('active');
div.classList.contains('dark');
它比 className 更精细。
4. 夜间模式案例
HTML:
html
<div class="container light">
这是一大段话。<br>
点击切换日间 / 夜间模式。
</div>
CSS:
css
html,
body {
width: 100%;
height: 100%;
}
.container {
width: 100%;
height: 100%;
}
.light {
background-color: #f3f3f3;
color: #333;
}
.dark {
background-color: #333;
color: #f3f3f3;
}
JS:
html
<script>
let container = document.querySelector('.container');
container.addEventListener('click', function () {
container.classList.toggle('dark');
container.classList.toggle('light');
});
</script>
这样点击页面时,就能在日间模式和夜间模式之间切换。
十六、操作节点:创建、插入和删除元素
动态页面经常需要新增或删除内容。
比如:
text
发布一条留言
新增一个任务
删除一条评论
添加一行表格
这些都属于节点操作。
1. 创建元素节点
javascript
let div = document.createElement('div');
此时只是创建了一个新元素。
注意:
创建出来不代表页面上能看到。
因为它还没有被加入 DOM 树。
2. 设置新元素内容和属性
javascript
let div = document.createElement('div');
div.id = 'mydiv';
div.className = 'box';
div.innerText = '我是新创建的 div';
3. appendChild:插入到最后
javascript
let container = document.querySelector('.container');
container.appendChild(div);
appendChild 会把新节点插入到父节点最后面。
4. insertBefore:插入到指定节点前面
javascript
parentNode.insertBefore(newNode, referenceNode);
示例:
html
<div class="container">
<div>11</div>
<div>22</div>
<div>33</div>
</div>
<script>
let newDiv = document.createElement('div');
newDiv.innerText = '我是新节点';
let container = document.querySelector('.container');
container.insertBefore(newDiv, container.children[0]);
</script>
这会把新节点插入到第一个子元素前面。
5. 一个节点不能同时在两个地方
如果把同一个节点插入两次,本质上不是复制,而是移动。
javascript
container.appendChild(newDiv);
anotherContainer.appendChild(newDiv);
最终 newDiv 会出现在 anotherContainer 中。
因为一个真实 DOM 节点只能有一个父节点。
6. removeChild:删除子节点
javascript
parent.removeChild(child);
示例:
html
<div class="container">
<div class="item">我要被删除</div>
</div>
<script>
let container = document.querySelector('.container');
let item = document.querySelector('.item');
container.removeChild(item);
</script>
注意:
text
removeChild 删除的是子节点
必须通过父节点删除子节点
十七、事件对象和 this
1. 事件对象 event
当事件发生时,浏览器会生成一个事件对象。
这个对象里保存了事件相关信息。
javascript
let btn = document.querySelector('button');
btn.addEventListener('click', function (event) {
console.log(event);
});
事件对象里常见信息包括:
text
事件类型
触发事件的元素
鼠标位置
键盘按键
是否阻止默认行为
是否阻止冒泡
2. this 表示当前处理事件的元素
javascript
let btn = document.querySelector('button');
btn.addEventListener('click', function () {
console.log(this);
});
在普通函数形式的事件处理程序中,this 通常指向当前绑定事件的元素。
例如:
javascript
checkbox.addEventListener('click', function () {
let row = this.parentNode;
});
这里 this 就是当前被点击的复选框。
3. 箭头函数中的 this 要小心
javascript
btn.addEventListener('click', () => {
console.log(this);
});
箭头函数没有自己的 this。
所以如果你需要在事件处理函数中使用当前元素,初学阶段建议先用普通函数:
javascript
btn.addEventListener('click', function () {
console.log(this);
});
十八、事件冒泡与事件委托
1. 什么是事件冒泡?
HTML 元素是嵌套的。
例如:
html
<div class="father">
<button class="son">点我</button>
</div>
当点击按钮时,事件会先发生在按钮上,然后往外层父元素传播。
这个过程叫:
text
事件冒泡
可以理解为:
text
button -> div -> body -> html -> document
2. 阻止冒泡
javascript
button.addEventListener('click', function (event) {
event.stopPropagation();
});
3. 什么是事件委托?
事件委托就是:
不给每个子元素单独绑定事件,而是把事件绑定到父元素上,通过事件冒泡统一处理。
例如:
html
<ul id="list">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
javascript
let list = document.querySelector('#list');
list.addEventListener('click', function (event) {
if (event.target.tagName === 'LI') {
console.log('点击了:' + event.target.innerText);
}
});
这样做的好处:
text
减少事件绑定数量
适合动态新增的元素
代码更容易维护
十九、DOMContentLoaded:等页面结构加载完再执行
有时候 JS 代码写在 HTML 元素前面,会出现找不到元素的问题。
错误示例:
html
<script>
let btn = document.querySelector('button');
console.log(btn); // 可能是 null
</script>
<button>点我</button>
原因是:
text
JS 执行时,button 还没被浏览器解析到。
解决方式一:把 script 放到 body 结束标签前面。
html
<body>
<button>点我</button>
<script>
let btn = document.querySelector('button');
console.log(btn);
</script>
</body>
解决方式二:使用 DOMContentLoaded。
javascript
document.addEventListener('DOMContentLoaded', function () {
let btn = document.querySelector('button');
console.log(btn);
});
它表示:
text
等 DOM 结构加载完,再执行 JS。
二十、BOM 简介:操作浏览器窗口
BOM 是:
text
Browser Object Model
浏览器对象模型
它主要用于操作浏览器相关内容。
1. window 对象
window 是浏览器中的全局对象。
javascript
window.alert('hello');
平时可以省略 window:
javascript
alert('hello');
2. location 对象
location 和地址栏有关。
javascript
console.log(location.href);
跳转页面:
javascript
location.href = 'https://www.baidu.com';
刷新页面:
javascript
location.reload();
3. history 对象
history 和浏览器历史记录有关。
javascript
history.back();
history.forward();
4. 定时器
延迟执行:
javascript
setTimeout(function () {
console.log('2 秒后执行');
}, 2000);
重复执行:
javascript
setInterval(function () {
console.log('每 1 秒执行一次');
}, 1000);
停止定时器:
javascript
let timer = setInterval(function () {
console.log('running');
}, 1000);
clearInterval(timer);
二十一、综合案例一:猜数字小游戏
需求:
- 浏览器随机生成 1~100 的数字
- 用户输入猜测数字
- 点击按钮后提示猜大了、猜小了、猜对了
- 记录已经猜的次数
- 支持重新开始
完整代码
html
<button type="button" id="reset">重新开始一局游戏</button>
<br>
请输入要猜的数字:
<input type="text" id="number">
<button type="button" id="button">猜</button>
<br>
已经猜的次数:
<span id="count">0</span>
<br>
结果:
<span id="result"></span>
<script>
let inputE = document.querySelector('#number');
let countE = document.querySelector('#count');
let resultE = document.querySelector('#result');
let btn = document.querySelector('#button');
let resetBtn = document.querySelector('#reset');
let guessNumber = Math.floor(Math.random() * 100) + 1;
let count = 0;
btn.addEventListener('click', function () {
count++;
countE.innerText = count;
let userGuess = parseInt(inputE.value);
if (userGuess === guessNumber) {
resultE.innerText = '猜对了';
resultE.style.color = 'gray';
} else if (userGuess < guessNumber) {
resultE.innerText = '猜小了';
resultE.style.color = 'blue';
} else {
resultE.innerText = '猜大了';
resultE.style.color = 'red';
}
});
resetBtn.addEventListener('click', function () {
guessNumber = Math.floor(Math.random() * 100) + 1;
count = 0;
countE.innerText = count;
resultE.innerText = '';
inputE.value = '';
});
</script>
二十二、综合案例二:表白墙
需求:
- 输入"谁"
- 输入"对谁"
- 输入"说什么"
- 点击提交后,把信息显示到页面下方
- 提交后清空输入框
完整代码
html
<div class="container">
<h1>表白墙</h1>
<p>输入后点击提交,会将信息显示在表格中</p>
<div class="row">
<span>谁:</span>
<input class="edit" type="text">
</div>
<div class="row">
<span>对谁:</span>
<input class="edit" type="text">
</div>
<div class="row">
<span>说什么:</span>
<input class="edit" type="text">
</div>
<div class="row">
<input type="button" value="提交" class="submit">
</div>
</div>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 400px;
margin: 0 auto;
}
h1 {
text-align: center;
padding: 20px 0;
}
p {
color: #666;
text-align: center;
font-size: 14px;
padding: 10px 0;
}
.row {
height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
span {
width: 100px;
line-height: 40px;
}
.edit {
width: 200px;
height: 30px;
}
.submit {
width: 304px;
height: 40px;
color: white;
background-color: orange;
border: none;
}
.submit:active {
background-color: #666;
}
</style>
<script>
let submit = document.querySelector('.submit');
submit.addEventListener('click', function () {
let edits = document.querySelectorAll('.edit');
let from = edits[0].value;
let to = edits[1].value;
let message = edits[2].value;
if (from === '' || to === '' || message === '') {
return;
}
let row = document.createElement('div');
row.className = 'row';
row.textContent = from + ' 对 ' + to + ' 说:' + message;
let container = document.querySelector('.container');
container.appendChild(row);
for (let i = 0; i < edits.length; i++) {
edits[i].value = '';
}
});
</script>
这里我使用了:
javascript
row.textContent
而不是:
javascript
row.innerHTML
原因是用户输入内容不应该直接当 HTML 解析,这样更安全。
二十三、综合案例三:待办事项 TodoList
需求:
- 输入任务内容
- 点击新建任务,把任务添加到未完成列表
- 勾选任务后,移动到已完成列表
- 取消勾选后,移动回未完成列表
- 点击删除按钮,删除该任务
完整代码
html
<div class="nav">
<input type="text" placeholder="请输入任务">
<button>新建任务</button>
</div>
<div class="container">
<div class="todo">
<h3>未完成</h3>
</div>
<div class="done">
<h3>已完成</h3>
</div>
</div>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.nav {
width: 800px;
height: 100px;
margin: 0 auto;
display: flex;
align-items: center;
}
.nav input {
width: 600px;
height: 50px;
padding-left: 10px;
}
.nav button {
width: 200px;
height: 50px;
border: none;
background-color: orange;
color: #fff;
}
.container {
width: 800px;
margin: 0 auto;
display: flex;
}
.todo,
.done {
width: 50%;
min-height: 300px;
}
.container h3 {
height: 50px;
text-align: center;
line-height: 50px;
background-color: #333;
color: #fff;
}
.row {
height: 50px;
display: flex;
align-items: center;
border-bottom: 1px solid #eee;
}
.row input {
margin: 0 10px;
}
.row span {
width: 300px;
}
.row button {
width: 50px;
height: 32px;
}
</style>
<script>
let addTaskButton = document.querySelector('.nav button');
addTaskButton.addEventListener('click', function () {
let input = document.querySelector('.nav input');
let taskContent = input.value.trim();
if (taskContent === '') {
return;
}
let row = document.createElement('div');
row.className = 'row';
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
let span = document.createElement('span');
span.textContent = taskContent;
let button = document.createElement('button');
button.textContent = '删除';
row.appendChild(checkbox);
row.appendChild(span);
row.appendChild(button);
let todo = document.querySelector('.todo');
todo.appendChild(row);
input.value = '';
checkbox.addEventListener('click', function () {
let row = this.parentNode;
if (this.checked) {
let done = document.querySelector('.done');
done.appendChild(row);
} else {
let todo = document.querySelector('.todo');
todo.appendChild(row);
}
});
button.addEventListener('click', function () {
let row = this.parentNode;
let parent = row.parentNode;
parent.removeChild(row);
});
});
</script>
这个案例把很多 WebAPI 知识串起来了:
1.querySelector 获取元素
2.value 获取输入框内容
3.createElement 创建节点
4.appendChild 插入节点
5.checked 判断复选框状态
6.parentNode 获取父节点
7.removeChild 删除节点
8.addEventListener 绑定事件
二十四、WebAPI 常见坑点总结
1. JS 执行太早,元素还没加载出来
错误:
html
<script>
let btn = document.querySelector('button');
btn.onclick = function () {};
</script>
<button>按钮</button>
解决:
text
1. 把 script 放在 body 最后
2. 使用 DOMContentLoaded
2. querySelector 忘记选择器符号
错误:
javascript
document.querySelector('box');
如果你要找类名,应该写:
javascript
document.querySelector('.box');
如果你要找 id,应该写:
javascript
document.querySelector('#box');
3. querySelectorAll 得到的是一组元素
错误:
javascript
let items = document.querySelectorAll('.item');
items.onclick = function () {};
正确:
javascript
for (let i = 0; i < items.length; i++) {
items[i].onclick = function () {};
}
或者使用事件委托。
4. input 的 value 是字符串
javascript
let num = input.value;
console.log(num + 1);
可能得到字符串拼接。
更稳妥:
javascript
let num = Number(input.value);
console.log(num + 1);
5. innerHTML 不要乱用
如果内容来自用户输入,尽量使用:
javascript
textContent
innerText
不要随便使用:
javascript
innerHTML
否则可能带来安全问题。
6. style 属性名要用驼峰
CSS:
css
background-color
font-size
JS:
javascript
element.style.backgroundColor = 'red';
element.style.fontSize = '20px';
7. className 会整体覆盖类名
javascript
element.className = 'dark';
这会把原来的类名全部替换掉。
更推荐:
javascript
element.classList.add('dark');
element.classList.remove('light');
element.classList.toggle('active');
8. removeChild 必须通过父节点删除子节点
错误:
javascript
child.removeChild(child);
正确:
javascript
parent.removeChild(child);
现代浏览器也可以使用:
javascript
child.remove();
全文总结
WebAPI 是 JavaScript 从"会算"走向"会动"的关键。
基础语法让我们能写逻辑:
而Web API能让我们操作网页
换句话说:
ECMAScript 负责基础语法,DOM 负责操作页面,BOM 负责操作浏览器,而 WebAPI 就是让网页真正产生交互的工具箱。
大多数网页交互,本质上都可以拆成这三步。
比如:
text
点击按钮切换文字
点击图片切换图片
输入内容后提交留言
勾选复选框移动任务
点击删除按钮移除节点
背后的套路都是:
text
获取元素 -> 注册事件 -> 操作 DOM