黄子涵

第10章 事件

绩效

章节 代码量(行)
10.1 0
10.2 236

10.1 事件驱动程序设计

在 JavaScript 中,最为重要的一件事就是对事件进行处理。与通常的 GUI 应用程序相同,Web 应用程序也是通过事件驱动程序设计的方式来实现其功能的。在事件驱动程序设计中,需要注册不同事件的处理方式。

在注册了事件的处理方式之后,浏览器就会在该事件发生时执行所注册的处理方式。所登录的处理方式被称作事件处理程序、事件句柄或事件侦听器。

对于 Web 应用来说,有下面这些代表性的事件:点击某个元素、将鼠标移动至某个元素上方、按下键盘上某个键,等等。此外,读取页面或跳转至其他页面等行为也会引发事件。根据这些不同的用户操作,浏览器会触发相应的事件。之后,执行事件处理程序,处理被触发的事件。

所以说,JavaScript 程序设计的基本内容之一就是获取需要对事件进行捕捉的元素,并针对该元素注册相应的事件处理程序。

DOM Level 2 中定义了标准的事件模型。大部分现代浏览器都是根据这一标准实现的。但是,在Internet Explorer 8 以及更早版本中,采用了自定义的事件模型实现方式。从功能上来说,这确实和标准事件模型没有太大的差别,但 API 是完全不同的,应当加以注意。

10.2 事件处理程序/事件侦听器的设定

对事件的处理方式被称为事件处理程序或事件侦听器,但这两者之间其实是有区别的。它们的设定方法并不相同,因此,两者支持的处理元素数量也不同。对于 1 个元素或事件,只能设定 1 个事件处理程序。而与之相对的,可以为其同时设定多个事件侦听器。

下面是一些对事件处理进行设定的方式。

  • 指定为 HTML 元素的属性(事件处理程序)
  • 指定为 DOM 元素的属性(事件处理程序)
  • 通过 EventTarget.addEventListener() 进行值定(事件侦听器)

10.2.1 指定为 HTML 元素的属性

将事件处理程序指定为 HTML 元素的属性是一种最为简单的设定事件处理程序的方式。在下面的例子中,将会在发生按钮点击事件时显示含有 “黄子涵是帅哥!” 和 “黄子涵是靓仔!” 消息的提示对话框。

<!DOCTYPE html>
<html lang="zh">
<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>指定为 HTML 元素的属性</title>
</head>
<body>
<input id="hzh" type="button" value="黄子涵" onclick="console.log('黄子涵是帅哥!');
console.log('黄子涵是靓仔!')">
</body>
</html>

image

在这个例子中,通过字符串对 onclick 事件处理程序将要执行的 JavaScript 代码进行了设定。如果代码包含多行,则可以通过分号分隔。当然,事先另外定义一个函数之后再执行该函数的方式也不会有问题。

这种方式的优点在于,设定步骤非常简单,并且能够确保事件处理程序会在载入时被设定。而如果使用之后所介绍的一些方法则可能会产生一些问题。即元素在被载入时,其事件处理程序可能还没有被注册,这时用户执行任何本应触发事件的操作,也不会有任何效果。与之相对地,将事件处理程序指定为 HTML 元素的属性的话,就能够确保它在载入的同时被设定。

在书写上也有一些需要注意的地方,就是这里的 onclick 全都是以小写字母书写的。HTML 不会区分大小写字母,所以即使在这里使用了 onClick 也不会有什么差别。但是,XHTML 则会区分大小写字母。考虑到这点,最好还是使用全部小写的 onclick,这样将有助于提高代码的兼容性。

表 10.1 事件处理程序
事件处理程序名 触发的时机
onclick 鼠标点击操作
ondblclick 鼠标双击操作
onmousedown 按下了鼠标按键
onmouseup 放开了鼠标按键
onmousemove 鼠标指针在元素上方移动
onmouseout 鼠标指针从元素上方离开
onmouseover 鼠标指针移动至了元素上方
onkeydown 按下了键盘按键
onkeypress 按过了键盘按键
onkeyup 放开了键盘按键
onchange 更改了input 元素的内容
onblur input 元素失去了焦点
onfocus input 元素获得了焦点
onselect 文本被选取
onsubmit 按下了表单的提交按钮
onreset 按下了表单的重置按钮
onload 载入完成
onunload 文档的载入被撤销(例如页面跳转等情况时)
onabort 图像的读取被中断
onerror 图像读取过程中发生错误
onresize 窗口尺寸发生改变

如果事件处理程序返回了一个 false 值,则会取消该事件的默认行为。例如,当onsubmit 事件处理程序返回了一个 false 时,表单的内容将不会被发送。这在使用 onsubmit 事件处理程序验证表单内容时会很有用,可以在发现内容有误时返回 false 以取消表单数据的发送。另外,如果像代码清单 10.1 中的例子这样,在 <a> 标签的 onclick 事件处理程序中返回 false,则会取消页面的跳转。

