详解javascript中的事件处理


Posted in Javascript onNovember 06, 2015

一.事件传播机制

客户端JavaScript程序(就是浏览器啦)采用了异步事件驱动编程模型。当文档、浏览器、元素或与之相关的对象发生某些有趣的事情时,Web浏览器就会产生事件(event)。如果JavaScript应用程序关注特定类型的事件,那么它可以注册当这类事件发生时要调用的一个或多个函数。当然了,这种风格并非Web编程独有,所有使用图形用户界面的应用程序都采用了它。

既然要详解事件处理,那我们先从几个基础概念说起吧:

①事件类型(event type):是一个用来说明发生什么类型事件的字符串。例如,“mousemove”表示用户移动鼠标,“keydown”表示键盘上某个键被按下。事件类型只是一个字符串,有时候又称之为事件名字(event name);

②事件目标(event target):是发生事件或与之相关的对象。Window、Document和Element对象是最常见的事件目标。当然,AJAX中的XMLHttpRequest对象也是一个事件目标;

③事件处理程序(event handler):是处理或响应事件的函数,它也叫事件监听程序(event listener)。应用程序通过指明事件类型和事件目标,在Web浏览器中注册它们的事件处理函数。

④事件对象(event object):是与特定事件相关且包含有关该事件详细信息的对象。事件对象作为参数传递给事件处理函数(但是在IE8以及其之前版本中,全局变量event才是事件对象)。事件对象都有用来指定事件类型(event type)的type属性和指定事件目标(event target)的target属性(但是在IE8以及其之前版本中,用的是srcElement而非target)。当然,不同类型的事件还会为其相关事件对象定义一些其他的独有属性。例如,鼠标事件的相关对象会包含鼠标指针的坐标,而键盘事件的相关对象会包含按下的键和辅助键的详细信息。

以上说完了四个基本概念。那么问题来了——如果在一个web页面上用鼠标点击一个元素a的某一子元素b时,应该先执行子元素b注册的事件处理程序还是先执行元素a注册的事件处理程序呢(假设元素a和它的子元素b都有注册事件处理程序)?身为读者的你是否想过这个问题呢?

这个问题就涉及到浏览器中的事件传播(event propagation)机制。相信大家都听说过事件冒泡(event bubble)和事件捕获(event capturing)吧!没错,它们就是浏览器中的事件传播机制。无图无真相,没有配图?那怎么阔以:

详解javascript中的事件处理

看了图之后相信你已经大概理解了浏览器中的事件传播机制了:当一个事件发生时,它会先从浏览器顶级对象Window一路向下传递,一直传递到触发这个事件的那个元素,这也就是事件捕获过程。然而,一切并没有结束,事件又从这个元素一路向上传递到Window对象,这也就是事件冒泡过程(但是在IE8以及其之前版本中,事件模型并未定义捕获过程,只有冒泡过程)。

所以,关于上面的问题,还得看元素a注册的事件处理程序是在捕获过程还是在冒泡过程了。那么到底什么是在捕获过程注册事件处理程序,在冒泡过程注册事件处理程序又是怎么做的呢?这就得好好说说几种注册事件处理程序的方式了:

1. 设置HTML标签属性为事件处理程序

文档元素的事件处理程序属性,其名字由“on”后面跟着事件名组成,例如:onclick、onmouseover。当然了,这种形式只能为DOM元素注册事件处理程序。实例:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
 #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
 #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
 </style>
</head>
<body>
 <div id="div1" onClick="console.log('div1');">div1
 <div id="div2" oNClick="console.log('div2');">div2
 <div id="div3" onclick="console.log('div3');" onclick="console.log('div3333');">div3
 </div>
 </div>
 </div>
<script type="text/javascript">
</script>
</body>
</html>

结果(鼠标点击div3区域后):

详解javascript中的事件处理

从结果中可以看出:

①因为HTML里面不区分大小写,所以这里事件处理程序属性名大写、小写、大小混写均可,属性值就是相应事件处理程序的JavaScript代码;

②若给同一元素写多个onclick事件处理属性,浏览器只执行第一个onclick里面的代码,后面的会被忽略;

③这种形式是在事件冒泡过程中注册事件处理程序的;

2.设置JavaScript对象属性为事件处理程序

可以通过设置某一事件目标的事件处理程序属性来为其注册相应的事件处理程序。事件处理程序属性名字由“on”后面跟着事件名组成,例如:onclick、onmouseover。实例:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
 #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
 #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
 </style>
</head>
<body>
 <div id="div1">div1
 <div id="div2">div2
 <div id="div3">div3
 </div>
 </div>
 </div>
