详解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 相关文章推荐
Javascript Object.extend
May 18 Javascript
js 自定义个性下拉选择框示例
Aug 20 Javascript
js onload事件不起作用示例分析
Oct 09 Javascript
jQuery调用RESTful WCF示例代码(GET方法/POST方法)
Jan 26 Javascript
js实现横向伸展开的二级导航菜单代码
Aug 28 Javascript
浅析ES6的八进制与二进制整数字面量
Aug 30 Javascript
jQuery序列化表单成对象的简单实现
Nov 29 Javascript
Angular2库初探
Mar 01 Javascript
JS实现的四级密码强度检测功能示例
May 11 Javascript
[原创]js实现保存文本框内容为本地文件兼容IE,chrome,火狐浏览器
Feb 14 Javascript
Vue2.5通过json文件读取数据的方法
Feb 27 Javascript
微信小程序登录数据解密及状态维持实例详解
May 06 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
BBS(php &amp; mysql)完整版(四)
2006/10/09 PHP
ajax php传递和接收变量实现思路及代码
2012/12/19 PHP
php中生成随机密码的自定义函数代码
2013/10/21 PHP
ThinkPHP 5.x远程命令执行漏洞复现
2019/09/23 PHP
聊聊 PHP 8 新特性 Attributes
2020/08/19 PHP
显示、隐藏密码
2006/07/01 Javascript
event.srcElement+表格应用
2006/08/29 Javascript
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
capacityFixed 基于jquery的类似于新浪微博新消息提示的定位框
2011/05/24 Javascript
js图片自动切换效果处理代码
2013/05/07 Javascript
javascript中attribute和property的区别详解
2014/06/05 Javascript
使用JavaScript 编写简单计算器
2014/11/24 Javascript
request请求获取参数的实现方法(post和get两种方式)
2016/09/27 Javascript
js复制内容到剪贴板代码,js复制代码的简单实例
2016/10/27 Javascript
vue2.0父子组件及非父子组件之间的通信方法
2017/01/21 Javascript
js异步编程小技巧详解
2017/08/14 Javascript
JS实现简单的浮动碰撞效果示例
2017/12/28 Javascript
vue实现提示保存后退出的方法
2018/03/15 Javascript
layui点击导航栏刷新tab页的示例代码
2018/08/14 Javascript
Vue监听页面刷新和关闭功能
2019/06/20 Javascript
vue 使用element-ui中的Notification自定义按钮并实现关闭功能及如何处理多个通知
2019/08/17 Javascript
layui表格 返回的数据状态异常的解决方法
2019/09/10 Javascript
jQuery实现手风琴特效
2021/01/11 jQuery
Python中type的构造函数参数含义说明
2015/06/21 Python
讲解Python的Scrapy爬虫框架使用代理进行采集的方法
2016/02/18 Python
python入门教程 python入门神图一张
2018/03/05 Python
Ubuntu下使用Python实现游戏制作中的切分图片功能
2018/03/30 Python
用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)
2020/05/17 Python
css3教程之倾斜页面
2014/01/27 HTML / CSS
致接力运动员广播稿
2014/02/17 职场文书
元宵晚会主持词
2014/03/25 职场文书
关于运动会的口号
2014/06/07 职场文书
结对共建协议书
2014/08/20 职场文书
学习党史心得体会2016
2016/01/23 职场文书
2019商业计划书格式、范文
2019/04/24 职场文书
Idea连接MySQL数据库出现中文乱码的问题
2021/04/14 MySQL