认识Dom事件
DOM中的事件->行为,行为背后对应着执行某个(某些)待执行函数。
事件的基本逻辑
- 给元素的某个行为添加上对应的函数 (称为事件处理函数)。
- 等到用户对该元素实施该行为。
- 浏览器会检测到,在元素上发生了该行为,就会马上触发该行为绑定的函数。
- 函数执行,那么会产生相应的效果。
常用的事件绑定方法
const h1 = document.querySelector(".en")
function abc(){
}
h1.addEventListener('click',abc)
事件处理函数中的this: 非箭头函数中是指向绑定元素本身的,可以通过this 来访问元素。如果你需要在事件处理函数中使用this,就用function关键字的函数。
重要的事件对象
什么是事件对象?
事件处理函数中系统自动传入的第一个参数。
事件(行为)发生后,所有相关的信息都会进入到事件对象中,从而让人们进行使用。我们可以通过事件对象来获取到这些信息,从而实现各种想要的效果。
要想获取事件对象,只需要在事件处理函数中定义一个形参,通过第一个形参就能获取这个事件对象,这个形参命名可以自定义 一般来说命名为e 或者event。
body.addEventListener('click', (e) => {
console.log("我是body")
})
编程说明:一般来说,事件对象中有非常多的属性和一些方法不同的属性,会在不同的类型上通过继承来实现,可以根据事件对象类型的继承关系,去相应的父类上找到属性和方法的说明。
事件对象的常用属性(重要)
事件流机制
1.当途中按钮被点击,click事件触发,新的事件对象就会被创建。但不会被创建在实际点击的地方,而是创建再document的根目录下——也就是树的最顶端。
2.该事件会在所谓的捕获阶段沿着整棵树传播,一路直达目标元素。目标元素就是实际触发事件的元素。(浏览器自然知道这个目标元素该是谁)。
3.我们就可以选择再该元素上放置事件处理函数来处理事件。
4.然后事件在到达目标元素后立即就会沿着树一路返回,也就是所谓的冒泡阶段。
5.如果说父元素也监听了目标元素的相同的事件类型,该父元素的该事件处理函数也会执行。
6.如果你想要阻止父元素的行为,就可以在目标元素上使用e.stopPropagation()来阻止冒泡。
注意:在CSS中,元素被覆盖(overlapped)指的是一个元素位于另一个元素的上方,覆盖了后者的部分或全部内容。在这种情况下,鼠标事件会被覆盖的元素捕获,而不会传递到被覆盖的元素上。具体来说,覆盖的元素会接收鼠标事件,例如
mouseover、mouseout、click等,而被覆盖的元素不会接收这些事件。这是因为浏览器的事件模型是基于捕获和冒泡的机制来处理事件的,当一个元素被覆盖时,覆盖的元素会捕获鼠标事件,而不会传递到被覆盖的元素上。
event中target与currentTarget的区别
target属性:指向事件最初发生的元素,即引发事件的目标元素。无论事件处理函数绑定在哪个元素上,target属性始终指向同一个元素,即最初触发事件的那个元素,也就是目标元素对象。currentTarget属性:指向当前正在处理事件的元素,即事件处理函数绑定的元素。随着事件在DOM树中传播(无论是捕获阶段还是冒泡阶段),每个处理函数中的currentTarget会变化,以反映当前正在执行处理函数的元素。
结合DOM树非常好理解。
实现交互的基本方法
实时交互性变化
- 直接修改
style属性:适用于需要根据用户实时输入(如鼠标位置)动态改变元素样式的情况。这种方法可以实现即时的视觉反馈。
过渡效果
- CSS
transition:适合简单的从一个状态到另一个状态的变化,例如颜色、尺寸、透明度等。transition需要事件触发(如点击或悬停),并且只能在两个状态之间进行平滑过渡。 - CSS
animation:适合更复杂的动画,可以定义多个中间状态,通过@keyframes实现。它不需要事件触发,可以自动运行,并支持循环和方向控制。
复杂动画
- JavaScript
Element.animate()方法:适用于需要更复杂控制和交互的动画,尤其是需要在动画过程中动态调整属性时。这种方法提供了更高的灵活性和控制力。 - 使用库(如Lottie):对于非常复杂的动画,通常使用动画软件(如After Effects)制作,然后通过工具导出为可嵌入网页的格式。这种方法适合于需要高质量和复杂效果的场景。
常见变化类型
选择具体方法时,需要考虑以下因素:
- 动画复杂度:简单过渡用
transition,复杂动画用animation或JavaScript。 - 性能影响:CSS过渡和动画通常比JavaScript更高效,因为它们可以由浏览器优化。
- 交互需求:实时交互建议直接修改样式,非实时交互可以使用过渡或动画。
动画交互常用的属性与方法
常见事件
事件补充
事件委托机制
为什么需要事件委托机制?
如上图DOM树所示,如果我们有1000个按钮的话,如果给每个按钮都加上事件处理函数,这会严重影响网站的性能。所以我们就可以使用事件委托机制。
实现机制

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM 树事件委托</title>
<style>
.btn { padding: 10px; margin: 5px; display: inline-block; background-color: #ccc; cursor: pointer; }
</style>
</head>
<body>
<div id="root">
<header class="topbar">
<div class="options">
<button class="btn">Button 1</button>
<button class="btn">Button 2</button>
<button class="btn">Button 3</button>
</div>
</header>
</div>
<script>
// 获取父元素
const root = document.getElementById('root');
// 为父元素添加点击事件监听器
root.addEventListener('click', function(event) {
// 检查点击的目标是否是按钮
if (event.target && event.target.matches('.btn')) {
alert('Clicked: ' + event.target.textContent);
}
});
</script>
</body>
</html>
编程提醒:如果你发现事件处理时出现了一些奇怪现象,那他有可能和事件冒泡有关。
节流与防抖
防抖
“你先一直执行,等你执行完,再过一段时间执行”,
例如,一个搜索输入框,等输入停止后,再触发搜索
function debounce(fn, delay = 200) {
let timer = 0;
return function(...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
timer = 0;
}, delay)
}
}
立即执行版本:
function debounce(fn, delay = 200, immediate = true) {
let timer = 0;
return function(...args) {
if (timer) clearTimeout(timer);
if (immediate && !timer) {
fn.apply(this, args);
}
timer = setTimeout(() => {
fn.apply(this, args);
timer = 0;
}, delay)
}
}
例子:
<input type="text" id="input1" placeholder="只能输入数字">
<div id="message"></div>
<script>
function debounce(fn, delay = 200) {
let timer = 0;
return function() {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
// 关键点:通过 apply 传递 this 和 arguments
fn.apply(this, arguments);
timer = 0;
}, delay);
};
}
const input1 = document.getElementById('input1');
const message = document.getElementById('message');
input1.addEventListener('keydown', debounce(function(event) {
// 1. 使用 this 获取输入框的值
const value = this.value;
// 2. 使用 event.key 获取按下的键
const key = event.key;
// 显示按键信息
message.textContent = `最后一次按下的键:${key},当前输入内容:${value}`;
// 3. 验证输入:如果不是数字,阻止默认行为
if (!/[0-9]/.test(key)) {
event.preventDefault(); // 阻止输入非数字字符
message.style.color = "red";
} else {
message.style.color = "green";
}
}));
</script>
为什么需要 this 和 event?
1. this 的作用
-
在事件监听器中,
this默认指向触发事件的元素(即输入框input1)。 -
我们需要通过
this.value获取输入框的当前值,而不是直接通过input1.value。这样做的好处是:-
代码更通用:如果同一个防抖函数应用到多个输入框,
this会自动指向各自的输入框。 -
避免闭包问题:如果直接在防抖回调中写
input1.value,当多个输入框共用同一个防抖函数时,可能会指向错误的元素。
-
2. event 的作用
-
.key(按下的按键)、event.preventDefault()(阻止默认行为)。 -
我们需要通过
event.key判断用户按下的键是否是数字,如果不是,则调用event.preventDefault()阻止输入。
如果不用 fn.apply(this, arguments) 会怎样?
假设防抖函数中直接调用 fn(),而不是 fn.apply(this, arguments):
问题 1:this 指向错误
-
fn中的this会变成全局对象(浏览器中是window),而不是输入框元素。 -
导致
this.value变成window.value(即undefined),无法获取输入框的值。
问题 2:event 参数丢失
-
fn无法接收到event对象,导致event.key和event.preventDefault()失效。 -
用户按下非数字键时,无法阻止输入,也无法显示按下的键信息。
图示:

节流
“别急,一个一个来,按时间节奏来,插队者无效”。
例如,drag或者scroll期间触发某个回调,要设置一个时间间隔。
function throttle(fn, delay = 200) {
let timer = 0;
return function(...args) {
if (timer) return;
timer = setTimeout(() => {
fn.apply(this, args);
timer = 0;
}, delay)
}
}
立即执行如下:
function throttle(fn, delay = 200, immediate = true) {
let timer = 0;
return function(...args) {
if (timer) return;
if (immediate && !timer) {
fn.apply(this, args);
}
timer = setTimeout(() => {
fn.apply(this, args);
timer = 0;
}, delay)
}
}


714

被折叠的 条评论
为什么被折叠?



