一直以来,在DOM中添加事件总是使用三种方法,
第一种使用HTML中的事件处理程序例如:
<button onclick=' $$ '></button>
第二种:使用在JavaScript中添加事件处理程序:
var s =document.getElementById('id'); s.oncilck=function(){}
第三种使用 addEventListener 方法注册事件处理程序:
document.getElementById('id').addEventListener('click',function(){})//注意是click
一直是这样用的,却没有理解使用的弊端和注意的问题,现在总结一下这三者DOM事件处理方法的优劣。
一、事件流
事件流有两种,事件冒泡和事件捕获,它表示当用户触发了某个页面元素的动作,应该由怎样的方式向“观察者”传递;就像是身为校长的你,学生发生了一起斗殴事件,你是通过挨个年级-班级去找到滋事者(事件捕获);还是通过一个学生主动告诉你事情发生的经过(事件冒泡);某种程度上,中国写信的地址方式和美国的写信的地址,有些类似冒泡和捕获的过程。
1、事件冒泡
假如一篇文档的结构:
<html> <head> </head> <body> <div></div> </body> </html>
当点击div元素 ,触发了click事件,那么冒泡传递的方式是这样的,
2、事件捕获
自然是反方向的:
3、DOM事件流
很多面试会有问到这个问题,实际上DOM事件流是DOM2级事件中规定的,它包括,事件捕获阶段,目标发生阶段,和事件冒泡阶段。
值得注意的是,div元素不会在捕获阶段接受事件,也就是捕获的过程到body后就停止了,接下来是目标阶段;在事件处理中属于冒泡阶段的一部分,
DOM2中规定捕获阶段不会设计目标事件,但是高版本浏览器没有遵循规定;因此就有两个机会在目标对象中操作事件。
二、事件处理程序
事件的名称 click ;load;mouseover;事件处理程序以‘on’开头;因此click事件的事件处理程序就是onclick;load事件的事件处理程序就是onload;
1、HTML事件处理程序
HTML事件处理程序可以直接在事件处理程序中,添加执行的函数,也可以进行调用其他地方定义的脚本;
<button oncilck="alert('hello world!')"></button>//执行代码直接添加到事件中
<script> function hello(){ alert('hello,world!') } </script> <button onclick='hello()'></button>//调用其他脚本
HTML事件处理程序优点
- 会创建一个封装着元素属性值的函数,这个函数中有一个局部变量event,也就是事件对象。
<input type='button' value='click me' onclick='alert(this.value)'>//click me this值指向发生事件的目标元素; <input type='button' value='click' onclick='alert(event.type)'>//click 通过event对象可以访问事件对象;
HTML事件处理程序缺点
- 失去响应。如果定义的脚本在触发事件元素的后面进行定义的,很可能在没有加载脚本之前;用户点击了元素,会失去响应;(JavaScript引擎是逐行读取代码的)
- HTML与JavaScript高度耦合,缺乏可维护性和增加维护成本。
2、DOM 0 级事件处理程序
JavaScript中常见的处理事件的方式,通过将函数赋值给一个事件处理程序属性;简单,而且跨浏览器;
var but=document.getElementById('id'); but.onclick=function(){ alert(this.value) }
这种方式的事件 处理程序在事件流的冒泡阶段被处理。
but.onclick=null;//阻止事件处理的方式,设置null;
3、DOM 2级事件处理程序
通过addEventListener() removeEventListener(),方法注册和删除事件处理程序,接受3个参数,事件名,处理函数,布尔值;
var but=document.getElementById('id'); but.addEventListener('click',function(){ alert('this.value'); },false);
布尔值true表示在事件流捕获阶段触发调用,布尔值false 表示在冒泡阶段调用事件处理程序。
注意:
- 第二个参数是事件名,不是事件处理函数名, ‘click’而不是‘onclick’,与前两个方式不同。
- 可以添加多个事件处理程序;处理的顺序按照注册的顺序执行。
var but=document.getElementById('id');
but.addEventListener('click',function(){ alert('this.value'); },false);
but.addEventListener('click',function(){ alert('hello world'); },false);
- 匿名函数无法通过removeEventListener()方法移除事件处理程序,因为两次的匿名函数不是同一个函数,
var but=document.getElementById('id'); but.addEventListener('click',function(){ alert('hello world'); },false); but.removeEventListener('click',function(){ alert('hello world'); },false);//无效,因为第二个参数是完全不同的匿名函数,只是定义相同;
var but=document.getElementById('id'); function hello(){ alert('hello world') } but.addEventListener('click',hello,false); but.removeEventListener('click',hello,false)//有效,使用相同函数
4、IE事件处理程序
IE使用attachEvent() detachEvent()两个方法添加事件处理,这两个方法分别接受两个参数,事件处理名称,事件处理函数;由于IE8更早版本只支持事件冒泡,因此通过attachEvent() 添加的时间处理程序会被添加到冒泡阶段。
注意
- attachEvent() 第一个参数是“onclick” 而不是“click”;
- attachEvent() 也可以添加多个事件,不过执行的顺序完全相反;
- 可以通过 detachEvent() 方法移除事件,和removeEventListener() 类似;不能移除匿名函数。
var but=document.getElementById('id'); function hello(){ alert('hello world') } but.attachEvent('onclick',hello); but.attachEvent('onclick',function(){ alert('this.id') })
//先 弹出id ,后弹出 hello world