JavaScript事件监听
目录
一、事件监听
1. 事件监听的基本概念
2. 使用 addEventListener
二、事件对象
1. 常见事件对象属性与方法
2. 示例代码
三、环境对象(this)的指向
1. 传统函数中的 this
2. 箭头函数中的 this
3. 结论
4. 注意事项
三、回调函数(Callback Function)
1. 回调函数的特点
2. 综合应用:事件对象、this 与回调
3. 常见问题与解决方案
示例:随机轮播图
一、事件监听
1. 事件监听的基本概念
-
事件:用户或浏览器触发的动作,如点击、滚动、键盘输入等。
-
监听器(Listener):事件发生时执行的函数。
-
两种绑定方式:
-
HTML 属性:
<button onclick="handleClick()">
(不推荐,混合逻辑与结构)。 -
DOM API:
addEventListener
(推荐,灵活且可维护)。
-
2. 使用 addEventListener
-
语法:
element.addEventListener(eventType, handler [, options]);
-
参数:
-
eventType:事件类型字符串(如
"click"
、"keydown"
)。 -
handler:事件触发时调用的函数。
-
options(可选):配置对象或布尔值,控制捕获/冒泡阶段、一次性执行等。
-
-
步骤:
-
获取 DOM 元素
-
通过
addEventListener
方法为 DOM 节点添加事件监听 -
等待事件触发,如用户点击了某个按钮时便会触发
click
事件类型 -
事件触发后,相对应的回调函数会被执行
-
-
示例:
const button = document.querySelector('button'); button.addEventListener('click', function(event) { console.log('按钮被点击!'); });
二、事件对象
当事件触发时,浏览器会自动创建一个 事件对象(event
),并将其作为参数传递给事件处理函数。该对象包含与事件相关的所有信息,例如触发事件的元素、事件类型、坐标位置、按键信息等。
1. 常见事件对象属性与方法
-
常用属性/方法:
-
event.target
:触发事件的元素(如点击的具体子元素)。 -
event.currentTarget
:绑定事件的元素(等同于this
)。 -
event.stopPropagation()
:阻止事件继续传播(冒泡或捕获)。 -
event.preventDefault()
:阻止默认行为(如表单提交、链接跳转)。 -
event.type
:事件类型(如"click"
)。
-
属性/方法 | 说明 | 示例场景 |
---|---|---|
event.target | 触发事件的实际元素(可能是事件冒泡中的子元素) | 点击子元素时,target 指向子元素 |
event.currentTarget | 绑定事件的元素(等同于 this ,在事件处理函数中指向绑定事件的元素) | 事件委托中,currentTarget 指向父元素 |
event.type | 事件类型(如 "click" 、"keydown" ) | 判断事件类型以执行不同逻辑 |
event.preventDefault() | 阻止事件的默认行为(如阻止链接跳转、表单提交) | 表单验证失败时阻止提交 |
event.stopPropagation() | 阻止事件继续传播(停止捕获或冒泡) | 避免父元素的事件监听器触发 |
event.stopImmediatePropagation() | 阻止同一元素上的其他监听器执行 | 优先级高于 stopPropagation ,阻止后续监听器 |
event.clientX / clientY | 鼠标相对于浏览器窗口的坐标(不包含滚动偏移) | 实现拖拽效果时获取鼠标位置 |
event.pageX / pageY | 鼠标相对于文档的坐标(包含滚动偏移) | 绘制画布时准确定位 |
event.key | 键盘事件的按键值(如 "Enter" 、"a" ) | 监听回车键提交表单 |
event.relatedTarget | 关联元素(如 mouseover 时表示鼠标来自哪个元素,focusout 时表示即将获得焦点的元素) | 实现下拉菜单的悬停交互 |
2. 示例代码
document.querySelector('button').addEventListener('click', function(event) {
console.log('事件类型:', event.type); // "click"
console.log('触发元素:', event.target.tagName); // 可能是子元素
console.log('绑定元素:', event.currentTarget.tagName); // BUTTON
event.preventDefault(); // 阻止默认行为(如表单提交)
});
三、环境对象(this)的指向
在事件处理函数中,this
的指向取决于函数的定义方式:
1. 传统函数中的 this
-
指向绑定事件的元素(与
event.currentTarget
相同)。 -
示例
2. 箭头函数中的 this
-
继承外层作用域的
this
(通常是window
或外层函数上下文)。 -
示例:
button.addEventListener('click', () => { console.log(this === window); // true(假设外层是全局作用域) });
3. 结论
-
this
本质上是一个变量,数据类型为对象 -
函数的调用方式不同
this
变量的值也不同 -
【谁调用
this
就是谁】是判断this
值的粗略规则
4. 注意事项
-
避免在需要
this
指向元素时使用箭头函数。 -
需要访问元素时,优先使用
event.currentTarget
(兼容性更好)。
三、回调函数(Callback Function)
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数。
1. 回调函数的特点
-
参数传递:
-
默认接收
event
对象作为参数。 -
若需传递自定义参数,需封装在匿名函数中:
const handler = (message) => { console.log(message); }; button.addEventListener('click', () => handler('按钮被点击!'));
-
-
匿名函数与命名函数:
-
匿名函数:直接定义在
addEventListener
中,但无法直接移除。button.addEventListener('click', function() { /* ... */ });
<script> // 调用定时器,匿名函数做为参数 setInterval(function () { console.log('我是回调函数...'); }, 1000); </script>
-
命名函数:需预先定义,便于复用和移除。
function handleClick() { /* ... */ } button.addEventListener('click', handleClick); button.removeEventListener('click', handleClick); // 可成功移除
<script> function fn() { console.log('我是回调函数...'); } // 调用定时器 setInterval(fn, 1000); </script>
-
-
内存管理:
-
若回调函数引用外部变量或 DOM 元素,可能导致内存泄漏(需及时移除监听)。
-
2. 综合应用:事件对象、this 与回调
<div id="container">
<button class="btn">点击我</button>
</div>
const container = document.querySelector('#container');
const button = document.querySelector('.btn');
// 传统函数中 this 指向绑定元素
button.addEventListener('click', function(event) {
console.log('this:', this); // <button> 元素
console.log('currentTarget:', event.currentTarget); // <button> 元素
console.log('target:', event.target); // <button> 元素(无子元素时与 currentTarget 相同)
});
// 事件委托:父元素监听子元素事件
container.addEventListener('click', function(event) {
if (event.target.classList.contains('btn')) {
console.log('点击的按钮:', event.target);
event.stopPropagation(); // 阻止冒泡到更外层
}
});
// 箭头函数中的 this(假设外层为全局作用域)
button.addEventListener('click', () => {
console.log('箭头函数中的 this:', this); // window 或 undefined(严格模式)
});
3. 常见问题与解决方案
问题场景 | 原因分析 | 解决方案 |
---|---|---|
this 指向不符合预期 | 使用了箭头函数 | 改用传统函数,或通过 event.currentTarget 获取元素 |
无法移除事件监听 | 回调函数为匿名函数 | 使用命名函数,并确保 removeEventListener 参数一致 |
事件委托中误判 event.target | 子元素嵌套导致 target 不准确 | 使用 closest 方法向上查找匹配元素:event.target.closest('.btn') |
高频事件(如 scroll )卡顿 | 回调函数执行过于频繁 | 使用 节流(throttle) 或 防抖(debounce) 优化 |
示例:随机轮播图
<!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></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 sliderData = [
{ 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)' },
]
function slider(i) {
const img = document.querySelector('.slider-wrapper img')
img.src = sliderData[i].url
const title = document.querySelector('.slider-footer p')
title.textContent = sliderData[i].title
const bgc = document.querySelector('.slider-footer')
bgc.style.backgroundColor = sliderData[i].color
const li = document.querySelector(`.slider-indicator li:nth-child(${i + 1})`)
li.classList.add('active')
}
function clearSlider(i) {
const li = document.querySelector(`.slider-indicator li:nth-child(${i + 1})`)
li.classList.remove('active')
}
let i = 0
slider(i)
//1.手动轮播
const next = document.querySelector('.toggle .next')
next.addEventListener('click', function () {
clearSlider(i)
i++;
if (i === 8)
i = 0
slider(i)
})
const prev = document.querySelector('.toggle .prev')
prev.addEventListener('click', function () {
clearSlider(i)
i--;
if (i === -1)
i = 7
slider(i)
})
//2.自动轮播
// let timer = setInterval(function () {
// clearSlider(i)
// i++;
// if (i === 8)
// i = 0
// slider(i)
// }, 1000)
</script>
</body>
</html>