详解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入门教程(11) js事件处理
Jan 31 Javascript
javascript相等运算符与等同运算符详细介绍
Nov 09 Javascript
JQuery 使用attr方法实现下拉列表选中
Oct 13 Javascript
javascript事件模型实例分析
Jan 30 Javascript
jquery实现textarea 高度自适应
Mar 11 Javascript
javascript cookie用法基础教程(概念,设置,读取及删除)
Sep 20 Javascript
JavaScript实现256色转灰度图
Feb 22 Javascript
基于vue实现网站前台的权限管理(前后端分离实践)
Jan 13 Javascript
微信小程序实现手势图案锁屏功能
Jan 30 Javascript
微信小程序实现tab左右切换效果
Nov 15 Javascript
JavaScript数组排序功能简单实现
May 14 Javascript
原生JS实现多条件筛选
Aug 19 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
支持生僻字且自动识别utf-8编码的php汉字转拼音类
2014/06/27 PHP
php的命名空间与自动加载实现方法
2019/08/25 PHP
jQuery 源代码显示控件 (Ajax加载方式).
2009/05/18 Javascript
js不完美解决click和dblclick事件冲突问题
2012/07/16 Javascript
jQuery遍历对象、数组、集合实例
2014/11/08 Javascript
使用jquery清空、复位整个输入域
2015/04/02 Javascript
javascript排序函数实现数字排序
2015/06/26 Javascript
Javascript仿新浪游戏频道鼠标悬停显示子菜单效果
2015/08/21 Javascript
jquery插件ajaxupload实现文件上传操作
2015/12/09 Javascript
JavaScript+html5 canvas制作的百花齐放效果完整实例
2016/01/26 Javascript
基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍
2016/05/12 Javascript
jQuery的层级查找方式分析
2016/06/16 Javascript
利用Angularjs和Bootstrap前端开发案例实战
2016/08/27 Javascript
JS仿hao123导航页面图片轮播效果
2016/09/01 Javascript
javascript的函数劫持浅析
2016/09/26 Javascript
微信小程序多张图片上传功能
2017/06/07 Javascript
js实现图片上传预览原理分析
2017/07/13 Javascript
nodejs搭建本地服务器轻松解决跨域问题
2018/03/21 NodeJs
vue点击页面空白处实现保存功能
2019/11/06 Javascript
基于vue+echarts 数据可视化大屏展示的方法示例
2020/03/09 Javascript
python对日志进行处理的实例代码
2018/10/06 Python
详解Django配置JWT认证方式
2020/05/09 Python
基于python模拟bfs和dfs代码实例
2020/11/19 Python
python mongo 向数据中的数组类型新增数据操作
2020/12/05 Python
浅谈CSS3中display属性的Flex布局的方法
2017/08/14 HTML / CSS
HTML5 Canvas绘制圆点虚线实例
2015/01/01 HTML / CSS
澳大利亚买卖正宗二手奢侈品交易平台:Luxe.It.Fwd
2019/10/16 全球购物
中国制造网:Made-in-China.com
2019/10/25 全球购物
软件生产职位结构化面试主要考察要素及面试题库
2015/06/12 面试题
国贸专业个人求职信分享
2013/12/04 职场文书
讲解员培训方案
2014/05/04 职场文书
多媒体教室标语
2014/06/26 职场文书
大学新生军训自我鉴定范文
2014/09/13 职场文书
2015年重阳节活动主持词
2015/07/30 职场文书
3招让你摆脱即兴讲话冷场尴尬
2019/08/08 职场文书
oracle数据库去除重复数据
2022/05/20 Oracle