JavaScript DOM事件

本文最后更新于:8 个月前

DOM内容较多,而且实用性较高,因此从DOM模型中抽离出来,单独成为一篇。本文内容包括:事件接口、事件流模型、Event对象

JavaScript DOM事件

需要了解具体的DOM事件参考:HTML DOM 事件对象 | 菜鸟教程 (runoob.com)

声明:此文大部分内容整理自事件 - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack,此文只整理了笔者认为核心重要的内容,需要了解更多详细信息,请参考原文

与单纯阅读相比,整理的过程其实是比较耗时间的,那为什么还要花更多的时间去整理呢?目的是为了提高后续再复习时的效率,为了归纳积累出一个逻辑清晰的知识体系。好记性不如烂笔头,再好的记性也没办法记得所有的东西,尤其还是同质化的知识,预期想着如何一次记得多么深刻,不如想想怎么要二次三次复习更加高效。同时整理归纳的过程也是一遍记忆的过程,在文档中归纳的过程也是在脑海中归纳的过程,说到底这也算是一种记忆方法和技巧。

事件的本质是程序各个组成部分之间的一种通信方式,也是异步编程的一种实现

DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequestAudioNodeAudioContext)也部署了这个接口

该接口主要提供三个实例方法。

  • addEventListener:绑定事件的监听函数
  • removeEventListener:移除事件的监听函数
  • dispatchEvent:触发事件

一、EventTarget接口

1、addEventListener

参数依次为:

  • 事件名称
  • 监听函数
  • boolean值,触发类型(true-事件捕获or false-事件冒泡)

关于参数,有两个地方需要注意。首先,第二个参数除了监听函数,还可以是一个具有handleEvent方法的对象。

1
2
3
4
5
buttonElement.addEventListener('click', {
handleEvent: function (event) {
console.log('click');
}
});

其次,第三个参数除了布尔值useCapture,还可以是一个属性配置对象。该对象有以下属性。

  • capture:布尔值,表示该事件是否在捕获阶段触发监听函数
  • once:布尔值,表示监听函数是否只触发一次,然后就自动移除
  • passive:布尔值,表示监听函数不会调用事件的preventDefault方法。如果监听函数调用了,浏览器将忽略这个要求,并在监控台输出一行警告。

如果希望事件监听函数只执行一次,可以打开属性配置对象的once属性

1
2
3
element.addEventListener('click', function (event) {
// 只执行一次的代码
}, {once: true});

addEventListener方法可以为针对当前对象的同一个事件,添加多个不同的监听函数。这些函数按照添加顺序触发,即先添加先触发。如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener方法手动去除)。

1
2
3
4
5
function hello() {
console.log('Hello world');
}
document.addEventListener('click', hello, false);
document.addEventListener('click', hello, false);

具体的实践比较请参阅:VSCode: 前端练习/DOM事件模型.html

2、removeEventListener

参数:与addEventListener完全一致

返回值:没有返回值

注意:removeEventListener方法移除的监听函数,必须是addEventListener方法添加的那个监听函数,而且必须在同一个元素节点,否则无效。包括第三个参数也必须相同,否则删除无效。

3.dispatchEvent

EventTarget.dispatchEvent方法在当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),则返回值为false,否则为true

dispatchEvent方法的参数是一个Event对象的实例

1
2
3
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);

二、事件模型

1、事件的传播

image-20220703164429468

一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。

  • 第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
  • 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
  • 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。

addEventListener方法可以设置节点在某个阶段的监听函数,理论上来说,两个节点最多可以设置4个监听函数(2个在捕获阶段、2个在冒泡阶段,目标阶段在捕获时触发一次,冒泡时触发一次)

2、监听函数的设置

一共有3种方式

  • HTML的on-属性,只会在冒泡阶段触发,违反了 HTML 与 JavaScript 代码相分离的原则,不推荐
  • 元素节点的事件属性,只会在冒泡阶段触发,同一个事件只能定义一个监听函数,不推荐
  • addEventListener,可以设置在冒泡还是捕获阶段触发,推荐的方式

HTML的on-属性

1
2
<body onload="doSomething()">
<div onclick="console.log('触发事件')">

HTML 语言允许在元素的属性中,直接定义某些事件的监听代码,但是这些属性的值必须是执行代码,而不是函数

元素的事件监听属性,都是on加上事件名,比如onload就是on + load,表示load事件的监听代码

元素的节点属性

