前面的话

  事件处理程序又叫事件侦听器,实际上就是事件的绑定函数。事件发生时会执行函数中相应代码。事件处理程序有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事件机制系列第二篇——事件处理程序的更多相关文章

  1. 深入理解DOM事件机制系列第一篇——事件流

    × 目录 [1]历史 [2]事件冒泡 [3]事件捕获[4]事件流 前面的话 javascript操作CSS称为脚本化CSS,而javascript与HTML的交互是通过事件实现的.事件就是文档或浏览器 ...

  2. 深入理解DOM事件类型系列第二篇——键盘事件

    × 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ...

  3. 深入理解this机制系列第二篇——this绑定优先级

    前面的话 上一篇介绍过this的绑定规则,那如果在函数的调用位置上同时存在两种以上的绑定规则应该怎么办呢?本文将介绍this绑定的优先级 显式绑定 pk 隐式绑定 显式绑定胜出 function fo ...

  4. 深入理解脚本化CSS系列第二篇——查询计算样式

    × 目录 [1]getComputedStyle [2]注意事项 [3]currentStyle[4]IE 前面的话 元素的渲染结果是多个CSS样式博弈后的最终结果,这也是CSS中的C(cascade ...

  5. 深入理解javascript函数进阶系列第二篇——函数柯里化

    前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ...

  6. 深入理解javascript选择器API系列第二篇——getElementsByClassName

    × 目录 [1]使用 [2]classList [3]扩展 前面的话 既然有getElementById()和getElementsByTagName()方法,为什么没有getElementsByCl ...

  7. 深入理解javascript函数系列第二篇——函数参数

    × 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...

  8. 深入理解javascript作用域系列第二篇——词法作用域和动态作用域

    × 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...

  9. 深入理解javascript作用域系列第二篇

    前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ...

随机推荐

  1. [NOIP2013]华容道

    1.题面 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间.小 B 玩的华容道与经典 ...

  2. 微信小程序火车票查询 直取12306数据

    最终效果图: 样式丑哭了,我毕竟不是前端,宗旨就是练练手,体验微信小程序的开发,以最直接的方式获取12306数据查询火车票. 目录结构: search1是出发站列表,search2是目的站列表,命名没 ...

  3. 初探React-Native

    props 大多数组件在创建时就可以使用各种参数来进行定制.用于定制的这些参数就称为props(属性). 以常见的基础组件Image为例,在创建一个图片时,可以传入一个名为source的prop来指定 ...

  4. 【ORACLE】MD5加密

        今天乌干达充值卡入库时,发现有资源已经存在的异常, 异常原因经过核实是由于卡资源密码在库中已经存在, 为进一步查找存在的原因, 因此需要对导入文件密码的MD5 加密, 通过MD5加密后的字符串 ...

  5. grunt-connect-proxy解决开发时跨域问题

    最近的项目中前后端是完全分离开发的,前端用grunt管理项目.这样就会导致一个问题:开发时前端调用后台的接口时因为不在一个服务器,所以会出现跨域问题.但是也不能用JSONP或CROS方式实现真正的跨域 ...

  6. https://oj.leetcode.com/problems/majority-element/

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  7. Python之路【第六篇】python基础 之面向对象(一)

    一.三大编程范式 1.面向过程编程 2.函数式编程 3.面向对象编程 二.编程进化论 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比 ...

  8. replace实现正则过滤替换非法字符

    html+js结构如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http: ...

  9. ARM 编译 phddns

    参考博文http://bluegemyf.blog.163.com/blog/static/11816340201310472751513/ 1.安装必要的开发包 sudo apt-get  inst ...

  10. pagination 分页

    <!DOCTYPE html> <html> <head> <title>pagination</title> <style type ...