一、什么是事件
MDN:
事件是您在编程时系统内发生的动作或者发生的事情,系统响应事件后,如果需要,您可以某种方式对事件做出回应。例如:如果用户在网页上单击一个按钮,您可能想通过显示一个信息框来响应这个动作。在这篇文章中,我们将讨论一些关于事件的重要概念,并且观察它们在浏览器上如何运行。这篇文章不会面面俱到,仅聚焦于您现阶段需要掌握的知识。
在Web中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:
- 用户在某个元素上点击鼠标或悬停光标。
- 用户在键盘中按下某个按键。
- 用户调整浏览器的大小或者关闭浏览器窗口。
- 一个网页停止加载。
- 提交表单。
- 播放、暂停、关闭视频。
- 发生错误。
三、使用网页事件的方式
兼容性很好
Netscape already started in Version 3, Explorer followed in Version 4.
So both browsers, and in fact all modern browsers, accept this code
// ondblclick onmouseover onmouseout
element.onclick = doSomething;
移除事件:
不通过事件触发
Microsoft has added the fireEvent() method to Explorer 5.5 and higher on Windows for the same purpose. The syntax is element.fireEvent('onclick')
问题
传统事件模型只能接受一个函数,如果注册多个事件,就会被后面的所覆盖
element.onclick = startDragDrop;
// 覆盖上面的 startDragDrop
element.onclick = spyOnUser;
2.早期的事件回调(行内事件处理器-请勿使用)
<A HREF="somewhere.html" onClick="alert('I\'ve been clicked!')">
<A HREF="somewhere.html" onClick="doSomething()">
3.addEventListener() 和 removeEventListener()-DOM2
W3C 和微软都各自开发了他们自己的一套事件模型用于替换 Netscape 的传统事件模型。
W3C 的 DOM2 事件模型提供了在同一元素上注册多个事件的方式。
var btn = document.querySelector('button');
function bgChange() {
var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
btn.addEventListener('click', bgChange);
// addEventListener() 三个参数:事件类型、执行函数(或匿名函数)、布尔值
element.addEventListener('click',doSomething,false)
注册多个:
// 先后执行顺序无法确定
element.addEventListener('click',startDragDrop,false)
element.addEventListener('click',spyOnUser,false)
移除事件:
element.removeEventListener('click',spyOnUser,false)
匿名函数(Anonymous functions)
element.addEventListener('click',function () {
this.style.backgroundColor = '#cc0000'
},false)
this
在 Javascript 中,this 始终指向函数的 “owner” ,在事件回调中可以方便的使用 this 去操作 HTML element。
element.addEventListener('click',doSomething,false);
another_element.addEventListener('click',doSomething,false);
function doSomething() {
this.style.backgroundColor = '#cc0000';
}
是否注册了事件?
W3C 的事件模型有个问题就是不能知道元素是否已经注册了哪些事件。
在传统事件模型中,我们可以这么做:
被注册的函数会被显示,undefined 表示没有注册。在最新的 DOM Level 3 Events 中添加了eventListenerList 去存储注册事件,不过 api 太新,兼容性不够。
庆幸的是,removeEventListener() 不会报错,哪怕没有注册,所以可以放心使用。
2).Microsoft(不推荐)
// 注册
element.attachEvent('onclick',doSomething)
// 注册多个
element.attachEvent('onclick',startDragDrop)
element.attachEvent('onclick',spyOnUser)
// 移除
element.detachEvent('onclick',spyOnUser)
DOM3级事件
DOM3级事件在DOM2级事件的基础上添加了更多的事件类型,全部类型如下:
- UI事件,当用户与页面上的元素交互时触发,如:load、scroll
- 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
- 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
- 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
- 文本事件,当在文档中输入文本时触发,如:textInput
- 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
- 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
- 变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
同时DOM3级事件也允许使用者自定义一些事件
假设以下场景:element1、element2 都有一个 onClick 事件,如果点击 element2,两个点击事件都会触发,但谁先触发就是下面要说的事件顺序
-----------------------------------
| element1 |
| ------------------------- |
| |element2 | |
| ------------------------- |
| |
-----------------------------------
1.两个模型
- Netscape 的定义 element1 先触发. 所谓事件捕获(event capturing).
- Microsoft 的定义 element2 优先. 叫做事件冒泡(event bubbling).
Explorer only supports event bubbling. Mozilla, Opera 7 and Konqueror support both. Older Opera's and iCab support neither.
1).事件捕获
| |
---------------| |-----------------
| element1 | | |
| -----------| |----------- |
| |element2 \ / | |
| ------------------------- |
| Event CAPTURING |
-----------------------------------
element1 的事件先触发,element2 事件最后触发。
2).事件冒泡
/ \
---------------| |-----------------
| element1 | | |
| -----------| |----------- |
| |element2 | | | |
| ------------------------- |
| Event BUBBLING |
-----------------------------------
element2 的事件先触发,element1 事件最后触发。
2.W3C 模型
W3C 整合后就是我们目前在使用的事件捕获、冒泡模型了。即任何事件都是先捕获直到抵达 target,然后再冒泡。
| | / \
-----------------| |--| |-----------------
| element1 | | | | |
| -------------| |--| |----------- |
| |element2 \ / | | | |
| -------------------------------- |
| W3C event model |
------------------------------------------
开发者可以自由选择是在捕获阶段还是冒泡阶段去注册事件。即通过 addEventListener() 函数的第三参数。如果是 true,则设定为捕获阶段,如果为 false,则设定为冒泡。
场景一:
element1.addEventListener('click',doSomething2,true)
element2.addEventListener('click',doSomething,false)
如果点击 element2:
- 点击事件从捕获阶段开始,查找任何 element2 父元素的捕获点击事件。
- 发现在 element1 上的
doSomething2() 事件,并执行。
- 找到目标本身,捕获阶段没有其他事件了。事件循环开始冒泡阶段,在目标本身找到并执行
doSomething()。
- 事件向上冒泡查找父元素是否有冒泡阶段的事件,直到结束。
场景二:
element1.addEventListener('click',doSomething2,false)
element2.addEventListener('click',doSomething,false)
如果点击 element2:
- 点击事件从捕获阶段开始,可以捕获到任何 element2 父元素的捕获点击事件,没有发现。
- 事件向下查找到目标本身。开始进入冒泡阶段,并执行注册在 element2 上的冒泡事件
doSomething()。
- 事件向上查找,检查目标的父元素是否有冒泡事件。
- 找到 element1,所以
doSomething2 执行
关闭
有时候希望停止冒泡或者捕获。
Microsoft
window.event.cancelBubble = true
W3C model
stopPropagation:阻止捕获和冒泡阶段中当前事件的进一步传播
stopImmediatePropagation:如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行。(译者注:注意与 event.stopPropagation() 之间的区别)
preventDefault:Event 接口的 **preventDefault()**方法,告诉user agent:如果此事件没有被显式处理,它默认的动作也不应该照常执行。此事件还是继续传播,除非碰到事件侦听器调用stopPropagation() 或stopImmediatePropagation(),才停止传播。
三个阶段
一旦查找顺序确定,事件就会经历三个阶段:捕获阶段、目标阶段、冒泡阶段。如果一个阶段不支持则会跳过,或者一个阶段的 propagation 被停止,也会跳过。举个例子,如果bubbles 属性呗设为 false,那么冒泡阶段就会被跳过。如果 stopPropagation() 在之前被调用,那么所有阶段都会跳过。
- 捕获阶段:事件对象在目标对象的父元素中查找,从 Window 到目标的父元素
- 目标阶段:事件对象到达目标元素,也叫在目标阶段。如果事件类型表明事件无需冒泡,则这个阶段结束,就会停止。
- 冒泡阶段:事件对象从目标元素的父元素开始直到 Window。

