深入理解DOM事件机制系列第二篇——事件处理程序
前面的话
事件处理程序又叫事件侦听器,实际上就是事件的绑定函数。事件发生时会执行函数中相应代码。事件处理程序有HTML事件处理程序、DOM0级事件处理程序、DOM2级事件处理程序和IE事件处理程序四类,下面将详细介绍该部分内容
HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的javascript代码
在事件处理程序函数内部,this值等于事件的目标元素
<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= '1';"></div>
在HTML中定义的事件处理程序也可以调用在页面其他地方定义的脚本
<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "test()"></div>
<script>
function test(){box.innerHTML+= '1';}
</script>
HTML事件处理程序会创建一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象。通过event变量,可以直接访问事件对象,不用自己定义它,也不用从函数的参数列表中获取
<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= event.type;"></div>
在事件处理程序函数内部,可以像访问局部变量一样访问document及该元素本身的成员。如此一来,事件处理程序要访问自己的属性就简单多了
<button id="box" value="test" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= value;"></button>
【扩展】
下列这种情况输出的是空字符串'',如果与预想结果不一致,请移步至此
<script>
var value=123;
</script>
<button style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= value;"></button>
缺点
【1】时差问题
因为用户可能会有HTML元素一出现在页面上时就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件,就会报错
<button style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= val;"></button>
<script src="http://www.qq.com/test.js"></script>
<script>
var val=123;
</script>
【2】耦合问题
客户端编程的通用风格是保持HTML内容和javaScript行为分离,所以应该避免使用HTML事件处理程序属性,因为这些属性直接混合了javascript和HTML,且不易扩展
DOM0级事件处理程序
通过javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,而且至今仍然为所有现代浏览器所支持。原因一是简单,二是具有跨浏览器的优势
每个元素都有自己的事件处理程序属性,这些属性通常全部小写,将这种属性的值设置为一个函数,就可以指定事件处理程序
[注意]以DOM0级方式添加的事件处理程序会在事件流的冒泡阶段被处理
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.onclick = function(){this.innerHTML += '1';}
</script>
可以通过将事件处理程序属性设置为null来删除事件处理程序
box.onclick = null;
缺点
DOM0级事件处理程序的缺点是围绕着每个事件目标对于每种事件类型只能添加一个事件处理程序
DOM2级事件处理程序
DOM2级事件处理程序定义了两个方法用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()
所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后的布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。若最后的布尔值不填写,则和false效果一样
[注意]IE8-浏览器不支持DOM2级事件处理程序
使用DOM2级事件处理程序的好处是可以添加多个事件处理程序,并按照他们添加的顺序触发
以下代码以1-2的顺序输出
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener('click',function(){this.innerHTML += '1'},false);
box.addEventListener('click',function(){this.innerHTML += '2'},false);
</script>
以下代码以2-1的顺序输出
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
setTimeout(function(){
box.addEventListener('click',function(){this.innerHTML += '1'},false);
},16);
box.addEventListener('click',function(){this.innerHTML += '2'},false);
</script>
参数
如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener("click",function(){
test('123');
},false);
function test(x){box.innerHTML += x;}
</script>
移除
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除,移除时传入的参数与添加处理程序时使用的参数相同。这意味着,addEventListener()添加的匿名函数将无法移除
以下无效
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener("click",function(){
this.innerHTML += '1'
},false);
box.removeEventListener('click',function(){
this.innerHTML += '1'
},false);
</script>
以下有效
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
var handle = function(){this.innerHTML += '1'};
box.addEventListener("click",handle,false);
box.removeEventListener('click',handle,false);
</script>
IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8-浏览器只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到事件冒泡阶段
attachEvent()方法的第一个参数是"onclick",而非DOM的addEventListener()方法中的"click"
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){this.innerHTML += '1';});
</script>
[注意]attachEvent()方法只冒泡到document,且IE10-浏览器支持
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<button id="reset">还原</button>
<script>
//IE10-浏览器返回div body html document
//其他浏览器报错
reset.onclick = function(){history.go();}
box.attachEvent('onclick',function(){box.innerHTML += 'div\n';});
document.body.attachEvent('onclick',function(){box.innerHTML += 'body\n';});
document.documentElement.attachEvent('onclick',function(){box.innerHTML += 'html\n';});
document.attachEvent('onclick',function(){box.innerHTML += 'document\n';});
window.attachEvent('onclick',function(){box.innerHTML += 'window\n';});
</script>
this
与其他三个事件处理程序不同,IE事件处理程序的this指向window,而非被绑定事件的元素
<!-- <div> -->
<div id="box" style="height:100px;width:300px;background-color:pink;"
onclick = "console.log(this)"></div>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.onclick= function(){
console.log(this);//<div>
}
</script>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.addEventListener('click',function(){
console.log(this);//<div>
});
</script>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){
console.log(this);//window
});
</script>
顺序
使用attachEvent()方法添加的事件处理程序的触发顺序是有区别的。IE9、10浏览器是按正序执行的,而IE8-浏览器则是按倒序执行的
<div id="box" style="height:30px;width:100px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){
box.innerHTML += '1';
});
box.attachEvent('onclick',function(){
box.innerHTML += '2';
});
</script>
移除
使用attachEvent()添加的事件可以通过detachEvent()来移除,条件是必须提供相同的参数。与DOM方法一样,这也意味着添加的匿名函数将不能被移除。不过,只要能够将对相同函数的引用传给detachEvent(),就可以移除相应的事件处理程序
以下无效
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.attachEvent("onclick",function(){
box.innerHTML += '1'
},false);
box.detachEvent('onclick',function(){
box.innerHTML += '1'
},false);
</script>
以下有效
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
var handle = function(){box.innerHTML += '1'};
box.attachEvent("onclick",handle,false);
box.detachEvent('onclick',handle,false);
</script>
总结
由于IE8-浏览器不支持addEventListener()方法,所以需要配合attachEvent()方法来实现全浏览器的事件绑定兼容写法。同时,由于attachEvent()方法中的this指向window,所以需要对this进行显式修改
function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent('on'+type,function(event){
            return handler.call(target,event);
        });
    }
}
调用顺序
如果浏览器同时出现这四种事件处理程序,那么它们的调用顺序在各浏览器中表现并不一致
<div id="box" style="height:100px;width:100px;background:pink;" onclick = "this.innerHTML +='html\n'"></div>
<script>
if(box.addEventListener){
box.addEventListener('click',function(){this.innerHTML += 'DOM2级\n'})
}
if(box.attachEvent){
box.attachEvent('onclick',function(){box.innerHTML +='IE\n'})
}
box.onclick = function(){
this.innerHTML += 'DOM0级\n';
}
</script>
【相同点】
如果同时出现HTML事件处理程序和DOM0级事件处理程序,DOM0级会覆盖HTML事件处理程序
【不同点】
chrome/opera/safari等webkit内核的浏览器会按照事件处理程序出现的顺序来排列,所以结果为:DOM2级 DOM0级
firefox浏览器和IE浏览器会将DOM0级事件优先调用
所以firefox和IE11浏览器结果为:DOM0级 DOM2级
IE9、10浏览器结果为:DOM0级 DOM2级 IE
IE8-浏览器结果为:DOM0级 IE
深入理解DOM事件机制系列第二篇——事件处理程序的更多相关文章
- 深入理解DOM事件机制系列第一篇——事件流
		× 目录 [1]历史 [2]事件冒泡 [3]事件捕获[4]事件流 前面的话 javascript操作CSS称为脚本化CSS,而javascript与HTML的交互是通过事件实现的.事件就是文档或浏览器 ... 
