1.事件流
事件流是描述页面接收事件的顺序(指的是事件的传递,不是事件处理程序)
先有三种;事件冒泡、事件捕获、dom2级事件流
事件冒泡,
事件捕获,
dom2级事件流,规定的事件流包括三个阶段
:事件捕获阶段
、处于目标阶段
和冒泡阶段
。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应
说明:并不是所有的事件都可以冒泡传递的,如blur,focus等。参考,http://segmentfault.com/q/1010000000687977
2、浏览器实现情况
ie8及以前只支持冒泡模型
现代浏览器由于实现了DOM2,都可以支持DOM2事件(具体兼容性在caniuse查看)
3、事件和事件处理程序的概念
事件:浏览器行为或用户操作
事件处理程序,事件触发时的回调函数。
4、添加事件处理程序的几种方法
1)html事件处理程序
通过html特性添加事件处理程序。
缺点,html与js没有分离,不利于维护。
2)dom0级事件处理程序
通过domElement.onclick这样的事件属性添加事件处理程序
缺点,每个元素只能添加一个事件处理函数
3)通过dom2级提供的API监听事件。
通过DOM提供的API绑定事件处理程序。
需要知道两个方法:
addEventListener()----给DOM元素添加事件监听
removeEventListener()----从DOM元素上移除事件监听
3个参数:事件名(click)、事件处理程序函数、useCaputer(布尔值)--true,表示在事件捕获阶段触发事件处理程序。
--false,表示在事件冒泡阶段触发事件处理程序。(默认)
说明;一般把事件处理程序添加到事件流的冒泡阶段以兼容各浏览器,如果想在事件到达目标之前截获它则将事件处理程序添加到捕获阶段。
优点,可以为每个元素添加多个事件处理函数
4)ie的事件处理程序
需要知道在ie上的两个方法,attachEvent()和detachEvent()
2个参数,事件处理程序名称(onclick)和事件处理函数
与dom标准的区别是:事件处理函数的作用域不同,dom标准的作用域是绑定事件的dom元素,而ie的事件处理函数的作用域是全局作用域windo
为了在函数中使用this,并使其正确的指向绑定事件处理函数的DOM元素,所以我们可以使用apply或者call方法来改变函数的作用域。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>跨浏览器事件监听封装</title> </head> <body> <p id="demo"> hello world </p> <script> function bind(ele,type,fn,useCapture){ if(window.addEventListener){ ele.addEventListener(type, function () { fn.apply(ele,arguments); },useCapture); }else if(window.attachEvent){ ele.attachEvent('on'+type, function () { fn.apply(ele,arguments); }); } } var ele=document.getElementById('demo'); var test= function () { alert(this.nodeName); }; bind(ele,'click',test,false); </script> </body> </html>
5)如何跨浏览器添加事件处理程序。
1)可以使用隔离浏览器差异的js类库
2)也可借助能力监测编写自己的跨浏览事件处理的方法,如下
var EventUtil={ addHander: function(element,type,handler) { if(element.addEventListener()){ element.addEventListener(type,handler,false); }else if(element.attachEvent('on'+type,handler)){ 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.detach('on'+type,handler) }else{ element['on'+type]=null; } } }
5.事件对象event(作用域,局部变量)
需要区分ie和非ie的不同,
1)非ie浏览器中(event作为形参传递给事件处理函数)
DOM中event对象中几个重要的属性方法
event.currentTarget--绑定事件处理函数的DOM元素。
event.target-----触发事件的dom元素。
event.stopImmediatePropagation
event.stoppropagation-----阻止事件的传递
event.preventDefault()---阻止浏览器默认行为
2)ie浏览器中
根据绑定事件的方式不同,获取event对象的方式有些差异。
a,通过DOM0级方法绑定时,event通过window.event获取
b,通过DOM2级方法绑定事件的话,通过event参数传递(当然也可以通过window.event)
event对象的几个重要属性
event.returnValue=false;----类比event.preventDefault()
event.srcElement;----类比event.target
event.cancelBubble=true---阻止冒泡。
6.事件类型
种类比较多,可以是用户行为和浏览器行为(onload)
7.应用场景及常见问题
阻止冒泡event.stoppropagation()/event.cancelBubble=true;
8.一些实验测试
1).一段程序说明:DOM2的事件会沿着捕获---目标----冒泡的顺序进行传递
DOM0上的事件为冒泡模型
var btn=document.getElementById('btn'); btn.onclick= function(event) { alert(event.eventPhase); }; document.body.addEventListener("click",function(event){ alert(event.eventPhase); },true); document.body.onclick= function(event) { alert(event.eventPhase); };
2).ie中根据事件添加的方式不同,this的指向不同
btn.onclick= function() { alert(window.event.srcElement==this); }; btn.attachEvent("onclick",function(event){ alert(event.srcElement==this); alert(window==this);//attachEvent中this为全局变量window })
3).真的理解了DOM2级事件流概念?
<div id="s1"> s1 <div id="s2">s2 <div id="s3">s3</div> </div> </div> document.getElementById("s1").addEventListener("click", function() { console.log('s1'); },true); document.getElementById('s2').addEventListener("click", function() { console.log("s2"); },true);
在s1上点击时,运行结果;
s1
这里我当时还在疑惑为什么事件没有传递到s2.
在做了好几种测试后依然不明,后来重新看概念。发现了问题所在DOM2(标准)事件流分三个过程,从document->html->body为捕获阶段。s1为目标阶段。从 body->html->document(为冒泡段)。很明显根据概念跟s2完全没关系,当时糊涂了。
在s2上点击时,运行结果为:
s1
s2
4).在DOM2事件绑定中,如果在目标元素同时绑定了捕获和冒泡两个事件。那么点击目标元素时,两次事件触发的顺序按照程序中捕获和冒泡出现的先后顺序执行。
s1.addEventListener('click',function(event){
console.log('s1捕获');
},true);
s2.addEventListener('click',function(event){
console.log('s2冒泡 ');
},false);
s2.addEventListener('click',function(event){
console.log('s2捕获 ');
},true);//可以对调s2的两个位置看结果输出
5)stopImmediatePropagatiin()
stoppropagation()仅仅是阻止事件的传递
而stopImmediatePropagation()同时组织绑定在该元素上的其他事件处理程序的执行
s1.addEventListener('click',function(event){ console.log('s1捕获'); event.stopPropagation(); },true); s1.addEventListener('click',function(event){ console.log("somethings"); },true); s2.addEventListener('click',function(event){ console.log('s2捕获 '); },true);
输出结果表明,当前事件传递到的元素上的其他事件处理程序还是会执行的。换做stopImmediatePropagation()试试
s1.addEventListener('click',function(event){ console.log('s1捕获'); // event.stopPropagation(); event.stopImmediatePropagation(); },true); s1.addEventListener('click',function(event){ console.log("somethings"); },true); s2.addEventListener('click',function(event){ console.log('s2捕获 '); },true);
结果表明,绑定在该元素上的剩余事件处理函数均不会再执行。
9.JavaScript的事件运行机制
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
http://chen498402552-163-com.iteye.com/blog/1997633