前端技术对JavaScript的事件学习
事件
EventTarge接口
基本概念
- 作用:EventTarget接口使得DOM元素和其他对象能够处理事件。通过该接口,可以绑定事件的监听函数,移除监听函数,以及触发事件。
- 应用范围:不仅限于DOM节点,还包括一些需要事件通信的浏览器内置对象,如XMLHttpRequest、AudioNode、AudioContext等。
主要方法
-
addEventListener(type, listener, [options])
-
作用:在当前节点或对象上定义一个特定事件的监听函数。
-
参数:
type
:事件名称,大小写敏感。listener
:监听函数,事件发生时调用此函数。也可以是具有handleEvent
方法的对象。options
(可选):一个属性配置对象,包括capture
(布尔值,表示是否在捕获阶段触发)、once
(布尔值,表示监听函数是否只触发一次)、passive
(布尔值,表示监听函数是否调用事件的preventDefault
方法)等属性。
-
示例:
var button = document.getElementById('btn'); button.addEventListener('click', function() { console.log('Button clicked!'); }, false);
-
-
removeEventListener(type, listener, [options])
- 作用:移除之前通过
addEventListener
方法添加的事件监听函数。 - 参数:与
addEventListener
方法相同。 - 注意:移除的监听函数必须是之前添加的相同函数(包括匿名函数),且
options
(如果有的话)也必须完全匹配。
- 作用:移除之前通过
-
dispatchEvent(event)
- 作用:在当前节点上触发指定事件,从而执行所有绑定到该事件的监听函数。
- 参数:一个Event对象,表示要触发的事件。
- 返回值:布尔值,表示事件是否成功被分派(即是否有监听函数调用了
Event.preventDefault()
方法)。
特点与优势
- 灵活性:EventTarget接口允许为同一个事件添加多个监听函数,这些函数将按照添加顺序触发。
- 控制性:通过
removeEventListener
方法可以灵活地移除不需要的事件监听函数。 - 扩展性:EventTarget接口支持通过
dispatchEvent
方法手动触发事件,这为事件驱动编程提供了更大的灵活性和控制力。
应用示例
EventTarget接口在Web开发中有着广泛的应用,例如:
- 监听按钮点击事件,并执行相应的回调函数。
- 监听表单提交事件,并在提交前进行验证。
- 监听滚动事件,实现懒加载或无限滚动等功能。
事件模型
监听函数
在JavaScript中,事件模型监听函数是通过特定的事件监听方法来注册的,这些方法允许你为DOM元素或JavaScript对象上的事件指定回调函数。这些回调函数会在事件发生时被调用。
element.addEventListener(event, function, useCapture);
event
:要监听的事件类型(如"click"
,"mouseover"
,"keydown"
等)。function
:当事件发生时调用的函数。useCapture
(可选):布尔值,指定事件是否在捕获或冒泡阶段执行。true
表示捕获阶段,false
表示冒泡阶段。默认为false
。
事件绑定
on-属性
这是最原始的事件绑定方式,通过在HTML元素上设置事件属性(如onclick
、onmouseover
等)来指定当事件发生时应该执行的JavaScript代码。这种方式简单但不推荐,因为它将JavaScript代码与HTML标记混合在一起,违反了内容与行为分离的原则。
<button onclick="alert('Button clicked!')">Click me</button>
元素节点的事件属性
这种方式通过JavaScript代码访问DOM元素,并将其事件处理属性设置为一个函数。这种方式比直接在HTML中设置事件属性稍微好一点,但仍然不是最佳实践,因为它不支持同时绑定多个事件处理器。
var button = document.getElementById('myButton');
button.onclick = function() {
alert('Button clicked!');
};
EventTarget.addEventListener
addEventListener
方法是DOM Level 2事件模型的一部分,它允许你为元素添加事件监听器,同时支持添加多个监听器到同一个元素上,并且可以控制监听器是在捕获阶段还是冒泡阶段被调用。这是现代Web开发中推荐的事件绑定方式。
var button = document.getElementById('myButton');
// 添加事件监听器
button.addEventListener('click', function() {
alert('Button clicked!');
});
// 或者使用命名函数,以便稍后可以移除监听器
function handleButtonClick() {
alert('Button clicked!');
}
button.addEventListener('click', handleButtonClick);
// 移除事件监听器
// button.removeEventListener('click', handleButtonClick);
注意,使用addEventListener
方法时,如果你想要之后能够移除该监听器,你应该使用命名函数而不是匿名函数,因为removeEventListener
需要引用相同的函数来移除监听器。
this 的指向
在JavaScript的事件模型中,this
的指向是一个关键概念,它决定了事件处理函数中当前执行上下文的对象。this
的指向具体取决于事件是如何被绑定和触发的。以下是几种不同情况下this
的指向:
- 使用
addEventListener
绑定事件
当使用addEventListener
方法绑定事件处理函数时,this
在事件处理函数内部指向触发事件的DOM元素。这是最常见的用法,也是推荐的方式。
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this); // 指向button元素
});
- 使用HTML属性绑定事件(不推荐)
虽然不推荐这种方式,但了解它的行为也很重要。当直接在HTML元素上使用事件属性(如onclick
)绑定事件处理函数时,this
同样指向触发事件的DOM元素。然而,这种方式将JavaScript与HTML混合,不利于代码的维护和可读性。
<button onclick="handleClick(event)">Click me</button>
<script>
function handleClick(event) {
console.log(this); // 指向button元素,但注意这里通常不会直接用到this,因为event对象提供了更多信息
}
</script>
注意:直接在HTML属性中使用时,虽然this
指向元素本身,但通常我们会通过事件对象event
来访问元素,因为这样可以避免在全局作用域中定义函数,减少全局命名空间的污染。
- 使用箭头函数
如果你使用箭头函数作为事件处理函数,情况就会有所不同。箭头函数不绑定自己的this
,它会捕获其所在上下文的this
值作为自己的this
值。因此,如果箭头函数是在某个对象的方法中定义的,并作为事件处理函数使用,那么它的this
将不会指向触发事件的DOM元素,而是指向定义它的上下文(通常是该对象)。
const obj = {
handleClick: () => {
console.log(this); // 指向定义它的上下文,而不是触发事件的元素
}
};
const button = document.getElementById('myButton');
button.addEventListener('click', obj.handleClick); // 注意:这里可能不是你想要的行为
为了解决这个问题,你可以将箭头函数改为传统函数,或者在绑定事件时显式地设置this
的值(使用.bind()
、.call()
或.apply()
方法)。
- 使用
.bind()
、.call()
和.apply()
改变this
指向
这些方法允许你在调用函数时显式地设置this
的值。这在需要将this
指向特定对象时非常有用,但通常不用于事件处理函数的直接绑定,因为它们会返回一个新的函数,而不是直接绑定事件。然而,在某些复杂的场景中,你可能会使用这些方法来处理this
的指向问题。
事件的传播(冒泡)
事件捕获(Event Capturing)
- 定义:事件捕获是从DOM树的最外层(通常是
document
对象)开始,然后向下逐级传递到目标元素的过程。在这个过程中,如果任何节点上设置了捕获阶段的事件监听器,它们会按照从外到内的顺序依次执行。 - 特点:
- 从DOM树的根节点开始,向目标节点传播。
- 只有设置为捕获阶段工作的监听器才会被调用。
- 可以通过
addEventListener
方法的第三个参数设置为true
来指定监听器在捕获阶段工作。
目标阶段(Target Phase)
- 定义:当事件传播到目标元素时,会触发目标元素上注册的所有监听器,而不管其捕获标志的值如何。这是事件处理的核心阶段,因为事件的目标(即实际发生事件的元素)会在此阶段接收到事件并作出响应。
- 特点:
- 事件对象到达目标元素。
- 目标元素上注册的所有监听器都会被执行,不考虑捕获或冒泡标志。
事件冒泡(Event Bubbling)
- 定义:事件冒泡是事件从最内层(即实际触发事件的目标元素)开始,然后向外逐级向上传播至DOM树的更高级别节点的过程。在这个过程中,如果任意祖先元素上设置了冒泡阶段的事件监听器,则这些监听器会在事件冒泡经过时被执行。
- 特点:
- 从目标元素开始,逐级向上传播到DOM树的根节点。
- 只有标记为非捕获(即
addEventListener
第三个参数为false
或省略)的监听器才会被调用。 - 默认情况下,大多数浏览器使用冒泡阶段来处理事件。
事件传播的执行顺序
当点击一个内层元素时,事件的执行顺序通常遵循以下步骤:
- 事件捕获阶段:从
document
对象开始,向下传播到目标元素。如果在DOM路径上的任何父元素上设置了捕获阶段的事件监听器,则这些监听器会按照从外到内的顺序依次触发。 - 目标阶段:事件到达目标元素,触发目标元素上注册的所有监听器。
- 事件冒泡阶段:从目标元素开始,向上冒泡至DOM树的更高级别节点。如果在内层元素的任何祖先元素上绑定了冒泡阶段的事件监听器,则这些监听器会在事件冒泡经过时按照由内向外的顺序依次触发。
事件传播的终止
事件传播可以通过调用事件对象的stopPropagation()
方法来终止。这意味着传播路径中当前节点之后节点上的所有监听器将不会被调用。此外,stopImmediatePropagation()
方法会立即停止事件传播,并阻止当前节点上的其他监听器被调用。
事件的代理
事件模型中的事件代理(Event Delegation),又称为事件委托,是JavaScript中一种常用的绑定事件的技术。它允许开发者将事件监听器绑定在父元素上,而不是直接绑定在子元素上,从而代理所有子元素上触发的事件。这种技术利用了事件冒泡的原理,即当一个事件在子元素上触发时,它会冒泡到父元素,并由父元素上的事件监听器来处理。
事件代理的优势
- 减少内存消耗和提高性能:通过事件代理,不需要为每个子元素单独添加事件监听器,从而减少了内存消耗。特别是在处理大量元素或动态生成的元素时,这种优势尤为明显。
- 简化代码:使用事件代理可以将相似的事件处理逻辑集中在一个地方,避免了代码的重复,使得代码更加简洁和易于维护。
- 动态添加和删除子元素:当动态添加或删除子元素时,无需重新绑定事件监听器,因为事件监听器是绑定在父元素上的,与子元素的增减无关。
示例:
// 获取父元素
const parent = document.querySelector('#parent');
// 绑定点击事件处理程序
parent.addEventListener('click', function(event) {
// 判断触发事件的元素是否是子元素
if (event.target.classList.contains('child')) {
// 执行相应的操作
console.log('点击了子元素', event.target.textContent);
}
});
Event对象
在JavaScript中,Event对象是用来记录一些事件发生时的相关信息的对象。它包含了事件的详细数据,如事件类型、触发事件的元素、鼠标位置、键盘按键等,允许开发者在事件处理函数中通过访问Event对象的属性和方法来获取这些信息,并据此执行相应的逻辑处理。
Event对象的产生与销毁
- 产生:当事件发生时(如用户点击按钮、键盘按键被按下等),浏览器会自动创建一个Event对象,并将其作为参数传递给绑定在该事件上的处理函数。
- 销毁:在所有事件处理函数运行结束后,Event对象会被销毁,以避免内存泄漏。
Event对象的常用属性
- type:表示事件的类型,如"click"、"keydown"等。
- target:表示触发事件的元素。在事件冒泡或捕获阶段,如果事件处理函数绑定在非目标元素上,
target
属性将指向实际触发事件的元素。 - currentTarget:表示当前正在处理事件的元素。在事件处理函数中,
currentTarget
始终指向绑定事件处理函数的元素,即使事件是从其子元素冒泡上来的。 - clientX/clientY:表示鼠标指针相对于浏览器窗口可视区域左上角的水平/垂直位置。
- pageX/pageY:表示鼠标指针相对于整个文档的水平/垂直位置,考虑了文档的滚动情况。
- key/keyCode/which:在键盘事件中,
key
属性表示按下的键的名称(如"ArrowDown"),而keyCode
和which
(keyCode
已废弃,建议使用which
)属性表示按键的键码或字符编码。注意,keyCode
属性在不同浏览器间可能存在差异,且已被废弃,建议使用event.key
或event.which
。 - shiftKey/ctrlKey/altKey/metaKey:表示在触发事件时是否按下了Shift、Ctrl、Alt或Meta(在Mac上为Command键)键。
- bubbles:表示事件是否冒泡。这是一个只读属性,如果事件会冒泡,则返回
true
;否则返回false
。 - cancelable:表示事件是否可以取消。这是一个只读属性,如果事件可以被取消(即可以通过调用
preventDefault()
方法来阻止其默认行为),则返回true
;否则返回false
。 - preventDefault():取消事件的默认行为。例如,阻止链接的默认跳转行为或表单的默认提交行为。
- stopPropagation():停止事件的进一步传播(冒泡或捕获)。
- timeStamp:表示事件触发的时间戳,即事件发生的时间距离1970年1月1日00:00:00 UTC的毫秒数。
事件类型
事件类型 | 描述 |
---|---|
click |
鼠标点击元素时触发 |
dblclick |
鼠标双击元素时触发 |
mousedown |
鼠标按钮在元素上按下时触发 |
mouseup |
鼠标按钮在元素上释放时触发 |
mousemove |
鼠标指针在元素内部移动时触发 |
mouseover |
鼠标指针移动到元素上方时触发 |
mouseout |
鼠标指针从元素上方移开时触发 |
mouseenter |
鼠标指针进入元素时触发(不冒泡) |
mouseleave |
鼠标指针离开元素时触发(不冒泡) |
keydown |
用户按下键盘上的任意键时触发 |
keyup |
用户释放键盘上的键时触发 |
keypress |
用户按下并释放键盘上的键,且该键产生一个可打印字符时触发(已废弃,建议使用keydown 或keyup ) |
load |
页面或图像等资源加载完成时触发 |
unload |
用户离开页面时触发(在现代浏览器中,由于安全限制,unload 事件的处理程序无法修改页面内容) |
scroll |
滚动条滚动时触发 |
resize |
窗口或框架大小改变时触发 |
change |
表单元素的值改变且失去焦点时触发(对于<select> 元素,选中项改变时即触发) |
submit |
提交表单时触发 |
reset |
重置表单时触发 |
focus |
元素获得焦点时触发 |
blur |
元素失去焦点时触发 |
touchstart |
触摸点放在触摸屏上时触发 |
touchmove |
触摸点在触摸屏上移动时触发 |
touchend |
触摸点从触摸屏上移开时触发 |
touchcancel |
系统取消触摸事件时触发(如触摸点过多,或浏览器认为触摸操作不再必要) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)