代码清单 10.1 在事件处理程序中返回false
<!DOCTYPE html>
<html lang="zh">
<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>在事件处理程序中返回false</title>
</head>
<body>
<a id="hzh" href="www.huangzihan.top" onclick="return stop();">黄子涵</a>
<script>
function stop(event) {
console.log("黄子涵是帅哥!");
return false;
}
</script>
</body>
</html>

image

10.2.2 指定为 DOM 元素的属性

如果一个页面分别使用了 HTML 文件和 JavaScript文件,则应该尽可能少地在 HTML 文件中使用JavaScript 代码,以提高可维护性。因此,最好将事件处理程序的设定全都写在 JavaScript 内。

事件处理程序可以被指定为节点的属性(代码清单10.2)。

代码清单 10.2 将事件处理程序指定为属性
<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="huangzihan">黄子涵</button>
<script>
var huangzihan = document.getElementById('huangzihan');
function hzh() {
console.log("黄子涵");
}
huangzihan.onclick = hzh;
</script>
</body>
</html>

image

需要注意的是,这里被指定为事件处理程序的正是一个函数。像下面这样,以函数执行后的返回值或用于 HTML 标签的字符串的形式来设定的话,将会发生错误。

代码清单 10.3 事件处理程序的设定
<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="hzh1">hzh1</button>
<button id="hzh2">hzh2</button>
<button id="hzh3">hzh3</button>
<script>
var hzh1 = document.getElementById('hzh1');
var hzh2 = document.getElementById('hzh2');
var hzh3 = document.getElementById('hzh3');
function huangzihan() {
console.log("黄子涵");
}
hzh1.onclick = huangzihan(); // 这种方式指定的是函数执行后的返回值,是错误的
hzh2.onclick = "huangzihan()"; // 以字符串的形式指定该函数也是无效的
hzh3.onclick = huangzihan; // 将函数指定为了事件处理程序,而能够正常运行
</script>
</body>
</html>

image

与通过 HTML 标签的属性设定时不同,这里必须全部使用小写字母书写。

而在设定为了属性之后,HTML 标签属性中的内容将会被覆写。因此,如果希望通过 JavaScript 代码在 HTML 标签属性所指定的内容之后再追加新的处理操作,仅采用这种指定 DOM 元素的方法是很难实现的。在 DOM Level 2 Events 中定义的一种方法可以简单地解决这一问题。

10.2.3 通过 EventTarget.addEventListener() 进行指定

注册事件侦听器

虽然之前所介绍的各种方式也能够对事件注册各种各样的处理,但它们都有一个缺点,那就是对于某一个元素的某一个事件,只能够指定 1 种处理操作。

如果只能够指定 1 种处理操作的话,就很难处理复杂的行为。为了弥补这一缺点,在 DOM Level 2 中定义了 EventTarget.addEventListener() 方法(代码清单 10.4)。不过正如前面所说,该方法无法在 Internet Explorer 8 以及更早版本的浏览器中使用。为此可以在 Internet Explorer 中换用 attachEvent() 方法。

代码清单 10.4 注册事件侦听器
<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="hzh1">hzh1</button>
<script>
var hzh1 = document.getElementById('hzh1');
hzh1.addEventListener('click', function(e) {
console.log("黄子涵");
}, false);
</script>
</body>
</html>

image

在注册事件侦听器时,还可以指定第 3 个参数,用以指定从捕获阶段还是从事件冒泡阶段开始执行。在 DOM Level 2 中,这一参数是必须的。而在 DOM Level 3 中,如果省略了该参数,则会默认从事件冒泡阶段开始执行。之前介绍的指定为 HTML 元素属性的方式,以及指定为 DOM 元素属性的方式,都会在事件冒泡阶段执行事件处理程序。如果希望在捕获阶段执行事件处理程序的话,则只能使用EventTarget.addEventListener() 方法了。而在 Internet Explorer 所使用的 attachEvent() 方法中,是没有与之相对应的参数的。在 Internet Explorer 中,事件侦听器总是会在事件冒泡阶段被执行。

事件侦听器的执行顺序

可以通过 addEventListener() 方法对某个特性元素的特定事件设定多个不同的事件侦听器。如果注册了多个事件侦听器,则会产生事件侦听器之间的执行顺序的问题。然而在 DOM Level 2 中并没有对这一点进行定义。在 DOM Level 3 中则是将执行顺序规定为与注册顺序相同。事实上,目前绝大部分的浏览器也都是以注册的顺序对事件侦听器执行的。即使如此,对于和执行顺序有关的处理,还是应该把它们放在同一个事件侦听器中执行,而不应该将它们置于多个不同的事件侦听器之中。

此外,不能同时对事件目标、事件类型及执行阶段都相同的对象注册多个相同的事件侦听器。之后的注册将会被忽略。在这种情况下,事件侦听器的注册顺序不会发生变化,所以其执行顺序也不会改变。

