JS的事件模型
之前对事件模型还是比较清楚的,许多概念都清晰映射在脑海中。工作之后,一方面使用的局限性,二是习惯于用框架中的各种事件监听方式,简单即方便,久而久之,事件的一些概念开始淡出记忆中,就像我现在已经开始淡忘C语言的指针、麦克斯韦方程组、矩阵的变换、最小二乘法等。知识就像五彩缤纷的鹅卵石铺垫在你前行的道路上,从简单到深刻,从深刻到领悟,一直 助你渐行渐远。回头看看事件模型呗。
一、事件简介
说到事件,大家可以很快想到很多事件类型,比如:
鼠标事件
键盘事件
框架事件 onerror onresize onscroll等
表单事件事件 onblur onfocus等
剪贴板事件 oncopy oncut onpaste
打印事件 onafterprint onbeforeprint
拖动事件 ondrag ondragenter等
media事件 onplay onpause
动画事件 animationend
UI事件:load 、unload、error、select、resize、scroll
二、事件的三种模型
1、原始事件模型(DOM0级)
特点:原始事件模型中,事件发生后没有传播的概念,没有事件流。事件发生,立即处理。
监听函数只是元素的一个属性值,通过指定元素的属性值来绑定监听器。书写方式有两种:
HTML: <input id=”btn” onclick=”func()” />
js : document.getElementsById(‘btn’).onclick = func
优点:所有浏览器都兼容
缺点:
a、逻辑与显示没有分离;
b、相同事件的监听函数只能绑定一个,后绑定的会覆盖掉前面的。
c、无法通过事件的冒泡、委托等机制等。
在当前web程序模块化开发以及更加复杂的逻辑状况下,这种方式显然已经落伍了,所以在真
正项目中不推荐使用,平时写点demo倒是可以,速度比较快。
2、IE事件模型
特点:IE是将event对象在处理函数中设为window的属性,一旦函数执行结束,便被置为null
了。
IE的事件模型只有两步,先执行元素的监听函数,然后事件沿着父节点一直冒泡到document。
绑定解除监听函数的方法:
attachEvent( "eventType","handler"),其中evetType为事件的类型,如onclick,注意要加
’on’。
解除事件监听器的方法是 detachEvent("eventType","handler" );
缺点:就是只能IE自己用,太高冷了。
说明:
a、IE中不支持事件捕获,只有事件冒泡。
b、使用attachEvent()方法时候,事件处理程序会在全局作用域中运行,因此this等于window。这点在编写跨浏览器代码时候很重要。
c、添加多个事件的时候,触发的顺序是:后添加的先执行,这点和DOM方法不同。
d、最好不要添加匿名的函数作为执行的函数,这样事件无法被移除。DOM也要注意这个问题,因为:
var btn = document.getElementById('myBtn'); //添加 btn.attachEvent("onclcik",function(){ dosomthing(); }); //移除 !!无法移除,因为两个匿名函数压根不一样 所以把事件写成handler的形式 btn.detachEvent("onclcik",function(){ dosomthing(); });
3、 DOM2事件模型
在 W3C 2 级 DOM 事件中规范了事件模型,即 DOM2事件模型。现代浏览器(IE9以下不算)都遵
循了这个规范。
特点:
W3C制定的事件模型中,一次事件的发生包含三个过程:
a、事件捕获阶段。事件被从document一直向下传播到目标元素,在这过程中依次检查经过的节
点是否注册了该事件的监听函数,若有则执行。
b、事件处理阶段。事件到达目标元素,执行目标元素的事件处理函数.
c、事件冒泡阶段。事件从目标元素上升一直到达document,同样依次检查经过的节点是否注册
了该事件的监听函数,有则执行。
注意:所有的事件类型都会经历事件捕获阶段,但是只有部分事件会经历事件冒泡阶段,例如
submit事件就不会被冒泡。为了最大程度兼容各种浏览器,一般都是将事件处理函程序添加到事件流的冒泡阶段。
绑定解除监听函数的方法:
addEventListener("eventType","handler","true|false");其中eventType指事件类型,注意
不要加‘on’前缀,与IE下不同。
第二个参数是处理函数,
第三个即用来指定是否在捕获阶段进 true捕获阶段 false 只有冒泡阶段
监听器的解除也类似:removeEventListner("eventType","handler","true!false");
兼容IE和现代浏览器的事件注册监听写法
var a = document.getElementById('XXX'); if(a.attachEvent){ a.attachEvent('onclick',func); } else{//IE9以上和主流浏览器 a.addEventListener('click',func,false); }
现有的框架和类库都会对适应各种浏览器做兼容性的封装,JQuery底层即使用了上面的兼容性写法。
三、事件的捕获-冒泡机制
DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和
冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监听函数。
以如下HTML结构为例子,执行流程应该是这样的:
<div id="parent"> 父元素 <div id="child">子元素</div> </div>
运行一下一目了然。
var parent= document.getElementById('parent'); console.dir(parent); var child = document.getElementById('child'); parent.addEventListener('click',function(){alert('父亲在捕获阶段被点 击');},true);//第三个参数为true child.addEventListener('click',function(){alert('孩子被点击了');},false); parent.addEventListener('click',function(){alert('父亲在冒泡阶段被点击 了');},false);//第三个参数为false
可以看到,第三个即用来指定是否在捕获阶段进 true捕获阶段,false没有捕获阶段 。
如果不想让事件向上冒泡,可以在监听函数中调用event.stopPrapagation()来完成,后面会有应
用的栗子。
四、事件对象
触发在DOM上的某个事件时,会产生一个事件对象event,这个对象包含了与事件有关的信息。所有浏览器都支持事件对象,主要分为:
1、DOM中的事件对象
兼容DOM的浏览器会将一个事件对象传到事件处理程序中。event对象包含了与创建它的特定事件有关的属性和方法,一般都不太一样,但也有些相同的成员。比如
bubbles:表明事件是否冒泡
currentTarget:事件处理程序当前正在处理的事件的那个元素
target:事件的目标,与currentTarget还不太一样。
preventDefault() :取消事件的默认行为,比如点击a标签默认跳转链接,点击button默认提交
stopPropagation():取消事件进一步捕获或者冒泡,经常会用到
注意:
只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完毕,event对象就会被销毁。
2、IE中的事件对象
与访问DOM中的event对象不同,访问IE中的event对象有几种不同的方式。
在DOM0级方法中,envet对象作为Window对象的一个属性存在。
注意:
a、在IE中,window.event.returnValue = false;相当于DO中的preventDefault();
b、stopPropagation()方法是一样的。
c、IE中的event.srcElement相当于DOM中的event.target
五、事件委托机制
委托就是把事件监听函数绑定到父元素上,让它的父辈来完成事件的监听,这样就把事情“委托
”了过去。在父辈元素的监听函数中,可通过event.target属性拿到触发事件的原始元素,然后
再对其进行相关处理。
六、jQuery中的事件监听方式
jQuery中提供了四种事件监听方式,分别是bind、live、delegate、on,对应的解除监听的
函数分别是unbind、die、undelegate、off。这几个方法已经对各种浏览器的兼容性进行封装。
具体方法可以查看手册。
注意几点:
jQuery推荐事件的绑定都使使用on方法
jQuery默认事件不在捕获中进行
七、什么是自定义事件
张鑫旭的《js-dom自定义事件》
八、一个简单例子
点击弹窗之外任何地方,弹框关闭。
方法:给body绑定事件,在事件的执行函数里关闭弹框;
给弹框元素绑定点击事件,在事件的执行函数里面组织事件冒泡,即:
event.stopPrapagation();