事件流
用来描从页面中接收事件的顺序。目前有两种事件流,一种是由IE提出的冒泡流,以及由网景提出的捕获流。
- 事件冒泡:事件开始由最具体的元素(文档中嵌套层次最深的元素),接收,然后逐级向上传播到最不具体的节点(document),就像气泡由水底向上冒一样。
- 事件捕获:与时间冒泡相反,事件由最不具体的事件到最具体的事件
- DOM事件流:“DOM2级事件”规定,事件流包括事件捕获阶段、处于目标阶段和事件冒泡阶段。在DOM事件流中,实际的目标元素,在捕获阶段不会接收到事件(如图1-2-3-4)当事件流“处于目标”阶段时,事件发生(如图4-5),此时在事件处理中会被当做冒泡的一部分,最后是冒泡阶段发生,事件又传播回去(如图5-6-7-8)
事件处理程序
事件是文档或浏览器窗口中发生的特定交互瞬间。JS和HTML之间的交互就是通过事件来实现的。例如click、load,响应某个事件的函数就叫做事件处理程序,由“on”开头,后跟对应的事件名称。例如onclick,onload。为事件指定处理程序的方式有很多种。
HTML事件处理程序
某个元素支持的某种事件,都可以使用一个与响应事件处理程序同名的HTML属性来指定,这个属性的值,应该是一段能够执行的js代码
<input type="button" value="Click Me" onclick="console.log(event.type)">
// 将JS代码与HTML分离
<script>
function showMessage(event) {
console.log(event.type);
}
</script>
<input type="button" value="Click Me" onclick="showMessage(event)">
DOM0 级事件处理程序
<input type="button" value="Click Me" id="btn">
<script>
var btn=document.getElementById("btn");
btn.onclick=function(){
console.log(this.id); // 输出 btn
}
</script>
DOM2 级事件处理程序
在DOM2中,所有的节点都包含addEventListener()
和removeEventListener()
这两个方法,用于处理和删除时间处理程序的操作。它们接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后的布尔值参数是 true 表示在捕获阶段调用事件处理程序,如果是 false(默认) 表示在冒泡阶段调用事件处理程序。
<input type="button" value="Click Me" id="btn">
<script>
//绑定的点击事件会依次触发
var btn=document.getElementById("btn");
btn.addEventListener("click",function(){
console.log(this.id);
},false);
btn.addEventListener("click",function(){
console.log('Hello word!');
},false);
</script>
通过 addEventListener()
添加的事件处理程序只能使用 removeEventListener()
来移除,移除时传入的参数与添加时使用的参数相同,如果传入的是匿名函数,则不会当做相同参数而无法被移除。
IE 事件处理程序
在IE中,添加和删除事件处理程序的方法分别是:attachEvent()
和 detachEvent()
。接受事件处理程序名称与事件处理程序函数两个参数,但跟addEventListener()的区别是:
- 事件名称需要加“on”,比如“onclick”
- 没了第三个布尔值,IE8及更早版本只支持事件冒泡
- 仍可添加多个处理程序,但触发顺序是反着来的
还有一点需要注意,DOM0 和 DOM2 级的方法,其作用域都是在其所依附的元素当中,attachEvent()
则是全局,即如果像之前一样使用this.id,访问到的就不是 button 元素,而是 window。
事件对象
在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。所有的浏览器都支持 event 对象,但支持方式不同。
DOM 中的事件对象
兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。
-
bubbles (boolean) — 表明事件是否冒泡
-
cancelable (boolean) — 这个变量指明这个事件的默认行为是否可以通过调用 event.preventDefault 来阻止。也就是说,只有 cancelable 为 true 的时候,调用 event.preventDefault 才能生效。
-
currentTarget(element) — 当事件遍历DOM时,标识事件的当前目标。它总是引用事件处理程序附加到的元素,而不是event.target,event.target标识事件发生的元素。
-
defaultPrevented (boolean) — 这个状态变量表明当前事件对象的 preventDefault 方法是否被调用过
-
eventPhase (number) — 这个数字变量表示当前这个事件所处的阶段(phase):none(0),capture(1),target(2),bubbling(3)。
-
preventDefault(function) — 这个方法将阻止浏览器中用户代理对当前事件的相关默认行为被触发。比如阻止元素的 click 事件加载一个新的页面
-
stopImmediatePropagation (function) — 这个方法将阻止当前事件链上所有的回调函数被触发,也包括当前节点上针对此事件已绑定的其他回调函数。
-
stopPropagation (function) — 阻止捕获和冒泡阶段中当前事件的进一步传播。
-
target (element) — 事件起源的 DOM 节点(获取标签名:ev.target.nodeName)
-
type (String) — 事件的名称
-
isTrusted (boolean) — 如果一个事件是由设备本身(如浏览器)触发的,而不是通过 JavaScript 模拟合成的,那个这个事件被称为可信任的(trusted)
-
timestamp (number) — 事件发生的时间
currentTarget与target
在事件处理程序内部,对象this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。
//事件处理程序在目标元素上, this、currentTarget、target相等
<input type="button" value="Click Me" id="btn">
<script>
var btn=document.getElementById("btn");
btn.onclick = function (event) {
console.log(event.currentTarget === this); // true
console.log(event.target === this); // true
}
</script>
//如果事件处理程序存在于按钮的父节点,则值不等
<input type="button" value="Click Me" id="btn">
<script>
var btn=document.getElementById("btn");
document.body.onclick = function (event) {
console.log(event.currentTarget === document.body); // true
console.log(this === document.body); // true
console.log(event.target === btn); // true
}
</script>
阻止事件冒泡/停止传播
调用事件对象的 stopPropagation
方法,可以在任何阶段(捕获阶段或者冒泡阶段)中断事件的传播。此后,事件不会在后面传播过程中的经过的节点上调用任何的监听函数。
<input type="button" value="Click Me" id="btn">
<script>
var btn=document.getElementById("btn");
btn.onclick = function (event) {
console.log("Clicked"); // 触发
event.stopPropagation();
}
document.body.onclick = function (event) {
console.log("Body clicked"); // 传播阻断 不触发
}
</script>
注意:调用 event.stopPropagation()
不会阻止当前节点上此事件其他的监听函数被调用。如果你希望阻止当前节点上的其他回调函数被调用的话,可以使用event.stopImmediatePropagation()
方法。
阻止浏览器默认行为
当特定事件发生的时候,浏览器会有一些默认的行为作为反应。最常见的例子就是<a>
标签的默认跳转href属性指定的地址。
如果我们想要使用<a>
标签的同时不触发页面跳转,这时,我们就需要调用 event.preventDefault()
。
我们可以阻止浏览器的很多其他默认行为。比如,我们可以在 HTML5 游戏中阻止敲击空格时的页面滚动行为,或者阻止文本选择框的点击行为。
调用 event.stopPropagation()
只会阻止传播链中后续的回调函数被触发。它不会阻止浏览器的自身的行为。
事件类型
JS的事件很多,常用的可分为以下几类:
- UI:
load
、error
(错误触发)、select
、resize
、scroll
等 - 焦点:
blur
、focus
、change
(当用户提交对元素值的更改时触发)等 - 鼠标:
click
、dblclick
、mousedown/up
、mouseenter/leave
、mousemove
、mouseover
等 - 键盘:
keydown/up
、keypress
- 触摸:
touchstart
(即使已经有一个手指放在屏幕上也会触发)、touchend
、touchmove
等 - 手势:
gesturestart
、gestureend
、gesturechange
等 - 设备:
orientationchange
(检测设备屏幕旋转)、deviceorientation
(检测设备方向变化)等
具体的事件用法就不一一列出。