1
2
3
4
window.onload = doSomething;
div.onclick = function (event) {
console.log('触发事件');
};

它接收到值是函数,而不是执行代码,与上一点不同

addEventListener

1
window.addEventListener('load', doSomething, false);

3、this的指向

三种方式,监听函数内部的this指向触发事件的那个元素节点

1
<button id="btn" onclick="console.log(this.id)">点击</button>

4、事件代理

把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,可以减少DOM节点绑定的函数数量,同时便于统一处理,典型的应用如 ul列表 事件的绑定

1
2
3
4
5
6
var ul = document.querySelector('ul');
ul.addEventListener('click', function (event) {
if (event.target.tagName.toLowerCase() === 'li') {
// some code
}
});

如果希望事件到某个节点为止,不再传播,可以使用事件对象的stopPropagation方法。

1
2
3
4
5
6
7
8
9
// 事件传播到 p 元素后,就不再向下传播了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, true);

// 事件冒泡到 p 元素后,就不再向上冒泡了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, false);

但是,stopPropagation方法只会阻止事件的传播,不会阻止该事件触发<p>节点的其他click事件的监听函数。

如果想要彻底取消该事件,不再触发后面所有click的监听函数,可以使用stopImmediatePropagation方法。

1
2
3
4
5
6
7
8
p.addEventListener('click', function (event) {
event.stopImmediatePropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 不会被触发
console.log(2);
});

三、Event对象

事件发生以后,会产生一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例

1、Event概述

Event对象本身就是一个构造函数,可以用来生成新的实例。

1
event = new Event(type, options);
  • type,字符串,表示事件的名称
  • options,对象,表示事件对象的配置,主要有两个属性:
    • bubbles:布尔值,可选,默认为false,表示事件对象是否冒泡
    • cancelable:布尔值,可选,默认为false,表示事件是否可以被取消,即能否用Event.preventDefault()取消这个事件。一旦事件被取消,就好像从来没有发生过,不会触发浏览器对该事件的默认行为。
1
2
3
4
5
6
7
8
var ev = new Event(
'look',
{
'bubbles': true,
'cancelable': false
}
);
document.dispatchEvent(ev);

2、实例属性

Event.eventPhase

返回一个整数常量,表示事件目前所处的阶段。该属性只读。

  • 0,事件目前没有发生。
  • 1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。
  • 2,事件到达目标节点,即Event.target属性指向的那个节点。
  • 3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。

Event.cancelable

返回一个布尔值,表示事件是否可以取消。该属性为只读属性。大多数浏览器的原生事件是可以取消的。比如,取消click事件,点击链接将无效。

Event.cancelable属性为true时,调用Event.preventDefault()就可以取消这个事件,阻止浏览器对该事件的默认行为。如果事件不能取消,调用Event.preventDefault()会没有任何效果。

currentTarget & target

  • Event.target,事件的原始触发节点,触发事件的节点,不会改变
  • Event.currentTarget,事件当前正在通过的节点,当前执行监听函数所在的那个节点,随着事件的传播而改变

前者通常是后者的后代节点。

Event.isTrusted

Event.isTrusted属性返回一个布尔值,表示该事件是否由真实的用户行为产生。比如,用户点击链接会产生一个click事件,该事件是用户产生的;Event构造函数生成的事件,则是脚本产生的。

3、实例方法

preventDefault()

Event.preventDefault方法取消浏览器对当前事件的默认行为。该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。如果要阻止传播,可以使用stopPropagation()stopImmediatePropagation()方法

1
2
3
4
5
6
7
8
9
// HTML 代码为
// <input type="text" id="my-input" />
var input = document.getElementById('my-input');
input.addEventListener('keypress', checkName, false);
function checkName(e) {
if (e.charCode < 97 || e.charCode > 122) {
e.preventDefault();
}
}

stopPropagation()

阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。

1
2
3
4
function stopEvent(e) {
e.stopPropagation();
}
el.addEventListener('click', stopEvent, false);

Event.stopImmediatePropagation()

阻止同一个事件的其他监听函数被调用,不管监听函数定义在当前节点还是其他节点。如果同一个节点对于同一个事件指定了多个监听函数,这些函数会根据添加的顺序依次调用。只要其中有一个监听函数调用了Event.stopImmediatePropagation方法,其他的监听函数就不会再执行了。


JavaScript DOM事件
http://timegogo.top/2022/09/28/JavaScript/DOM事件/
作者
丘智聪
发布于
2022年9月28日
更新于
2023年7月16日
许可协议