<script type="text/javascript">
 var div1 = document.getElementById('div1');
 var div2 = document.getElementById('div2');
 var div3 = document.getElementById('div3');
div1.onclick = function(){


console.log('div1');

};

div2.onclick = function(){


console.log('div2');

};

div3.onclick = function(){


console.log('div3');

};

div1.onclick = function(){


console.log('div11111');

};


div1.onClick = function(){


console.log('DIV11111');

};

</script>
</body>
</html>

结果(鼠标点击div3区域后):

详解javascript中的事件处理

 从结果中可以看出:

①因为JavaScript是严格区分大小写的,所以,这种形式下属性名只能按规定小写;

②若给同一元素对象写多个onclick事件处理属性,后面写的会覆盖前面的(ps:这就是在修改一个对象属性的值,属性的值是唯一确定的);

③这种形式也是在事件冒泡过程中注册事件处理程序的;

3.addEventListener()

前两种方式出现在Web初期,众多浏览器都有实现。而addEventListener()方法是标准事件模型中定义的。任何能成为事件目标的对象——这些对象包括Window对象、Document对象和所有文档元素等——都定义了一个名叫addEventListener()的方法,使用这个方法可以为事件目标注册事件处理程序。addEventListener()接受三个参数:第一个参数是要注册处理程序的事件类型,其值是字符串,但并不包括前缀“on”;第二个参数是指当指定类型的事件发生时应该调用的函数;第三个参数是布尔值,其可以忽略(某些旧的浏览器上不能忽略这个参数),默认值为false。这种情况是在事件冒泡过程中注册事件处理程序。当其为true时,就是在事件捕获过程中注册事件处理程序。实例:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

<title>test</title>

<style type="text/css">


#div1{width: 300px; height: 300px; background: red; overflow:hidden;}


#div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}


#div3{margin:50px auto; width: 100px; height: 100px; background: blue;}

</style>
</head>
<body>

<div id="div1">div1


<div id="div2">div2



<div id="div3">div3



</div>


</div>

</div>
<script type="text/javascript">

var div1 = document.getElementById('div1');

var div2 = document.getElementById('div2');

var div3 = document.getElementById('div3');

div1.addEventListener('click', function(){ console.log('div1-bubble'); }, false);

div2.addEventListener('click', function(){ console.log('div2-bubble'); }, false);

div3.addEventListener('click', function(){ console.log('div3-bubble'); }, false);

div3.addEventListener('click', function(){ console.log('div3-bubble222'); }, false);

div1.addEventListener('click', function(){ console.log('div1-capturing'); }, true);

div2.addEventListener('click', function(){ console.log('div2-capturing'); }, true);

div3.addEventListener('click', function(){ console.log('div3-capturing'); }, true);
</script>
</body>
</html>

结果(鼠标点击div3区域后):

详解javascript中的事件处理

从结果中可以看出:

①addEventListener()第三个参数的作用正如上面所说;

②通过addEventListener()方法给同一对象注册多个同类型的事件,并不会发生忽略或覆盖,而是会按顺序依次执行;

相对addEventListener()的是removeEventListener()方法,它同样有三个参数,前两个参数自然跟addEventListener()的意义一样,而第三个参数也只需跟相应的addEventListener()的第三个参数保持一致即可,同样可以省略,默认值为false。它表示从对象中删除某个事件处理函数。实例:

div1.addEventListener('click', div1BubbleFun, false);
div1.removeEventListener('click', div1BubbleFun, false);
function div1BubbleFun(){
 console.log('div1-bubble');
}

4.attachEvent()

但是,IE8以及其之前版本的浏览器并不支持addEventListener()和removeEventListener()。相应的,IE定义了类似的方法attachEvent()和detachEvent()。因为IE8以及其之前版本浏览器也不支持事件捕获,所以attachEvent()并不能注册捕获过程中的事件处理函数,因此attachEvent()和detachEvent()要求只有两个参数:事件类型和事件处理函数。而且,它们的第一个参数使用了带“on”前缀的事件处理程序属性名。实例:

var div1 = document.getElementById('div1');
div1.attachEvent('onclick', div1BubbleFun);
function div1BubbleFun(){
console.log('div1-bubble');
}

相应的,从对象上删除事件处理程序函数使用detachEvent()。例如:

div1.detachEvent('onclick', div1BubbleFun);

到此为止,我们已经说了浏览器中事件传播机制以及各种注册事件处理程序的方法。下面我们就再说说事件处理程序调用时的一些问题吧!

二.事件处理程序的调用

