在前面的两篇文章中,已经介绍了事件的传达机制,以及如何阻止事件的冒泡与默认的行为。 那么,作为「事件三部曲」的最后一篇,我们就来大概介绍一下,DOM 规范之中究竟提供了哪些事件。

事件的种类

浏览器可能发生的事件有很多种,了解这些事件的情境及效果,是 Web 开发不能忽略的基础,接下来介绍一下常见的几种事件:

界面相关事件

界面相关的事件不一定会与使用者对 DOM 的操作有关系,反而大多数与window对象比较有关系。

  • load 事件:

注册在 window 对象上,指的是在页面或某个资源加载成功时触发。注意,页面或资源从浏览器缓存加载,并不会触发load事件。

window.addEventListener('load', function(event) {
console.log('所有资源都加载完成');
});
  • unloadbeforeunload 事件:

load 事件相反,unloadbeforeunload 事件分别会在离开页面或重新整理时触发,而 beforeunload 会跳出对话框询问使用者是否要离开当前页面。

  • error 事件:

error 事件会在 document 或是图片载入错误时触发。由于考虑到维护性,大多事件的注册我会强烈建议使用「非侵入式 JavaScript」的写法,即JavaScript代码和标记的分离。只有 error 事件最适合以 on-event 的写法来处理:

<img src="image.jpg" onerror="this.src='default.jpg'">

像这样,当image.jpg这张图片不存在的时候,马上就会触发error事件,就会通过this.src<img>src 属性替换成指定的图片,是相当实用的技巧。

若是在网页 load 完成后才注册了error事件,你只会看到破图的结果,因为error事件不会再次被触发,后来挂上去的事件处理函数就等于没有一样。

  • resize事件:在改变浏览器窗口大小时触发,主要发生在window对象上面。

  • scroll事件:在文档或文档元素滚动时触发,主要出现在用户拖动滚动条。

  • DOMContentLoaded事件:

类似于load事件,但不同的是,load事件是在网页「所有」资源都已经载入完成后才会触发,而DOMContentLoaded事件是在 DOM 结构被完整的读取跟解析后就会被触发,不须等待外部资源读取完成,因此可以这么说,这两者所监听的阶段不同,可以用下面这张图来解释:

我们在《通过DOM API 查找节点》这篇文章中曾介绍过,<script>标签要是放在<head> ... </head>之间,因为还没解析到网页本体会有选取不到 DOM 的问题对吧?

<html>
<head>
<script>
// 因 Document 结构未载入,无效
document.getElementById('hello').textContent = 'Hello';
</script>
</head>
<body>
<div id="hello"></div>
</body>
</html>

那么,改成这样:

<html>
<head>
<script>
document.addEventListener("DOMContentLoaded", function() {
// 当 document 结构已解析完成才会执行
document.getElementById('hello').textContent = 'Hello';
}, false);
</script>
</head>
<body>
<div id="hello"></div>
</body>
</html>

就可以排除<script>标签放在<head> ... </head>抓不到 DOM 的问题啦。

与大家所熟知的 jQuery$( document ).ready( handler )作用类似。

鼠标相关事件

  • mousedown / mouseup 事件: 这两个事件分别会在鼠标点击了某元素「按下」(mousedown) 按钮,以及「放开」(mouseup)按钮时触发。

  • click 事件: 当鼠标「点击」了某元素时触发。

  • dblclick事件: 当鼠标「连点两次」某元素时触发。

  • mouseenter / mousemove / mouseleave 事件:这三个事件要放在一起看:

  1. 当鼠标光标移入了某元素时,会先触发 mouseenter 事件。
  2. 鼠标光标在这个元素內「移动」时,会连续触发 mousemove 事件。
  3. 直到鼠标光标离开了这个元素,才触发 mouseleave 事件。

这些鼠标相关的事件,都可以通过event.pageXevent.pageY属性去取得目前鼠标在网页对应的坐标。

键盘相关事件

键盘常用相关事件有下列三种,在大多数情况下会将键盘事件注册在 input 的输入框上。

  • keydown 事件: 「压下」键盘按键时会触发 keydown 事件。

  • keypress 事件: 除了 Shift, Fn, CapsLock 这三种按键外按住时会触发,若按着不放则会连续触发。

  • keyup 事件: 「放开」键盘按键时会触发。

