1.事件
事件是编程语言中的术语,它是用来描述程序的行为或状态的,一旦行为或状态发生改变,便立即调用一个函数。
例如:用户使用【鼠标点击】网页中的一个按钮、用户使用【鼠标拖拽】网页中的一张图片
1.1 事件监听
什么是事件监听?
就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为绑定事件或者注册事件
比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等
结合 DOM 使用事件时,需要为 DOM 对象添加事件监听,等待事件发生(触发)时,便立即调用一个函数。
addEventListener` 是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和【事件回调】。
语法:
element.addEventListener(event, function, useCapture);参数说明:
event- 事件名称(如 'click', 'mouseover' 等,不带 on 前缀)function- 事件触发时执行的函数(事件处理函数)useCapture- 可选,布尔值,指定事件是在捕获阶段还是冒泡阶段触发(默认 false)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件监听</title>
</head>
<body>
<h3>事件监听</h3>
<p id="text">为 DOM 元素添加事件监听,等待事件发生,便立即执行一个函数。</p>
<button id="btn">点击改变文字颜色</button>
<script>
// 1. 获取 button 对应的 DOM 对象
const btn = document.querySelector('#btn')
// 2. 添加事件监听
btn.addEventListener('click', function () {
console.log('等待事件被触发...')
// 改变 p 标签的文字颜色
let text = document.getElementById('text')
text.style.color = 'red'
})
// 3. 只要用户点击了按钮,事件便触发了!!!
</script>
</body>
</html>完成事件监听分成3个步骤:
- 获取 DOM 元素
- 通过
addEventListener方法为 DOM 节点添加事件监听 - 等待事件触发,如用户点击了某个按钮时便会触发
click事件类型 - 事件触发后,相对应的回调函数会被执行
1.2 事件类型
click 译成中文是【点击】的意思,它的含义是监听(等着)用户鼠标的单击操作,除了【单击】还有【双击】dblclick
<script>
// 双击事件类型
btn.addEventListener('dblclick', function () {
console.log('等待事件被触发...');
// 改变 p 标签的文字颜色
const text = document.querySelector('.text')
text.style.color = 'red'
})
// 只要用户双击击了按钮,事件便触发了!!!
</script>结论:【事件类型】决定了事件被触发的方式,如 click 代表鼠标单击,dblclick 代表鼠标双击。
1.3 事件处理程序
addEventListener 的第2个参数是函数,这个函数会在事件被触发时立即被调用,在这个函数中可以编写任意逻辑的代码,如改变 DOM 文本颜色、文本内容等。
<script>
// 双击事件类型
btn.addEventListener('dblclick', function () {
console.log('等待事件被触发...')
const text = document.querySelector('.text')
// 改变 p 标签的文字颜色
text.style.color = 'red'
// 改变 p 标签的文本内容
text.style.fontSize = '20px'
})
</script>2.事件类型
2.1 鼠标事件
鼠标事件是用户使用鼠标操作时触发的事件:
click // 鼠标单击(最常用)
dblclick // 鼠标双击
mousedown // 鼠标按下(左键/中键/右键按下时触发)
mouseup // 鼠标释放(左键/中键/右键释放时触发)
mouseover // 鼠标移入(移入元素或其子元素时触发,会冒泡)
mouseout // 鼠标移出(移出元素或其子元素时触发,会冒泡)
mousemove // 鼠标移动(在元素上移动时持续触发)
mouseenter // 鼠标进入(移入元素时触发,不会冒泡)
mouseleave // 鼠标离开(移出元素时触发,不会冒泡)2.2 键盘事件
键盘事件是用户操作键盘时触发的事件:
keydown // 键盘按下(任意键按下时触发,包括功能键)
keyup // 键盘释放(任意键释放时触发)
keypress // 键盘按键(仅对产生字符的键触发,已废弃,推荐使用 keydown/keyup)2.3 表单事件
表单事件是与表单元素相关的事件:
submit // 表单提交(form 元素提交时触发)
change // 值改变(input、select、textarea 的值改变并失去焦点时触发)
input // 输入事件(input、textarea 的值实时改变时触发)
focus // 获取焦点(元素获得焦点时触发)
blur // 失去焦点(元素失去焦点时触发)2.4 文档/窗口事件
文档和窗口相关的事件:
load // 页面加载完成(所有资源加载完成时触发)
DOMContentLoaded // DOM结构加载完成(DOM 树构建完成时触发)
resize // 窗口大小改变(窗口尺寸变化时触发)
scroll // 滚动(元素或窗口滚动时触发)2.5 事件类型使用示例
鼠标事件示例
<button id="btn">点击我</button>
<div id="box" style="width: 200px; height: 200px; background: lightblue;"></div>
<script>
const btn = document.getElementById('btn');
const box = document.getElementById('box');
// 单击事件
btn.addEventListener('click', function() {
console.log('按钮被单击');
});
// 双击事件
btn.addEventListener('dblclick', function() {
console.log('按钮被双击');
});
// 鼠标移入和移出
box.addEventListener('mouseenter', function() {
console.log('鼠标进入盒子');
this.style.background = 'lightgreen';
});
box.addEventListener('mouseleave', function() {
console.log('鼠标离开盒子');
this.style.background = 'lightblue';
});
</script><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轮播图点击切换</title>
<style>
* {
box-sizing: border-box;
}
.slider {
width: 560px;
height: 400px;
overflow: hidden;
}
.slider-wrapper {
width: 100%;
height: 320px;
}
.slider-wrapper img {
width: 100%;
height: 100%;
display: block;
}
.slider-footer {
height: 80px;
background-color: rgb(100, 67, 68);
padding: 12px 12px 0 12px;
position: relative;
}
.slider-footer .toggle {
position: absolute;
right: 0;
top: 12px;
display: flex;
}
.slider-footer .toggle button {
margin-right: 12px;
width: 28px;
height: 28px;
appearance: none;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-radius: 4px;
cursor: pointer;
}
.slider-footer .toggle button:hover {
background: rgba(255, 255, 255, 0.2);
}
.slider-footer p {
margin: 0;
color: #fff;
font-size: 18px;
margin-bottom: 10px;
}
.slider-indicator {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
}
.slider-indicator li {
width: 8px;
height: 8px;
margin: 4px;
border-radius: 50%;
background: #fff;
opacity: 0.4;
cursor: pointer;
}
.slider-indicator li.active {
width: 12px;
height: 12px;
opacity: 1;
}
</style>
</head>
<body>
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="toggle">
<button class="prev"><</button>
<button class="next">></button>
</div>
</div>
</div>
<script>
// 1. 初始数据
const data = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
{ url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
{ url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
]
// 获取元素
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const footer = document.querySelector('.slider-footer')
// 1. 右按钮业务
// 1.1 获取右侧按钮
const next = document.querySelector('.next')
let i = 0 // 信号量 控制播放图片张数
// 1.2 注册点击事件
next.addEventListener('click', function () {
// console.log(11)
i++
// 1.6判断条件 如果大于8 就复原为 0
// if (i >= 8) {
// i = 0
// }
i = i >= data.length ? 0 : i
// 1.3 得到对应的对象
// console.log(data[i])
// 调用函数
toggle()
})
// 2. 左侧按钮业务
// 2.1 获取左侧按钮
const prev = document.querySelector('.prev')
// 1.2 注册点击事件
prev.addEventListener('click', function () {
i--
// 判断条件 如果小于0 则爬到最后一张图片索引号是 7
// if (i < 0) {
// i = 7
// }
i = i < 0 ? data.length - 1 : i
// 1.3 得到对应的对象
// console.log(data[i])
// 调用函数
toggle()
})
// 声明一个渲染的函数作为复用
function toggle() {
// 1.4 渲染对应的数据
img.src = data[i].url
p.innerHTML = data[i].title
footer.style.backgroundColor = data[i].color
// 1.5 更换小圆点 先移除原来的类名, 当前li再添加 这个 类名
document.querySelector('.slider-indicator .active').classList.remove('active')
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
}
// 3. 自动播放模块
let timerId = setInterval(function () {
// 利用js自动调用点击事件 click() 一定加小括号调用函数
next.click()
}, 1000)
// 4. 鼠标经过大盒子,停止定时器
const slider = document.querySelector('.slider')
// 注册事件
slider.addEventListener('mouseenter', function () {
// 停止定时器
clearInterval(timerId)
})
// 5. 鼠标离开大盒子,开启定时器
// 注册事件
slider.addEventListener('mouseleave', function () {
// 停止定时器
if (timerId) clearInterval(timerId)
// 开启定时器
timerId = setInterval(function () {
// 利用js自动调用点击事件 click() 一定加小括号调用函数
next.click()
}, 1000)
})
</script>
</body>
</html>键盘事件示例
<input type="text" id="input" placeholder="输入内容">
<script>
const input = document.getElementById('input');
input.addEventListener('keydown', function(event) {
console.log('键盘按下:', event.key);
});
input.addEventListener('keyup', function(event) {
console.log('键盘释放:', event.key);
});
</script><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul {
list-style: none;
}
.mi {
position: relative;
width: 223px;
margin: 100px auto;
}
.mi input {
width: 223px;
height: 48px;
padding: 0 10px;
font-size: 14px;
line-height: 48px;
border: 1px solid #e0e0e0;
outline: none;
}
.mi .search {
border: 1px solid #ff6700;
}
.result-list {
display: none;
position: absolute;
left: 0;
top: 48px;
width: 223px;
border: 1px solid #ff6700;
border-top: 0;
background: #fff;
}
.result-list a {
display: block;
padding: 6px 15px;
font-size: 12px;
color: #424242;
text-decoration: none;
}
.result-list a:hover {
background-color: #eee;
}
</style>
</head>
<body>
<div class="mi">
<input type="search" placeholder="小米笔记本">
<ul class="result-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">小米11</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">黑鲨4</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
// 1. 获取元素
const input = document.querySelector('[type=search]')
const ul = document.querySelector('.result-list')
// console.log(input)
// 2. 监听事件 获得焦点
input.addEventListener('focus', function () {
// ul显示
ul.style.display = 'block'
// 添加一个带有颜色边框的类名
input.classList.add('search')
})
// 3. 监听事件 失去焦点
input.addEventListener('blur', function () {
ul.style.display = 'none'
input.classList.remove('search')
})
</script>
</body>
</html>表单事件示例
<form id="form">
<input type="text" id="username" placeholder="用户名">
<input type="password" id="password" placeholder="密码">
<button type="submit">提交</button>
</form>
<script>
const form = document.getElementById('form');
const username = document.getElementById('username');
// 表单提交
form.addEventListener('submit', function(event) {
event.preventDefault(); // 阻止表单默认提交行为
console.log('表单提交');
});
// 输入框实时输入
username.addEventListener('input', function(event) {
console.log('输入内容:', event.target.value);
});
// 输入框获取焦点
username.addEventListener('focus', function() {
console.log('获取焦点');
this.style.borderColor = 'blue';
});
// 输入框失去焦点
username.addEventListener('blur', function() {
console.log('失去焦点');
this.style.borderColor = '#ccc';
});
</script><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>评论回车发布</title>
<style>
.wrapper {
min-width: 400px;
max-width: 800px;
display: flex;
justify-content: flex-end;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
overflow: hidden;
background: url(./images/avatar.jpg) no-repeat center / cover;
margin-right: 20px;
}
.wrapper textarea {
outline: none;
border-color: transparent;
resize: none;
background: #f5f5f5;
border-radius: 4px;
flex: 1;
padding: 10px;
transition: all 0.5s;
height: 30px;
}
.wrapper textarea:focus {
border-color: #e4e4e4;
background: #fff;
height: 50px;
}
.wrapper button {
background: #00aeec;
color: #fff;
border: none;
border-radius: 4px;
margin-left: 10px;
width: 70px;
cursor: pointer;
}
.wrapper .total {
margin-right: 80px;
color: #999;
margin-top: 5px;
opacity: 0;
transition: all 0.5s;
}
.list {
min-width: 400px;
max-width: 800px;
display: flex;
}
.list .item {
width: 100%;
display: flex;
}
.list .item .info {
flex: 1;
border-bottom: 1px dashed #e4e4e4;
padding-bottom: 10px;
}
.list .item p {
margin: 0;
}
.list .item .name {
color: #FB7299;
font-size: 14px;
font-weight: bold;
}
.list .item .text {
color: #333;
padding: 10px 0;
}
.list .item .time {
color: #999;
font-size: 12px;
}
</style>
</head>
<body>
<div class="wrapper">
<i class="avatar"></i>
<textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea>
<button>发布</button>
</div>
<div class="wrapper">
<span class="total">0/200字</span>
</div>
<div class="list">
<div class="item" style="display: none;">
<i class="avatar"></i>
<div class="info">
<p class="name">清风徐来</p>
<p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p>
<p class="time">2022-10-10 20:29:21</p>
</div>
</div>
</div>
<script>
const tx = document.querySelector('#tx')
const total = document.querySelector('.total')
const item = document.querySelector('.item')
const text = document.querySelector('.text')
// 1. 当我们文本域获得了焦点,就让 total 显示出来
tx.addEventListener('focus', function () {
total.style.opacity = 1
})
// 2. 当我们文本域失去了焦点,就让 total 隐藏出来
tx.addEventListener('blur', function () {
total.style.opacity = 0
})
// 3. 检测用户输入
tx.addEventListener('input', function () {
// console.log(tx.value.length) 得到输入的长度
total.innerHTML = `${tx.value.length}/200字`
})
// 4. 按下回车发布评论
tx.addEventListener('keyup', function (e) {
// 只有按下的是回车键,才会触发
// console.log(e.key)
if (e.key === 'Enter') {
// 如果用户输入的不为空就显示和打印
if (tx.value.trim()) {
// console.log(11)
item.style.display = 'block'
// console.log(tx.value) // 用户输入的内容
text.innerHTML = tx.value
}
// 等我们按下回车,结束,清空文本域
tx.value = ''
// 按下回车之后,就要把 字符统计 复原
total.innerHTML = '0/200字'
}
})
</script>
</body>
</html>窗口事件示例
// 页面加载完成
window.addEventListener('load', function() {
console.log('页面加载完成');
});
// DOM 结构加载完成
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 结构加载完成');
});
// 窗口大小改变
window.addEventListener('resize', function() {
console.log('窗口宽度:', window.innerWidth);
console.log('窗口高度:', window.innerHeight);
});
// 滚动事件
window.addEventListener('scroll', function() {
console.log('滚动位置:', window.scrollY);
});3. 事件对象
3.1 什么是事件对象
事件对象(Event Object)是事件触发时自动传递给事件处理函数的对象,它包含了与该事件相关的所有信息。
3.2 获取事件对象
事件对象作为事件处理函数的参数自动传入:
element.addEventListener('click', function(event) {
// event 就是事件对象
console.log(event);
});3.3 常用事件对象属性
鼠标事件属性
event.type // 事件类型(如 'click')
event.target // 触发事件的元素(实际被点击的元素)
event.currentTarget // 绑定事件的元素
event.clientX // 鼠标相对于视口的 X 坐标
event.clientY // 鼠标相对于视口的 Y 坐标
event.pageX // 鼠标相对于文档的 X 坐标
event.pageY // 鼠标相对于文档的 Y 坐标
event.offsetX // 鼠标相对于目标元素的 X 坐标
event.offsetY // 鼠标相对于目标元素的 Y 坐标
event.button // 鼠标按键(0=左键, 1=中键, 2=右键)键盘事件属性
event.key // 按键名称(如 'Enter', 'a', 'ArrowUp')
event.code // 按键代码(如 'Enter', 'KeyA', 'ArrowUp')
event.keyCode // 按键码(已废弃,不推荐使用)
event.ctrlKey // 是否按下了 Ctrl 键
event.shiftKey // 是否按下了 Shift 键
event.altKey // 是否按下了 Alt 键
event.metaKey // 是否按下了 Meta 键(Windows 下是 Win 键)通用属性
event.timeStamp // 事件发生的时间戳
event.bubbles // 事件是否冒泡
event.cancelable // 事件是否可以取消默认行为3.4 常用事件对象方法
event.preventDefault() // 阻止事件的默认行为
event.stopPropagation() // 阻止事件冒泡
event.stopImmediatePropagation() // 阻止事件冒泡并阻止同一元素的其他监听器3.5 事件对象使用示例
鼠标事件对象
<div id="box" style="width: 300px; height: 200px; background: lightblue; padding: 20px;">
<button id="btn">点击我</button>
</div>
<script>
const box = document.getElementById('box');
const btn = document.getElementById('btn');
btn.addEventListener('click', function(event) {
console.log('事件类型:', event.type);
console.log('目标元素:', event.target);
console.log('当前元素:', event.currentTarget);
console.log('视口坐标:', event.clientX, event.clientY);
console.log('文档坐标:', event.pageX, event.pageY);
console.log('相对坐标:', event.offsetX, event.offsetY);
});
box.addEventListener('click', function(event) {
console.log('box 被点击,事件源:', event.target.tagName);
});
</script>键盘事件对象
<input type="text" id="input" placeholder="按任意键">
<script>
const input = document.getElementById('input');
input.addEventListener('keydown', function(event) {
console.log('按键名称:', event.key);
console.log('按键代码:', event.code);
console.log('Ctrl键:', event.ctrlKey);
console.log('Shift键:', event.shiftKey);
console.log('Alt键:', event.altKey);
// 检测快捷键 Ctrl + Enter
if (event.ctrlKey && event.key === 'Enter') {
console.log('按下了 Ctrl + Enter');
}
});
</script>阻止默认行为
<a href="https://www.example.com" id="link">点击跳转</a>
<form id="form">
<input type="text" placeholder="输入内容">
<button type="submit">提交</button>
</form>
<script>
const link = document.getElementById('link');
const form = document.getElementById('form');
// 阻止链接跳转
link.addEventListener('click', function(event) {
event.preventDefault();
console.log('链接跳转被阻止');
});
// 阻止表单提交
form.addEventListener('submit', function(event) {
event.preventDefault();
console.log('表单提交被阻止');
});
</script>阻止事件冒泡
<div id="outer" style="width: 200px; height: 200px; background: lightblue; padding: 20px;">
<div id="inner" style="width: 100px; height: 100px; background: lightgreen;"></div>
</div>
<script>
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');
outer.addEventListener('click', function() {
console.log('外层元素被点击');
});
inner.addEventListener('click', function(event) {
console.log('内层元素被点击');
event.stopPropagation(); // 阻止事件冒泡到外层元素
});
</script>4. 环境对象
4.1 什么是环境对象
环境对象指的是函数内部特殊的变量 this,它代表当前函数运行时所处的环境对象。
4.2 this 的指向规则
在普通函数中,this 的指向取决于函数的调用方式:
4.2.1 直接调用函数
function fn() {
console.log(this); // window(严格模式下为 undefined)
}
fn();4.2.2 对象方法调用
const obj = {
name: '张三',
sayHello: function() {
console.log(this); // obj 对象
console.log('你好,我是', this.name);
}
};
obj.sayHello(); // 调用对象方法4.2.3 DOM 事件监听中的 this
重点: 在事件监听函数中,this 指向绑定事件的 DOM 元素!
<button id="btn">点击我</button>
<script>
const btn = document.getElementById('btn');
btn.addEventListener('click', function() {
console.log(this); // <button> 元素
console.log(this.id); // btn
this.style.background = 'red'; // 修改按钮背景色
});
</script>4.3 环境对象使用示例
示例1:使用 this 获取绑定事件的元素
<button class="btn">按钮1</button>
<button class="btn">按钮2</button>
<button class="btn">按钮3</button>
<script>
const buttons = document.querySelectorAll('.btn');
buttons.forEach(function(btn) {
btn.addEventListener('click', function() {
console.log('点击了按钮:', this.textContent);
this.style.background = 'lightblue';
});
});
</script>示例2:使用 this 操作元素
<div class="box" style="width: 100px; height: 100px; background: lightblue;">盒子1</div>
<div class="box" style="width: 100px; height: 100px; background: lightblue;">盒子2</div>
<div class="box" style="width: 100px; height: 100px; background: lightblue;">盒子3</div>
<script>
const boxes = document.querySelectorAll('.box');
boxes.forEach(function(box) {
box.addEventListener('click', function() {
// 使用 this 操作当前被点击的元素
this.style.background = 'red';
this.style.color = 'white';
this.textContent = '被点击了';
});
});
</script>示例3:使用 this 实现选项卡切换
<div id="tab">
<button class="active">选项1</button>
<button>选项2</button>
<button>选项3</button>
<div class="content" style="display: block;">内容1</div>
<div class="content">内容2</div>
<div class="content">内容3</div>
</div>
<script>
const buttons = document.querySelectorAll('#tab button');
const contents = document.querySelectorAll('#tab .content');
buttons.forEach(function(btn, index) {
btn.addEventListener('click', function() {
// 移除所有按钮的 active 类
buttons.forEach(function(b) {
b.classList.remove('active');
});
// 为当前点击的按钮添加 active 类
this.classList.add('active');
// 隐藏所有内容
contents.forEach(function(content) {
content.style.display = 'none';
});
// 显示对应索引的内容
contents[index].style.display = 'block';
});
});
</script>4.4 箭头函数中的 this
注意: 箭头函数没有自己的 this,它会继承外层作用域的 this。
<button id="btn">点击我</button>
<script>
const btn = document.getElementById('btn');
// 箭头函数中的 this 不指向按钮元素
btn.addEventListener('click', () => {
console.log(this); // window
// this.style.background = 'red'; // 报错!
});
// 如果需要使用 this,应该使用普通函数
btn.addEventListener('click', function() {
console.log(this); // <button> 元素
this.style.background = 'red';
});
</script>5. 回调函数
5.1 什么是回调函数
回调函数(Callback Function)是作为参数传递给另一个函数,并在该函数内部被调用的函数。
简单理解: 如果一个函数 A 被作为参数传递给函数 B,并且在函数 B 内部被调用,那么函数 A 就是回调函数。
5.2 回调函数的基本使用
示例1:最简单的回调函数
// 定义一个接收回调函数的函数
function greet(callback) {
console.log('开始打招呼');
callback(); // 调用回调函数
console.log('结束打招呼');
}
// 定义回调函数
function sayHello() {
console.log('你好!');
}
// 调用函数,传入回调函数
greet(sayHello);
// 输出:
// 开始打招呼
// 你好!
// 结束打招呼示例2:使用匿名函数作为回调
greet(function() {
console.log('大家好!');
});示例3:使用箭头函数作为回调
greet(() => {
console.log('箭头函数回调');
});5.3 回调函数传参
回调函数可以接收参数:
// 定义带参数的回调函数
function calculate(a, b, callback) {
console.log('计算中...');
const result = a + b;
callback(result); // 将计算结果传递给回调函数
}
// 定义接收参数的回调函数
function showResult(result) {
console.log('计算结果:', result);
}
calculate(10, 20, showResult);
// 输出:
// 计算中...
// 计算结果: 305.4 事件监听中的回调函数
在事件监听中,事件处理函数就是回调函数:
const btn = document.getElementById('btn');
// 这个匿名函数就是回调函数
btn.addEventListener('click', function() {
console.log('按钮被点击了!');
});
// 命名函数作为回调函数
function handleClick() {
console.log('按钮被点击了!');
}
btn.addEventListener('click', handleClick);5.5 回调函数的应用场景
场景1:数组方法中的回调函数
const numbers = [1, 2, 3, 4, 5];
// forEach 中的回调函数
numbers.forEach(function(num) {
console.log(num);
});
// map 中的回调函数
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled);
// filter 中的回调函数
const evenNumbers = numbers.filter(function(num) {
return num % 2 === 0;
});
console.log(evenNumbers);场景2:定时器中的回调函数
// setTimeout 中的回调函数
setTimeout(function() {
console.log('2秒后执行');
}, 2000);
// setInterval 中的回调函数
let count = 0;
const timer = setInterval(function() {
count++;
console.log('每秒执行一次', count);
if (count >= 5) {
clearInterval(timer); // 清除定时器
}
}, 1000);场景3:使用回调函数处理点击事件
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button id="btn3">按钮3</button>
<script>
const btn1 = document.getElementById('btn1');
const btn2 = document.getElementById('btn2');
const btn3 = document.getElementById('btn3');
// 为每个按钮添加点击事件
btn1.addEventListener('click', function() {
console.log('按钮1被点击');
});
btn2.addEventListener('click, function() {
console.log('按钮2被点击');
});
btn3.addEventListener('click', function() {
console.log('按钮3被点击');
});
</script>场景4:封装一个通用的事件处理函数
<button class="btn" data-msg="消息1">按钮1</button>
<button class="btn" data-msg="消息2">按钮2</button>
<button class="btn" data-msg="消息3">按钮3</button>
<script>
const buttons = document.querySelectorAll('.btn');
// 定义一个通用的点击处理函数
function handleClick(event) {
const msg = this.getAttribute('data-msg');
console.log(msg);
this.style.background = 'lightblue';
}
// 为所有按钮绑定同一个回调函数
buttons.forEach(function(btn) {
btn.addEventListener('click', handleClick);
});
</script>5.6 回调函数的优点
- 代码复用: 同一个回调函数可以被多次调用
- 代码分离: 将函数的定义和调用分离
- 异步处理: 适合处理异步操作(如定时器、网络请求)
- 灵活性: 可以根据需要传入不同的回调函数
5.7 回调地狱
当多个异步操作需要按顺序执行时,会产生回调嵌套,形成回调地狱:
// 回调地狱示例(不推荐)
setTimeout(function() {
console.log('第一步');
setTimeout(function() {
console.log('第二步');
setTimeout(function() {
console.log('第三步');
// 继续嵌套...
}, 1000);
}, 1000);
}, 1000);解决回调地狱的方法:
- 使用 Promise
- 使用 async/await
// 使用 Promise 和 async/await 解决
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function run() {
await delay(1000);
console.log('第一步');
await delay(1000);
console.log('第二步');
await delay(1000);
console.log('第三步');
}
run();5.8 总结
- 回调函数是作为参数传递给另一个函数的函数
- 在事件监听中,事件处理函数就是回调函数
- 回调函数可以接收参数,提高灵活性
- 合理使用回调函数可以简化代码,提高复用性
- 避免过度嵌套产生回调地狱,可以使用 Promise 或 async/await 解决