1.事件处理程序的参数:正如前面所说,通常事件对象作为参数传递给事件处理函数,但IE8以及其之前版本的浏览器中全局变量event才是事件对象。所以,我们在写相关代码时应该注意兼容性问题。实例(给页面上id为div1的元素添加点击事件,当点击该元素时在控制台输出事件类型和被点击元素本身):

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 if(div1.addEventListener){
 div1.addEventListener('click', div1Fun, false);
 }else if(div1.attachEvent){
 div1.attachEvent('onclick', div1Fun);
 }
 function div1Fun(event){
 event = event || window.event;
 var target = event.target || event.srcElement;
 console.log(event.type);
 console.log(target);
 }
 </script>
</body>
</html>

2.事件处理程序的运行环境:关于事件处理程序的运行环境,也就是在事件处理程序中调用上下文(this值)的指向问题,可以看下面四个实例。

实例一:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 </script>
</body>
</html>

结果一:

详解javascript中的事件处理

从结果可以看出:

①第一种方法事件处理程序中this指向这个元素本身;

实例二:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 div1.onclick = function(){
 console.log('div1.onclick:');
 console.log(this);
 };
 </script>
</body>
</html>

结果二:

详解javascript中的事件处理

从结果可以看出:

①第二种方法事件处理程序中this也指向这个元素本身;

②存在第二种方法时,它会覆盖第一种方法注册的事件处理程序;

实例三:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 div1.onclick = function(){
 console.log('div1.onclick:');
 console.log(this);
 };
 div1.addEventListener('click', function(){
 console.log('div1.addEventListener:');
 console.log(this);
 }, false);
 </script>
</body>
</html>

结果三:

详解javascript中的事件处理

从结果可以看出:

①第三种方法事件处理程序中this也指向这个元素本身;

②第三种方法并不会覆盖第一种或第二种方法注册的事件处理程序;

实例四:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 div1.onclick = function(){
 console.log('div1.onclick:');
 console.log(this);
 };
 div1.attachEvent('onclick', function(){
 console.log('div1.attachEvent:');
 console.log(this === window);
 });
 
 </script>
</body>
</html>

结果四:

详解javascript中的事件处理

从结果可以看出:

①第四种方法事件处理程序中this指向全局对象Window;

②第四种方法也不会覆盖第一种或第二种方法注册的事件处理程序;

3.事件处理程序的调用顺序:多个事件处理程序调用规则如下:

①通过HTML属性注册的处理程序和通过设置对象属性的处理程序一直优先调用;

②使用addEventListener()注册的处理程序按照它们的注册顺序依次调用;

③使用attachEvent()注册的处理程序可能按照任何顺序调用,所以代码不应该依赖于调用顺序;

4.事件取消:

①取消事件的浏览器默认操作(比如点击超链接元素会自动发生页面跳转的默认操作):如果使用前两种方法注册事件处理程序,可以在处理程序中添加返回值false来取消事件的浏览器默认操作。在支持addEventListener()的浏览器中,也可以通过调用事件对象的preventDefault()方法取消事件的默认操作。至于IE8及其之前的浏览器可以通过设置事件对象的returnValue属性为false来取消事件的默认操作。参考代码:

function cancelHandler(event){
 var event = event || window.event;
 if(event.preventDefault){
 event.preventDefault();
 }
 if(event.returnValue){
 event.returnValue = false;
 }
 return false;
}

②取消事件传播:在支持addEventListener()的浏览器中,可以调用事件对象的一个stopPropagation()方法阻止事件的继续传播,它能工作在事件传播期间的任何阶段(捕获期阶段、事件目标本身、冒泡阶段);但是在IE8以及其之前版本的浏览器中并不支持stopPropagation()方法,而且这些浏览器也不支持事件传播的捕获阶段,相应的,IE事件对象有一个cancelBubble属性,设置这个属性为true能阻止事件进一步传播(即阻止其冒泡)。参考代码(阻止发生在div3区域的点击事件冒泡到div2和div1):

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
 #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
 #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
 </style>
</head>
<body>
 <div id="div1">div1
 <div id="div2">div2
 <div id="div3">div3
 </div>
 </div>
 </div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 var div2 = document.getElementById('div2');
 var div3 = document.getElementById('div3');
  div1.onclick = function(){
 console.log('div1');
 
 };
 
 div2.onclick = function(){
 console.log('div2');
 
 };
    div3.onclick = function(event){
 stopEventPropagation(event);
 console.log('div3');
 
 };
 
 function stopEventPropagation(event){
 var event = event || window.event;
 if(event.stopPropagation){
 event.stopPropagation();
 }else{
 event.cancelBubble = true;
 }
 }
 </script>
