深入理解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机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ...
随机推荐
- QGis、Qt对话框上的OK、Open、Cancel、Help等英文翻译
成功编译qgis,启动程序发现对话框上的OK.Open.Cancel.Help等依然是英文字段,然后查找源码看这些字段是否都添加到了语言翻译包中: 最后发现这些按钮都是qt的QTGui4库中的QDia ...
- 解决FF浏览器无法执行window.close()脚本
在FF浏览器中输入about:config 查找dom.allow_scripts_to_close_windows 将值改为true
- goEasy消息推送,pushlet 向特写用户实时推送
goEasy 1先去goEasy官网注册goeasy.io,并创建application,得到superKey 2引入js <script type="text/javascript& ...
- PHP的数组排序函数
<?php class order{ /** * * 数组排序 * @param array $arr 例如: * array ( array ( 'deskId' => '460646' ...
- vim的配置与使用
经历了一次source insight 一言不合就崩溃之后,决定还是花点时间好好配置和学习以下vim 于是找到大神的配置 https://github.com/humiaozuzu/dot-vimrc ...
- ubuntu中source insight打不开,报错pagefault的解决方法
在ubuntu中装了source insight用来看代码. 但用了一段时间后,有一天突然就发现打不开了,打开马上会弹出wine的错误,详细信息里面是什么PageFault 0x0000008之类的, ...
- jquery.ajax
var params = {};//定义一个数组 var USERNAME= $("#USERNAME").val(); params["USERNAME"]= ...
- 安装jdk
检查已安装jdk,如果有,先删除 rpm -qa|grep java rpm -e --nodeps filename 从oracle官方网站下载jdk安装包:jdk-8u111-linux-x64. ...
- Curator Framework的基本使用方法
Curator Framework提供了简化使用zookeeper更高级的API接口.它包涵很多优秀的特性,主要包括以下三点: 自动连接管理:自动处理zookeeper的连接和重试存在一些潜在的问题: ...
- SQL Server 索引和表体系结构(包含列索引)
包含列索引 概述 包含列索引也是非聚集索引,索引结构跟聚集索引结构是一样,有一点不同的地方就是包含列索引的非键列只存储在叶子节点:包含列索引的列分为键列和非键列,所谓的非键列就是INCLUDE中包含的 ...