- 深入理解DOM事件类型系列第二篇——键盘事件
		× 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ... 
- 深入理解this机制系列第二篇——this绑定优先级
		前面的话 上一篇介绍过this的绑定规则,那如果在函数的调用位置上同时存在两种以上的绑定规则应该怎么办呢?本文将介绍this绑定的优先级 显式绑定 pk 隐式绑定 显式绑定胜出 function fo ... 
- 深入理解脚本化CSS系列第二篇——查询计算样式
		× 目录 [1]getComputedStyle [2]注意事项 [3]currentStyle[4]IE 前面的话 元素的渲染结果是多个CSS样式博弈后的最终结果,这也是CSS中的C(cascade ... 
- 深入理解javascript函数进阶系列第二篇——函数柯里化
		前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ... 
- 深入理解javascript选择器API系列第二篇——getElementsByClassName
		× 目录 [1]使用 [2]classList [3]扩展 前面的话 既然有getElementById()和getElementsByTagName()方法,为什么没有getElementsByCl ... 
- 深入理解javascript函数系列第二篇——函数参数
		× 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ... 
- 深入理解javascript作用域系列第二篇——词法作用域和动态作用域
		× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ... 
- 深入理解javascript作用域系列第二篇
		前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ... 
随机推荐
- angluar去掉url中#
			众所周知,angular项目中路由机制会在地址栏加一个#来实现各个页面的切换,虽然url中有个#号也无伤大雅,但每次看到多一个这个东西总是不舒服(我不是强迫证啊),趁着项目间隙还是决定把它去掉. 去谷 ... 
- (原).NET程序加入多语言包解决方案工具,超级棒
			Multi-Language Add-In Version 5.04.0088 for Visual Studio 2013 安装包:http://www.jollans.com/SetupMulti ... 
- Daily Scrum02 12.15
			今天会议主要还是大家汇报进度与任务.由于团队中有两位成员在周一会有其他事情处理,暂不分配任务,因而这些事情要交给其他成员处理…… 由于要反复修改,查看效果,所以要花费很长的时间,但大家最近都很忙,我们 ... 
- System.Web.HttpContext.Current.Session为NULL解决方法
			http://www.cnblogs.com/tianguook/archive/2010/09/27/1836988.html 自定义 HTTP 处理程序,从IHttpHandler继承,在写Sys ... 
- SVN冲突
			svn冲突,导致工程打不开,报错:xxx..xcodeproj cannot be opened because the project file cannot be parsed. 解决方法: ... 
- Java获得键盘输入的两种方法
			import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ... 
- 关于HTML是什么,能做什么
			HTML(Hyper Text Mark-up Language )即超文本标记语言,是 WWW 的描述语言,由 Tim Berners-lee提出.设计 HTML 语言的目的是为了能把存放在一台电脑 ... 
- 推荐升级ASP.NET Web API 2
			ASP.NET Web API 使用很长时间了,期间也碰到不少问题,升级到WebAPI2后这些问题都解决了,稳定性方面也提升不少,所以推荐使用.碰到的问题是下面的2类: 1.multipart/for ... 
- 对改善ABP的一些建议
			园子里有不少同学对ABP框架很感兴趣,而且也已经将ABP用在了商用项目中,有些可能还在操练阶段.一般来说,我们使用ABP默认的一些功能已经足够了,但还是有很多人想要自己拓展一些功能而自己实现不了或者说 ... 
- 剑指Offer面试题:34.翻转单词顺序VS左旋转字符串
			一.题目一:翻转单词顺序 1.1 题目说明 题目一:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变.为简单起见,标点符号和普通字母一样处理.例如输入字符串"I am a st ... 
