事件模型
事件模型有三种:
- 内联模型:与HTML代码紧密耦合在一起,没有实现相互分离,维护和更新代码困难
- 脚本模型:也称为DOM0级模型,是为了解决内联模型上述的问题
- DM2级模型:解决脚本模型不能给同一个元素添加多个事件,只会执行最后一个事件的问题
1 input.onclick = function(){ 2 alert("我是事件一"); 3 } 4 input.onclick = function(){ 5 alert("我是事件二"); 6 }
比如我给input标签添加了两个事件,但是最后只会弹出我是事件二,把事件一给覆盖了。
事件流:
事件流是从页面接收事件的顺序,当几个具有事件的元素叠加在一起的时候,点击其中一个元素,并不是只有当前元素被触发,而是层叠在你点击范围的所有事件都会被触发,包括两种事件流模式
- 事件冒泡:是从里往外逐个触发
- 事件捕获:是从外往里逐个触发
举个事件冒泡的例子:
<div id="box1"> <input type="button" value="我是input按钮" id="btn1"> 我是div盒子 </div> <script> var btn1 = document.getElementById("btn1"); var box1 = document.getElementById("box1"); btn1.onclick = function(){ alert("我是input按钮"); } box1.onclick = function(){ alert("我是div盒子"); } document.body.onclick = function(){ alert("我是body元素"); } document.documentElement.onclick = function(){ alert("我是html元素"); } document.onclick = function(){ alert("我是docuemnt元素"); }
执行的顺序为 input-->div-->body-->html-->document
取消冒泡的方法:
1 <body> 2 <div id="box1"> 3 <input type="button" value="我是input按钮" id="btn1"> 4 我是div盒子 5 </div> 6 <script> 7 var btn1 = document.getElementById("btn1"); 8 var box1 = document.getElementById("box1"); 9 btn1.onclick = function(event){ 10 alert("我是input按钮"); 11 var event = event||window.event; 12 //w3c取消冒泡的方式 13 //event.stopPropagation(); 14 //IE取消冒泡的方式 15 //event.cancelBubble=true; 16 //兼容写法 17 if(typeof event.cancelBubble =='undefined'){ 18 event.stopPropagation(); 19 }else{ 20 event.cancelBubble=true; 21 } 22 } 23 box1.onclick = function(){ 24 alert("我是div盒子"); 25 } 26 document.body.onclick = function(){ 27 alert("我是body元素"); 28 } 29 document.documentElement.onclick = function(){ 30 alert("我是html元素"); 31 } 32 document.onclick = function(){ 33 alert("我是docuemnt元素"); 34 } 35 </script> 36 </body>
这样当点击了input标签后,下面的元素绑定的事件不会被执行了,只会执行input绑定的事件。点击div元素,依然会冒泡,依次执行 div-->body-->html-->document
- var event = event || window.event为兼容的写法,因为IE下自定义了event对象,绑定在了window对象上,所以只能通过window.event来获取。
- W3C标准取消冒泡的方法为event.stopPropagation();
- IE取消冒泡的方式,event.cancelBubble=true;
对于单个元素的无法添加多个事件的解决方法是通过下面的方法来操作:
- 标准的DOM2级事件处理:addEventListenter() removeEventListenter();
包含三个参数:
- 第一次参数为事件名称,比如click,注意不能加上on
- 第二个参数为执行的函数,一般有三种形式
- 匿名函数
- 函数名,不能加上括号
- 带参数的函数需要使用以下形式
1 btn1.addEventListener('click',function(){ 2 test(3); 3 },false); 4 function test(num){ 5 alert(num); 6 }
- 第三个参数为false或者true,默认为false,表示事件流以冒泡的方式执行,true表示以捕获的方式执行
下面来实现给相同元素绑定多个事件
1 <input type="button" value="我是input按钮" id="btn1"> 2 <script> 3 var btn1 = document.getElementById("btn1"); 4 btn1.addEventListener('click',function(event){ 5 alert("我是事件一"); 6 }); 7 btn1.addEventListener('click',function(){ 8 alert("我是事件二"); 9 }); 10 btn1.addEventListener('click',function(){ 11 alert("我是事件三"); 12 }); 13 </script>
依次弹出我是事件一,二,三,
需要注意的是:
- 冒泡和捕获是对于不同的叠加元素绑定事件来设置的,通过addEventListener()来给不同元素绑定事件,第三个参数设置为true来变为捕获状态,对于单个元素 addEventListener()绑定多个事件,把第三个参数设为true并不能改变顺序,好像只能通过代码书写的顺序来改变。
下面实现绑定三个事件,但是我只需要执行第一个事件,删除其他两个事件的方法:
1 <body> 2 <input type="button" value="我是input按钮" id="btn1"> 3 <script> 4 var btn1 = document.getElementById("btn1"); 5 btn1.addEventListener('click',test); 6 btn1.addEventListener('click',test3); 7 btn1.addEventListener('click',test2); 8 function test(){ 9 alert("我是事件一"); 10 btn1.removeEventListener('click',test2); 11 btn1.removeEventListener('click',test3); 12 } 13 function test2(){ 14 alert("我是事件二"); 15 } 16 function test3(){ 17 alert("我是事件三"); 18 } 19 </script> 20 </body>
上面代码只会弹出“我是事件一”,事件二,三被移除了。
- removeEventListener()包含两个参数,一个参数为事件名,另一个为需要移除的函数名称
对于IE下的事件处理使用下面的方法:
- attachEvent() detachEvent();
- 包含两个参数,第一个参数为事件名称,注意这里需要加上on,比如onclick。第二个参数为执行的函数
1 <input type="button" value="我是input按钮" id="btn1"> 2 <script> 3 var btn1 = document.getElementById("btn1"); 4 btn1.attachEvent('onclick',test); 5 btn1.attachEvent('onclick',test3); 6 btn1.attachEvent('onclick',test2); 7 function test(){ 8 alert("我是事件一"); 9 } 10 function test2(){ 11 alert("我是事件二"); 12 } 13 function test3(){ 14 alert("我是事件三"); 15 } 16 </script>
注意:IE8之前出现的是相反的顺序,之后为正常的顺序。
如何解决事件流方法兼容性的问题:
1 var EventUtil = { 2 //检测绑定事件 3 //element为元素节点,type为事件类型,handler为执行函数, 4 addHanlder:function(element,type,handler){ 5 //如果element.addEventListener 他有的话执行DOM2级的方法 6 if(element.addEventListener){ 7 element.addEventListener(type,handler,false); 8 }else if(element.attachEvent){ 9 element.attachEvent('on'+type,handler); 10 }else{ 11 //否则使用DOM0级来处理 12 element['on'+type] = handler; 13 } 14 }, 15 //检测移除事件 16 removeHandler:function(element,type,handler){ 17 //检测是否能执行DOM0级方法 18 if(element.removeEventListener){ 19 element.removeEventListener(type,handler,false); 20 //检测是否能执行IE方法 21 }else if(element.detachEvent){ 22 element.detachEvent('on'+type,handler); 23 //如果都不行就执行DOM0级方法 24 }else{ 25 element['on'+type] = null; 26 } 27 } 28 };
最后来做个测试
1 <body> 2 <input type="button" id="btn1" value="按钮"> 3 <input type="button" id="btn2" value="按钮2"> 4 <script> 5 var EventUtil = { 6 addHanlder:function(element,type,handler){ 7 if(element.addEventListener){ 8 element.addEventListener(type,handler,false); 9 }else if(element.attachEvent){ 10 element.attachEvent('on'+type,handler); 11 }else{ 12 element['on'+type] = handler; 13 } 14 }, 15 removeHandler:function(element,type,handler){ 16 if(element.removeEventListener){ 17 element.removeEventListener(type,handler,false); 18 }else if(element.detachEvent){ 19 element.detachEvent('on'+type,handler); 20 }else{ 21 element['on'+type] = null; 22 } 23 } 24 }; 25 var btn1 = document.getElementById("btn1"); 26 var btn2 = document.getElementById("btn2"); 27 function test(){ 28 alert("我是一个事件"); 29 } 30 function test1(){ 31 alert("我是第二个事件"); 32 } 33 function remove(){ 34 EventUtil.removeHandler(btn1,'click',test1); 35 } 36 EventUtil.addHanlder(btn1,'click',test); 37 EventUtil.addHanlder(btn1,'click',test1); 38 EventUtil.addHanlder(btn2,'click',remove); 39 </script> 40 </body>