1.事件流
事件流描述的是从页面中接收事件的顺序。
有两种,分别是:事件冒泡流和事件捕获流。
事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。事件捕获(event capturing)
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。
事件捕获从IE9和现在浏览器才支持他,也就是说支持DOM2事件的浏览器才支持他-
DOM事件流
“DOM2级事件”规定的事件流包括3各阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
在 DOM 事件流中,实际的目标( <div> 元素)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从 document 到 <html> 再到 <body> 后就停止了。下一个阶段是“处于目标”阶段,于是事件在 <div>上发生,并在事件处理(后面将会讨论这个概念)中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。(这里的事件在div上发生,并在事件处理中被看成冒泡阶段的一部分可以这么理解:在“处于目标”阶段,事件处理程序就开始执行了,那么在事件处理程序里面不是有阻止事件冒泡嘛,你要阻止那么就必须有冒泡开始啊,那就把处于目标阶段看成是事件冒泡的一部分,当然只是在事件处理程序里面这么认为,事件流还是分三个阶段。)
2.事件处理程序
- HTML 事件处理程序
行内式
<input type='button' value='click me' onclick='alert("haha")' >
- DOM0级事件处理程序
给元素添加自己的事件处理程序属性。这些属性通常全部小写,比如onclick。
var btn = document.getElementById("mybtn");
btn.onclick = function(){
//事件处理程序的函数部分
// this引用当前元素,即btn
}
以这种方式被添加的事件处理程序在事件流的冒泡阶段被处理。也可以删除通过DOM0级方法指定的事件处理程序,只要向下面这样将事件处理程序属性的值设为null即可:
btn.onclick = null;
如果你是用的是html指定事件处理程序,那么onclick属性的值就是一个包含着在同名html特性中指定的代码的函数。而将相应的属性设置为null,也可以删除以这种方式指定的事件处理程序。(这个就是说行内式绑定事件处理程序相当于dom0级方法指定的事件处理程序,可以用dom0级的方法去获取或换行内式绑定的同名事件的处理程序)。
- DOM2级事件处理程序
两个方法:addEventListener(),removeEventListener()。
所有的DOM节点都包含这两个方法,他们都接受三个参数:要处理的事件名(没有on哦比如click)、作为事件处理程序的函数和一个布尔值。这个布尔值如果是true,表示在事件捕获阶段调用这个事件处理程序,如果是false,表示在冒泡阶段调用事件处理程序。
DOM2级事件处理程序的主要好处是可以添加多个事件处理程序。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
这两个事件处理程序会按照添加他们的顺序触发。先显示元素的ID,再显示“hello world!”消息。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除。在给removeEventListener()传第二个参数的时候,必须传入事件处理函数的函数名,不能写一个内容一样的匿名函数代替,那样是不对的,因为那两个函数是完全不同的函数。
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,我们不建议在事件捕获阶段注册事件处理程序。
- IE事件处理程序
两个方法:attachEvent()和detachEvent。
接收两个参数:事件处理程序名称(有on哦比如'onclick')与事件处理程序函数。由于IE8及跟早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。在使用DOM0级方法的时候,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert(this === window); //true
});
在编写跨浏览器的代码时,牢记这一区别非常重要。
与 addEventListener() 类似, attachEvent() 方法也可以用来为一个元素添加多个事件处理程序。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
btn.attachEvent("onclick", function(){
alert("Hello world!");
});
这里调用了两次 attachEvent() ,为同一个按钮添加了两个不同的事件处理程序。不过,与 DOM方法不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。单击这个例子中的按钮,首先看到的是 "Hello world!" ,然后才是 "Clicked" 。
使用 attachEvent() 添加的事件可以通过 detachEvent() 来移除,条件是必须提供相同的参数。与 DOM 方法一样,这也意味着添加的匿名函数将不能被移除。不过,只要能够将对相同函数的引用传给 detachEvent() ,就可以移除相应的事件处理程序。
var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
};
btn.attachEvent("onclick", handler);
// 这里省略了其他代码
btn.detachEvent("onclick", handler);
支持IE事件处理程序的浏览器只有IE和Opera。
- 跨浏览器的事件处理程序
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
};
事件对象
事件对象存在于事件处理程序函数中,event对象可以使函数的第一个形参,也是window对象的属性。所以在通过HTML特性指定事件处理程序时,变量event未经申明就可以使用,因为他保存着event对象,event是window对象的属性。
事件对象的属性与方法
this, currentTarget, target分别指的是什么?他们的关系是什么?
在事件处理程序内部,对象this始终等于currentTarget的值,即事件绑定的那个对象,而target则只包含时间的实际目标。看个例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this); //true
alert(event.target === this); //true
};
由于click事件的目标是按钮,因此这三个值是相等的。如果事件处理程序存在于按钮的父节点中(例如document.body),那么这些值是不相同的。看下面这个例子:
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
};
当单击这个例子中的按钮时,this和currentTarget都等于document.body,即事件绑定的那个对象。而target则是事件最具体发生的那个对象,即按钮。(点击在按钮上的)
还有就是事件捕获只能在DOM2二级事件处理程序里存在,DOM0级和HTML里面绑定的事件都是发生在事件冒泡或者处于目标阶段。
event.type
event.type 指的是事件的类型,如点击事件,他的值就是'click'。
preventDefault()
想要阻止特定事件的默认行为,可以使用preventDefault()方法,例如阻止连接在被点击之后默认会跳转到href特性指定的URL。你可以在事件处理程序函数里面调用event.preventDefault()方法。
stopPropagation()
stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。
看下面的这个例子:
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event.stopPropagation();
};
document.body.onclick = function(event){
alert("Body clicked");
};
对于这个例子而言,如果不调用 stopPropagation() ,就会在单击按钮时出现两个警告框。可是,由于 click 事件根本不会传播到 document.body ,因此就不会触发注册在这个元素上的 onclick 事件处理程序。
stopImmediatePropagation()
如果某个元素有多个相同类型事件的事件监听函数,则当该类型的事件触发时,多个事件监听函数将按照顺序依次执行.如果某个监听函数执行了 event.stopImmediatePropagation()方法,则除了该事件的冒泡行为被阻止之外(event.stopPropagation方法的作用),该元素绑定的后序相同类型事件的监听函数的执行也将被阻止。
eventPhase
事件对象的eventPhase属性可以用来确定当前事件处于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么eventPhase等于1;如果事件处理程序在处于目标阶段调用,那么eventPhase等于2,如果是在冒泡阶段调用的事件处理程序,那么eventPhase等于3.这里注意一点:尽管“处于目标”发生在冒泡阶段,但eventPhase仍然一直等于2。发现没有:当事件捕获阶段和事件冒泡阶段调用事件处理程序的时候,event.target不等于this对象和currentTarget属性,而只有在eventPhase等于2的时候,这三个才相等。
当事件处理程序绑定的对象和事件发生的那个具体的对象是同一个对象的时候,那么eventPhase一定等于2,this、currentTarget、target也一定相等。就算这时候用DOM2级事件去绑定事件处理程序,并传入true,要他在事件捕获阶段调用事件处理程序,他的eventPhase值还是2,因为在DOM事件流中,实际的目标(target)在捕获阶段不会接收到事件。
只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。
事件类型
- load
- unload
- resize
- scroll
客户区坐标位置
event.clientX 和event.clientY
页面坐标位置
event.pageX和event.pageY
在页面没有滚动的情况下,pageX和pageY的值与clientX和clientY相等。
在IE8及更早版本不支持事件对象上的页面坐标,不过使用客户区坐标和滚动信息可以计算出来。这时需要用到 document.body (混杂模式)或 document.documentElement (标准模式)中的scrollLeft和scrollTop的属性。
屏幕坐标位置
event.screenX 和event.screenY
修改键
虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键就是 Shift、Ctrl、Alt 和 Meta(在 Windows键盘中是 Windows键,在苹果机中是 Cmd 键),它们经常被用来修改鼠标事件的行为。DOM 为此规定了 4 个属性,表示这些修改键的状态: shiftKey 、 ctrlKey 、 altKey 和 metaKey 。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为 true ,否则值为 false 。
IE9、Firefox、Safari、Chrome和 Opera 都支持这 4 个键。IE8 及之前版本不支持metaKey 属性
相关元素
这个属性只对mouseover和mouseout事件才包含值。
这个有意思,具体可以看看高程373页。