如果我们针对同个元素同时绑定了这三个键盘事件,那么这三个事件执行的顺序会是:

keydown
keypress
keyup

若此时想要知道使用者按下的按键是哪个,则可以通过event.keyCode属性来查询。keyCode的对应表可以到这里查看:

例如,今天你想要当使用者在input输入框按下 「enter」 时,发动submit,就可以这样做:

textBox.addEventListener('keydown', function(e){
// enter 的 keyCode 是 13
if( e.keyCode === 13 ){
formSubmit();
}
}, false);

像这样,通过e.keyCode就可以判断使用者目前按下的是哪个按钮。

表单相关事件

  • input 事件: 当 inputtextarea 以及带有 contenteditable 的元素內容被改变时,就会触发 input 事件。

  • change 事件: 当 inputselecttextarearadiocheckbox 等表单元素被改变时触发。 但与 input 事件不同的是,input 事件会在输入框输入內容的当下触发,而 change 事件则是在目前焦点离开输入框后才触发。

  • submit 事件:当表单被送出时触发,通常表单验证都会在这一步处理,若验证未通过则 return false;

  • focus 事件:当元素被聚焦时触发。

  • blur 事件:当元素失去焦点时触发。

特殊事件

  • Composition Event (组成事件):

Composition Event 其实指的是 compositionstartcompositionend ,以及 compositionupdate 这三个事件。

介绍 Composition Events 之前先来谈谈 DOM API 过去对输入框侦测变化的几个方式:

常见的表单输入框如: <input type="text"> 如果要动态监听输入框的文字变化时, 大多会通过监听 keydownkeypresskeyup 等键盘事件来判断 value 是否变动,但如果是通过「复制粘贴」之类的操作,就无法通过键盘事件来判断。

而即使是 change 事件则是要在使用者改变內容,且焦点离开输入框的前一刻才会被触发。

所以后来有了 input 事件, input 事件会在输入框的內容被改变时即时触发,确实也解决了过去在 onChange 以及键盘相关事件功能不足所产生的问题。

但是,新的问题来了!

通常像这样的搜索框,我们会用类似 autocomplete (自动完成) 的方式给使用者搜索建议 (以 google 为例):

如上图,在输入中文的时候,通常会需要通过注音之类的输入法来做拼字。

但是在大部分的情況下,针对「注音符号」或是「拼音文字」去给搜索建议是没有太大意义的。

这个时候就需要通过 Composition Events 来为输入框做增强。

通过 Composition Events 我们可以观察使用者在输入框內开启输入法 (Input Method Editor, IME) 时,组字或选字的状态。

Composition Events 提供三个事件给开发者监听:分別是 compositionstartcompositionend ,以及 compositionupdate

  • compositionstart: 输入框內开启输入法,且正在拼字时触发。
  • compositionupdate: 输入框內开启输入法,且正在拼字或选字时更改了內容时触发。
  • compositionend: 输入框內开启输入法,拼字或选字完成,正要送出至输入框时触发。

执行的时候像这样:

可以看到,如果要确认使用者输入完成并送出文字时,就可以通过compositionend来做最后确认。

自订事件

自订事件可以用Event constructor建立,同样通过addEventListener去监听,由dispatchEvent决定触发的时间。

var event = new Event('build');

// 监听事件
elem.addEventListener('build', function (e) { ... }, false);

// 触发事件
elem.dispatchEvent(event);

若是想要在自订事件內增加更多资料,则可以改用CustomEvent

var event = new CustomEvent('build', { 'detail': elem.dataset.time });

那么在 Event 处理函数就可以通过event来接收:

function eventHandler(e) {
log('The time is: ' + e.detail);
}

当然,浏览器提供的事件相当多,今天分享的部分主要是比较常见的一些事件,以及工作上实际需要特別注意的部分。

其他的多数事件你都可以在 MDN 的 Event reference 找到。

如果觉得文章对你有些许帮助,欢迎在我的GitHub博客点赞和关注,感激不尽!



JavaScript  

JavaScript 事件 event

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!