EventTarget.addEventListener()
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
target.addEventListener(type, listener, useCapture, wantsUntrusted ); // Gecko/Mozilla only
-
type:表示监听事件类型的字符串。
-
listener:当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数。有关回调本身的详细信息,请参阅The event listener callback
-
options 可选
一个指定有关 listener 属性的可选参数对象。
可用的选项如下:
capture: Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
once: Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
passive: Boolean,设置为true时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。查看 Improving scrolling performance with passive listeners 了解更多.
mozSystemGroup: 只能在 XBL 或者是 Firefox' chrome 使用,这是个 Boolean,表示 listener 被添加到 system group。
-
useCapture 可选
Boolean,在DOM树中,注册了listener的元素, 是否要先于它下面的EventTarget,调用该listener。 当useCapture(设为true) 时,沿着DOM树向上冒泡的事件,不会触发listener。当一个元素嵌套了另一个元素,并且两个元素都对同一事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。事件传播模式决定了元素以哪个顺序接收事件。进一步的解释可以查看 事件流 及 JavaScript Event order 文档。 如果没有指定, useCapture 默认为 false 。
注意: 对于事件目标上的事件监听器来说,事件会处于“目标阶段”,而不是冒泡阶段或者捕获阶段。在目标阶段的事件会触发该元素(即事件目标)上的所有监听器,而不在乎这个监听器到底在注册时useCapture 参数值是true还是false。
注意: useCapture 仅仅在现代浏览器最近的几个版本中是可选的。 例如 Firefox 6以前的版本都不是可选的。为了能够提供更广泛的支持,你应该提供这个参数。
注意:如果同一个监听事件分别为“事件捕获”和“事件冒泡”注册了一次,这两次事件需要分别移除。两者不会互相干扰。移除捕获监听器不会影响非捕获版本的相同监听器,反之亦然。
事件委托
冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。
一个很好的例子是一系列列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将click单击事件监听器设置在父元素上,这样事件就会从列表项冒泡到其父元素上。
一、什么是事件
二、一些事件 Event reference
在Web中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:
三、使用网页事件的方式
1.传统事件模型-DOM0 traditional model
移除事件:
不通过事件触发
问题
传统事件模型只能接受一个函数,如果注册多个事件,就会被后面的所覆盖
2.早期的事件回调(行内事件处理器-请勿使用)
3.addEventListener() 和 removeEventListener()-DOM2
W3C 和微软都各自开发了他们自己的一套事件模型用于替换 Netscape 的传统事件模型。
1).W3C(Document Object Model (DOM) Level 2 Events )
W3C 的 DOM2 事件模型提供了在同一元素上注册多个事件的方式。
注册多个:
移除事件:
匿名函数(Anonymous functions)
this
在 Javascript 中,this 始终指向函数的 “owner” ,在事件回调中可以方便的使用 this 去操作 HTML element。
是否注册了事件?
W3C 的事件模型有个问题就是不能知道元素是否已经注册了哪些事件。
在传统事件模型中,我们可以这么做:
被注册的函数会被显示,undefined 表示没有注册。在最新的 DOM Level 3 Events 中添加了eventListenerList 去存储注册事件,不过 api 太新,兼容性不够。
庆幸的是,removeEventListener() 不会报错,哪怕没有注册,所以可以放心使用。
2).Microsoft(不推荐)
DOM3级事件
DOM3级事件在DOM2级事件的基础上添加了更多的事件类型,全部类型如下:
同时DOM3级事件也允许使用者自定义一些事件
四、事件顺序(Event order)
假设以下场景:element1、element2 都有一个 onClick 事件,如果点击 element2,两个点击事件都会触发,但谁先触发就是下面要说的事件顺序
1.两个模型
1).事件捕获
element1 的事件先触发,element2 事件最后触发。
2).事件冒泡
element2 的事件先触发,element1 事件最后触发。
2.W3C 模型
W3C 整合后就是我们目前在使用的事件捕获、冒泡模型了。即任何事件都是先捕获直到抵达 target,然后再冒泡。
开发者可以自由选择是在捕获阶段还是冒泡阶段去注册事件。即通过 addEventListener() 函数的第三参数。如果是 true,则设定为捕获阶段,如果为 false,则设定为冒泡。
场景一:
如果点击 element2:
doSomething2()事件,并执行。doSomething()。场景二:
如果点击 element2:
doSomething()。doSomething2执行关闭
有时候希望停止冒泡或者捕获。
Microsoft
W3C model
三个阶段
一旦查找顺序确定,事件就会经历三个阶段:捕获阶段、目标阶段、冒泡阶段。如果一个阶段不支持则会跳过,或者一个阶段的 propagation 被停止,也会跳过。举个例子,如果
bubbles属性呗设为 false,那么冒泡阶段就会被跳过。如果stopPropagation()在之前被调用,那么所有阶段都会跳过。EventTarget.addEventListener()
type:表示监听事件类型的字符串。listener:当所监听的事件类型触发时,会接收到一个事件通知(实现了Event接口的对象)对象。listener必须是一个实现了EventListener接口的对象,或者是一个函数。有关回调本身的详细信息,请参阅The event listener callbackoptions 可选
一个指定有关
listener属性的可选参数对象。可用的选项如下:
capture:Boolean,表示listener会在该类型的事件捕获阶段传播到该EventTarget时触发。once:Boolean,表示listener 在添加之后最多只调用一次。如果是true,listener会在其被调用之后自动移除。passive:Boolean,设置为true时,表示listener永远不会调用preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。查看 Improving scrolling performance with passive listeners 了解更多.mozSystemGroup: 只能在 XBL 或者是 Firefox' chrome 使用,这是个Boolean,表示listener被添加到 system group。useCapture可选Boolean,在DOM树中,注册了listener的元素, 是否要先于它下面的EventTarget,调用该listener。 当useCapture(设为true) 时,沿着DOM树向上冒泡的事件,不会触发listener。当一个元素嵌套了另一个元素,并且两个元素都对同一事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。事件传播模式决定了元素以哪个顺序接收事件。进一步的解释可以查看 事件流 及 JavaScript Event order 文档。 如果没有指定,useCapture默认为 false 。wantsUntrusted如果为
true, 则事件处理程序会接收网页自定义的事件。此参数只适用于 Gecko(chrome的默认值为true,其他常规网页的默认值为false),主要用于附加组件的代码和浏览器本身。事件委托
冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。
一个很好的例子是一系列列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将
click单击事件监听器设置在父元素上,这样事件就会从列表项冒泡到其父元素上。