事件||事件流||通用事件侦听器函数
1、跨浏览器事件对象:
事件流:描述的是从页面中接收事件的顺序。
IE:的事件流是事件冒泡流。
事件冒泡(event bubbling):事件由最具体的元素(嵌套最深的那个节点)接收,然后逐级向上传播到不具体的节点(文档)。明显的缺点就是在多个子元素上绑定事件,父元素节点会接受到所有的事件绑定,这是个很坑爹的体验。
Netscape Communicator:的事件流是事件捕获流。
事件捕获(event capuring):是不太具体的元素节点应该更早的接收到事件,而更具体的节点应该最后接收到事件。符合常用的模型和思考方式。
Dom事件流:“Dom2级事件”规定了事件流包括三个阶段。(事件代理处理)
事件捕获阶段、处于事件阶段和事件冒泡阶段。即事件捕获为截获事件提供了机会,接着实际的目标接收到事件,最后冒泡阶段,在这个阶段对事件做出响应。
实际上这里已经显示了捕获要在冒泡之前。
大多的时候会把事件流添加到冒泡阶段,这样可以最大限度的兼容各种浏览器。此时DOM2级事件设为false。
这里的捕获阶段不涉及事件目标。这个时候的事件冒泡或者是事件捕获使用,添加事件最后的限定true(捕获阶段)||false(冒泡阶段)来触发。如果不写是根据浏览器的特性来实现的。
如下来验证:一个DOM元素绑定两个事件,一个冒泡,一个捕获,则事件会执行多少次,执行顺序如何。先捕获后冒泡。
//如下事件冒泡阶段触发 <div id='one'>1 <div id='two'>2 <div id='three'>3 <div id='four'>4 </div> </div> </div> </div> <script type='text/javascript'> var one=document.getElementById('one'); var two=document.getElementById('two'); var three=document.getElementById('three'); var four=document.getElementById('four'); one.addEventListener('click',function(){ alert('one'); },false);//表示在冒泡阶段触发事件 two.addEventListener('click',function(){ alert('two'); },false); three.addEventListener('click',function(){ alert('three'); },false); four.addEventListener('click',function(){ alert('four'); },false); </script> //点击4,依次会触发four、three、two、one。
<div id='one'>1 <div id='two'>2 <div id='three'>3 <div id='four'>4 </div> </div> </div> </div> <script type='text/javascript'> var one=document.getElementById('one'); var two=document.getElementById('two'); var three=document.getElementById('three'); var four=document.getElementById('four'); one.addEventListener('click',function(){ alert('one'); },true);//表示在捕获阶段触发事件 two.addEventListener('click',function(){ alert('two'); },true); three.addEventListener('click',function(){ alert('three'); },true); four.addEventListener('click',function(){ alert('four'); },true); </script>
//点击4则依次触发:one、two、three、four
<div id='one'>1 <div id='two'>2 <div id='three'>3 <div id='four'>4 </div> </div> </div> </div> <script type='text/javascript'> var one=document.getElementById('one'); var two=document.getElementById('two'); var three=document.getElementById('three'); var four=document.getElementById('four'); one.addEventListener('click',function(){ alert('one'); },true); two.addEventListener('click',function(){ alert('two,bubble'); },false);//这里的事件在冒泡阶段触发 two.addEventListener('click',function(){ alert('two,capture'); },true);//这里的事件在捕获阶段触发 three.addEventListener('click',function(){ alert('three,bubble'); },true); four.addEventListener('click',function(){ alert('four'); },true); </script> <!-- 执行顺序最终结果是:(chrome先捕获后冒泡)点击4,依次会触发 one、two,capture、three,bubble、four、two,bubble-->
<!-- 说明,同一个元素上绑定事件是先捕获,再冒泡。中间还有一个处于目标阶段 -->
2、事件处理程序(事件侦听器):
事件是用户或浏览器自身执行的某种动作:如click、load、mouseover等,响应某个事件的函数称为事件处理程序(事件监听器)。
1、html事件处理程序
事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码(匿名函数的作用域-全局)。
直接写在页面元素中,强耦合,摒弃。页面在前解析,js在后解析。
存在时差问题,可能在处理程序解析之前就触发了事件,会导致错误。
MouseEvent对象如下:
<input type="button" value = "click me" onclick = "console.log(event)"/>
这里的path保存着事件冒泡的顺序(具体到不具体),target保存着目标节点的信息,view保存着window对象的一些信息。
2、Dom0级事件处理程序(onclick):
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种方法被称为事件处理程序赋值,出现在第四代 Web 浏览器中。
每个元素(包括 window 和 document)都有自己的事件处理程序属性,属性通常全部小写,如 onclick。将这种属性的值设置为一个函数,就可以指定事件处理程序。
<input type="button" value = "Dom0" id = "myBtn" /> <script type="text/javascript"> var btn = document.getElementById("myBtn"); btn.onclick = function(event){ console.log(event); } </script>
打印结果与上边无差。处理函数内部this指向元素节点。可以取到this.id。
删除事件,将事件处理函数置为null即可。
btn.onclick = null;
3、Dom2级事件处理程序(事件绑定与取消绑定):
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener() ;
它们接受3个参数:
-
要处理的事件名
-
作为事件处理程序的函数
-
一个布尔值:
true: 捕获阶段调用事件处理程序
false: 冒泡阶段调用
<input type="button" value = "Dom2" id = "myBtn2" /> <script type="text/javascript"> var btn = document.getElementById("myBtn2"); btn.addEventListener("click",function(event){ alert("Dom2"); console.log(event); },false); </script>
事件event与之前的无差。
可以添加多个事件,添加多个事件的时候,按添加的顺序依次执行。
移除事件要一一对应,注意由于匿名函数在内存中没有指向对象,所以添加之后无法移除,所以做好的做法就是先定义好声明的处理函数(或者函数表达式),在进行绑定和删除。
大部分时候都是把事件添加到冒泡(false)阶段,这样可以最大限度的兼容各种浏览器。
4、IE事件处理程序:
IE 实现了与 DOM 中类似的两个方法:attachEvent()和detachEvent()
接受两个参数:事件处理程序名称、事件处理程序函数
通过这种方法添加的事件处理程序会被添加到冒泡阶段。
<input type="button" value = "IE" id = "myBtn3" /> <script type="text/javascript"> var btn = document.getElementById("myBtn3"); btn.attachEvent("onclick",function(event){ alert("Clicked"); console.log(event); console.log(this) }); </script>
打印的结果不能展开:
在IE中使用 attachEvent() 与使用 DOM0 级方法的主要区别在于事件处理程序的作用域。
DOM0 级方法:在Dom0级事件当中,事件处理程序会在其所属的元素的作用于内运行。
attachEvent()方法:使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行。此时this 等于window。
当然,匿名函数不能被移除。
5、事件对象:
触发Dom上的某个事件的时候,会产生一个事件对象event,这个对象包含着与事件相关的信息,包含导致事件的元素、事件的类型及其他信息。
event对象包含着与创建它的特定事件有关的属性和方法。
Dom中的事件对象:
事件对象的一些属性和方法:
主要的有:
cancelable:表明是否可以取消事件默认行为。
currentTarget:时间处理程序当前正在处理的那个元素。处理程序内部,对象this始终等于currentTarget的值。而target只包含事件的实际目标。
preventDefault():取消事件的默认行为。
target:事件目标。
stopPropagation(): 取消事件进一步的捕获或者冒泡。
只有在执行事件期间,event才存在,事件执行完成之后,event对象就会被销毁。
IE事件对象中有区别的地方:
3、通用事件监听函数
//写一个通用的事件侦听器函数 //跨浏览器事件对象 var EventUtil = { //页面加载完成之后,页面完全加载之后执行。 readyEvent:function(fn){ if(document.addEventListener){ //标准浏览器 document.addEventListener('DOMContentLoaded',function(){ //注销事件,避免反复触发 document.removeEventListener('DOMContentLoaded',arguments.callee,false); //执行函数 fn(); },false); }else if(document.attachEvent){ //IE浏览器 document.attachEvent('onreadystatechange',function(){ if(document.readyState=='complete'){ document.detachEvent('onreadystatechange',arguments.callee); //执行函数 fn(); } }); } }, /* *attachEvent可以使用匿名函数,但这样之后,detachEvent将无法卸载之。 *detachEvent所卸载的函数必须使用函数名。 */ //添加事件 addEvent:function(element,type,handler){ if(element.addEventListener){//非IE,DOM2 //事件类型、需要执行的函数、是否捕获 element.addEventListener(type,handler,false); }else if(element.attachEvent){//IE element.attachEvent('on'+type,handler); }else{ element['on'+type] = handler;//DOM0 } }, //移除事件 removeEvent: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; } }, //阻止事件冒泡(主要是事件冒泡,因为IE不支持事件捕获),用于立即停止事件在Dom层中的传播,即进一步取消事件的捕获或者冒泡。 stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation();//W3C标准 }else{ event.canceBubble = true;//IE } }, //取消事件的默认行为, 如url的跳转等 preventDefault:function(event){ if(event.preventDefault){ event.preventDefault();//W3C标准 }else{ event.returnValue = false;//IE } }, //获取事件目标 getTarget:function(event){ return event.target||event.srcElement; }, //获取event对象的引用,获取事件的所有信息,确保随时能使用event; getEvent:function(event){ var event = event||window.event;//在Dom0级事件添加,event作为window对象的一个属性存在。 } };
// 定义一个函数处理多种类型的事件 var handler = function(event){ switch(event.type){ case "click": alert("Clicked"); break; case "mouseover": event.target.style.backgroundColor = "red"; break; case "mouseout": event.target.style.backgroundColor = ""; break; default: alert("test switch"); } };
//阻止事件冒泡的实例 var btnCancel = document.getElementById("btnCancel"); btnCancel.onclick = function(event){ alert("clicked222"); //这样之后,body上的事件就不会触发了,不然点一下,就会触发这两个事件。 EventUtil.stopPropagation(event); }; document.body.onclick = function(event){ //如果btnCancle上阻止了事件冒泡,那么body上的事件就不会被添加到btnCancle上。 alert("body clicked") }
获取一组元素的序号,原理是自己先要获取元素然后添加属性到标签中。点击事件的时候再获取自定义的属性。 两种方法,标签添加属性和使用自定义属性。 另:Dom0型事件的处理函数的this 等于 event.target属性。 <ul > <li class="a1">1</li> <li class="a2">2</li> <li class="a3">3</li> <li class="a4">4</li> <li class="a5">5</li> <li class="a6">6</li> </ul> var list = document.getElementsByTagName("li"); for(var i = 0,len = list.length;i<len;i++){ list[i].index = i;//方法1,比较好 list[i].setAttribute("data-index",i);//方法2 list[i].onclick = function(event){ console.log(this.getAttribute("data-index")); console.log(this.index); console.log(event); console.log(this==event.target); } } //当然事件也是可以绑定到父元素上 var ul = document.getElementsByTagName("ul"); ul[0].onclick = function(event){ console.log(event.target.index); }
IE事件与FF(DOM)事件的区别:
1、window.event
表示当前的事件对象,IE的事件对象是window对象的一个属性。 FF:没有window.event对象。可以通过给函数的参数传递event对象。如onmousemove=domousemove(event)。
2、获取事件源
IE:srcElement;FF:target;
3、添加删除事件方法不同
IE:element.attachEvent("onclick",function(){})
FF: element.addEventListener("click",function(){},false);
删除事件也是不同的。
4、自定义DOM事件
自定义DOM事件不是由DOM原生触发的,目的是让开发人员创建自己的事件。创建自定义事件,需要调用createEvent("CustomEvent");
且看如下实例:
var div = document.getElementById("myDiv"); var event; EventUtil.addHandler(div,"myevent",function(event){ alert("DIV: " + event.detail); }); EventUtil.addHandler(document,"myevent",function(event){ alert("DOCUMENT: " + event.detail); }); if(document.implementation.hasFeature("CustomEvents","3.0")){ event = document.createEvent("CustomEvent"); event.initCustomEvent("myevent",true,false,"hello world!");//对应参数:触发事件类型、事件是否冒泡、事件是否可以取消、任意值,保留在event对象的detail中 div.dispatchEvent(event); }
事件代理:
事件代理,具体实例,上边的ul元素绑定index,可以用。