四天学完前端基础三件套(JavaScript webAPI篇)

JavaScript WebAPI 入门详解:从 DOM 操作到事件驱动,一篇文章搞懂网页交互

往期回顾

指针合集
c语言基础
数据结构与算法

目录


前言

前面学习 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';

这里的 documentwindowlocationquerySelectorinnerHTML 等,都不是我们自己定义的。

它们是浏览器环境提前提供好的能力。

这些能力就属于 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. 浏览器随机生成 1~100 的数字
  2. 用户输入猜测数字
  3. 点击按钮后提示猜大了、猜小了、猜对了
  4. 记录已经猜的次数
  5. 支持重新开始

完整代码

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>

二十二、综合案例二:表白墙

需求:

  1. 输入"谁"
  2. 输入"对谁"
  3. 输入"说什么"
  4. 点击提交后,把信息显示到页面下方
  5. 提交后清空输入框

完整代码

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

需求:

  1. 输入任务内容
  2. 点击新建任务,把任务添加到未完成列表
  3. 勾选任务后,移动到已完成列表
  4. 取消勾选后,移动回未完成列表
  5. 点击删除按钮,删除该任务

完整代码

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
相关推荐
存在morning1 小时前
【GO语言开发实践】一 GO 语法快速上手
开发语言·python·golang
晨曦中的暮雨1 小时前
Python 并发模型理解:GIL、线程、async 到底是什么关系
开发语言·python
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第59题】【JVM篇】第19题:并发标记过程中会出现什么问题?
java·开发语言·jvm
摇滚侠1 小时前
Mybatis 面试题 真正的 offer 偏方 Java 基础 Java 高级
java·开发语言·mybatis
林熙蕾LXL1 小时前
进程处理操作
开发语言·c++·算法
IT_陈寒1 小时前
Redis突然吃掉所有内存,我的服务差点挂了
前端·人工智能·后端
2601_958492551 小时前
Behavioral Analysis of HTML5 Trivia Integration
前端·html·html5
知彼解己1 小时前
从后端视角学习 Vue3:核心知识与数据流实践
javascript·vue.js·ecmascript
兩尛1 小时前
C++多线程编程
开发语言·jvm·c++