</body>
</html>

以上就是javascript事件处理的全部介绍,当然,关于事件冒泡还是有可利用之处的,这也就是我们常说的事件代理或者事件委托,对于这方面知识以后会陆续更新。

Javascript 相关文章推荐
$.ajax返回的JSON无法执行success的解决方法
Sep 09 Javascript
让图片跳跃起来  javascript图片轮播特效
Feb 16 Javascript
js删除局部变量的实现方法
Jun 25 Javascript
JS如何生成一个不重复的ID的函数
Dec 25 Javascript
jQuery 禁止表单用户名、密码自动填充功能
Oct 30 jQuery
详解React 在服务端渲染的实现
Nov 16 Javascript
Vue.JS项目中5个经典Vuex插件
Nov 28 Javascript
js实现一个简单的MVVM框架示例
Jan 15 Javascript
详解如何在项目中使用jest测试react native组件
Feb 09 Javascript
Node.js中,在cmd界面,进入退出Node.js运行环境的方法
May 12 Javascript
angularJs中跳转到指定的锚点实例($anchorScroll)
Aug 31 Javascript
小程序跨页面交互的作用与方法详解
Jan 07 Javascript
jQuery插件实现静态HTML验证码校验
Nov 06 #Javascript
jQuery Real Person验证码插件防止表单自动提交
Nov 06 #Javascript
jQuery实现非常实用漂亮的select下拉菜单选择效果
Nov 06 #Javascript
javascript如何实现暂停功能
Nov 06 #Javascript
JavaScript实现带缓冲效果的随屏滚动漂浮广告代码
Nov 06 #Javascript
jQuery实现可关闭固定于底(顶)部的工具条菜单效果
Nov 06 #Javascript
JS实现的自定义网页拖动类
Nov 06 #Javascript
You might like
PHP对表单提交特殊字符的过滤和处理方法汇总
2014/02/18 PHP
Yii2简单实现多语言配置的方法
2016/07/23 PHP
PHP实现批量检测网站是否能够正常打开的方法
2016/08/23 PHP
Laravel模糊查询区分大小写的实例
2019/09/29 PHP
Laravel 类和接口注入相关的代码
2019/10/15 PHP
IE和Firefox在JavaScript应用中的兼容性探讨
2008/04/01 Javascript
Javascript 跨域访问解决方案
2009/02/14 Javascript
JavaScript高级程序设计 扩展--关于动态原型
2010/11/09 Javascript
关于JS控制代码暂停的实现方法分享
2012/10/11 Javascript
js函数获取html中className所在的内容并去除标签
2013/09/08 Javascript
使用iojs的jsdom库实现同步系统时间
2015/04/20 Javascript
浅析node.js的模块加载机制
2018/05/25 Javascript
vue移动端屏幕适配详解
2019/04/30 Javascript
Vue实现点击显示不同图片的效果
2019/08/10 Javascript
JS删除对象中某一属性案例详解
2020/09/08 Javascript
基于Vant UI框架实现时间段选择器
2020/12/24 Javascript
[00:32]2018DOTA2亚洲邀请赛Liquid出场
2018/04/03 DOTA
[06:43]2018DOTA2国际邀请赛寻真——VGJ.Thunder
2018/08/11 DOTA
Python中存取文件的4种不同操作
2018/07/02 Python
Django使用list对单个或者多个字段求values值实例
2020/03/31 Python
Python爬虫:Request Payload和Form Data的简单区别说明
2020/04/30 Python
python中温度单位转换的实例方法
2020/12/27 Python
纯CSS实现颜色渐变效果(包含环形渐变、线性渐变、彩虹效果等)
2014/05/07 HTML / CSS
CSS3绘制超炫的上下起伏波动进度加载动画
2016/04/21 HTML / CSS
美国一家主营日韩美妆护肤品的在线商店:iMomoko
2016/09/11 全球购物
Yankee Candle官网:美国最畅销蜡烛品牌之一
2020/01/05 全球购物
日语专业个人的求职信
2013/12/03 职场文书
我的大学生活职业生涯规划
2014/01/02 职场文书
大学学习生活感言
2014/01/18 职场文书
法制宣传日活动总结
2014/04/29 职场文书
幼儿园迎国庆65周年活动策划方案
2014/09/16 职场文书
国防教育标语
2014/10/08 职场文书
2014年综合治理工作总结
2014/11/20 职场文书
优秀员工自荐书
2015/03/06 职场文书
《惊弓之鸟》教学反思
2016/02/20 职场文书
高三生物教学反思
2016/02/22 职场文书