代码清单 10.5 对同一个事件侦听器进行注册
<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="huangzihan">黄子涵</button>
<script>
var huangzihan = document.getElementById('huangzihan');
function sayHuangzihan() {
alert("你好呀!黄子涵。");
}
huangzihan.addEventListener('click', sayHuangzihan, false);
huangzihan.addEventListener('click', sayHuangzihan, false); // 对相同的事件侦听器进行注册将被忽略
huangzihan.addEventListener('click', sayHuangzihan, true);
// 由于执行阶段不同,则将会被作为另一个事件侦听器被注册
</script>
</body>
</html>

image

代码清单 10.6 事件侦听器的执行顺序
<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="huangzihan">黄子涵</button>
<script>
var huangzihan = document.getElementById('huangzihan');
function sayHzh1() {
alert("黄子涵是帅哥!");
}
function sayHzh2() {
alert("黄子涵是靓仔!");
}
function sayHzh3() {
alert("黄子涵真厉害!");
}
function sayHzh4() {
alert("黄子涵真聪明!");
}
huangzihan.addEventListener('click', sayHzh1, false);
huangzihan.addEventListener('click', sayHzh2, false);
huangzihan.addEventListener('click', sayHzh3, false);
huangzihan.addEventListener('click', sayHzh4, false);
huangzihan.addEventListener('click', sayHzh1, false); // 根据规则,这次注册将被忽略
// 在点击按钮时,应该以“黄子涵是帅哥!”、“黄子涵是靓仔!”、“黄子涵真厉害!”、“黄子涵真聪明!”的顺序显示对话框
// 在Firefox、Google Chrome以及Safari中,将会以预想的情况执行
</script>
</body>
</html>

image

事件侦听器对象

通常,只需要使用函数就能够指定事件侦听器。一些浏览器还可以将含有 handleEvent() 方法的对象指定为事件侦听器(代码清单 10.7)。

代码清单 10.7 将对象注册为事件侦听器
<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="huangzihan">黄子涵</button>
<script>
var huangzihan = document.getElementById('huangzihan');
var eventListener = {
hzh1: '黄子涵比彭于晏要帅!',
hzh2: '黄子涵比吴彦祖要帅!',
hzh3: '黄子涵比尤雨溪厉害!',
hzh4: '上面说的都是假话!',
handleEvent: function (e) {
alert(this.hzh1);
alert(this.hzh2);
alert(this.hzh3);
alert(this.hzh4);
}
};
huangzihan.addEventListener('click', eventListener, false);
// 在点击按键时将会显示四个消息对话框
</script>
</body>
</html>

image

原本在 DOM Level 2 Events 中,EventListener 接口的定义仅仅是一种含有 handleEvent() 方法的一种接口。对于Java 这类函数不是第一类的语言来说,这种定义有效的。然而,在 DOM Level 2 Events 的附录中对ECMAScript Language Binding 进行定义时,又规定了 EventListener 对象只是一个函数。

因此,JavaScript 是可以向 addEventListener() 方法传递函数的。DOM Level 3 会对 EventListener 究竟是一个函数还是一个对象进行表述,不过目前还没有定论。所以,可以认为在 JavaScript 中向 addEventListener() 方法传递对象,是一种与 DOM 的定义向背的做法。但是现在主要的浏览器都对这一功能进行了实现,所以如果非要这样使用也不会有什么问题。

还可以将事件对象作为参数传递给一个事件侦听器。

为了便于今后的说明,先在此对两个词进行定义。第一个词是事件目标。这是触发了某个事件的元素,可以通过事件对象的target属性对其引用。另一个词是侦听器目标。这是注册了某个事件侦听器的元素,可以通过事件对象的currentTarget属性对其引用。

10.2.4 事件处理程序 / 事件侦听器内的 this 引用

在事件处理程序内的 this 所引用的对象即是设定了该事件处理程序的元素。如果是像下面这样的代码
,确实是没有什么问题的。

<!DOCTYPE html>
<html lang="zh">
<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>
</head>
<body>
<button id="hzh">黄子涵</button>
<script>
document.getElementById('hzh').onclick = function () {
/* this 是 #hzh元素 */
alert("黄子涵");
};
</script>
</body>
</html>

image

然而,下面这种情况则会有些不同。lib 可能会被认为是 this 所引用的对象,但事实上,this 引用的是设定了事件处理程序的元素。

var Listener = function () {};
lib.handleClick = function (event) { /* this 引用的是 lib?*/ };
document.getElementById('hzh').onclick = lib.handleClick;
// => 在 lib.handleClick 中,this引用的不是 lib 而是 #hzh元素

如果希望在 lib.handleClick 内通过 this 引用 lib,可以像下面这样,先包装一个匿名函数之后设定。

document.getElementById('hzh').onclick = function (event) {
lib.handleClick(event);
// => 在 lib.handleClick 中,this引用的不是 lib 而是 #hzh元素
};

对于事件侦听器来说,上面的情况同样成立。在 JavaScript 中,我们必须对 this 的使用方式十分小心。

10.3 事件的触发

10.4 事件的传播

10.5 事件所具有的元素

10.6 标准事件

10.7 自定义事件

posted @   黄子涵  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示