JavaScript 事件流、事件处理程序及事件对象总结


Posted in Javascript onApril 01, 2017

JS与HTML之间的交互通过事件实现。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用监听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式,支持页面的行为与页面的外观之间的松散耦合。本文将介绍JS事件相关的基础知识。

一、事件流

事件流描述的是从页面中接受事件的顺序。

事件冒泡

事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的结点(文档)。以下面HTML页面为例,如果你点击了页面中的按钮,那么”click”事件会按照< button>、< body>、< html>、document的顺序传播。换句话说,事件冒泡指的就是事件从底层触发事件的元素开始沿着DOM树向上传播,直到document对象。

<html>
 <head>
  <title>Test</title>
 </head>
 <body>
  <button id="myBtn">A Btn</button>
 </body>
</html>

事件捕获

与事件冒泡的思路相反,事件捕获的思想是不太具体的节点应该更早地接收到事件,最具体的结点应该最后才接收事件。同样还是上面那个例子,点击页面中的按钮之后,”click”事件会按照document、< html>、< body>、< button>的顺序传播。换句话说,事件捕获就是指事件从document对象开始沿着DOM树向下传播,直到事件的实际目标元素。

DOM事件流

“DOM2级事件”规定的事件包括三个阶段: 事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

还是以之前的点击按钮为例,在DOM事件流中,捕获阶段,”click”事件从document开始向下传递到body元素(注意,实际目标button在捕获阶段不会接收到事件)。目标阶段,button元素接收到”click”事件。最后,冒泡阶段,事件又被传播回文档。

二、事件处理程序

事件是用户或浏览器自身执行的某种动作,而响应某个事件的函数就叫做事件处理程序或事件侦听器。

HTML事件处理程序

这里的HTML事件处理程序指的是直接在HTML元素里面通过特性(attribute)定义的事件处理程序,请看下面的代码示例。这样是定的事件处理程序会创建一个封装着元素属性值的函数,this值等于事件的目标元素。通过这种方法指定事件处理程序存在不少缺点,不推荐使用。

<button onclick="alert('HaHa~')">Btn-1</button>
<button onclick="alert('event.type')">Btn-2</button>
<button onclick="handler()">Btn-3</button>
<script type="text/javascript">
 function handler() {
  alert("Haha~");
 }
</script>

DOM0级事件处理程序

通过JS指定事件处理程序的传统方式就是将一个函数赋值给一个事件处理程序属性,请看下面代码示例。通过这种方式指定的事件处理程序是在元素的作用域中运行,this引用的是当前元素。这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。若要删除事件,直接令onclick的值为空即可。

var btn = document.getElementById("myBtn");
btn.onclick = function() {
 console.log("this.id"); // "myBtn"
};
// 删除事件处理程序
btn.onclick = null;

DOM2级事件处理程序

“DOM2级事件”定义了两个方法用于指定和删除事件处理程序,addEventListener()和removeEventListener()。所有DOM节点中都包含这两个方法。这两个方法都接收3个参数,要处理的事件、处理函数、布尔值。最后的布尔值为true时表示在捕获阶段调用事件处理程序,为false时表示在冒泡阶段调用处理程序。与DOM0级方法一样,这里添加的事件处理程序也是在其依附的元素的作用域中运行。DOM2级方法添加事件处理程序的优势是可以添加多个事件处理程序。这些事件处理程序会按照它们被添加的顺序触发。下面是代码示例:

var btn = document.getElementById("myBtn");
// 添加,触发点击事件时先输出"myBtn"再输出"HaHa~"
btn.addEventListener("click", function() {
 console.log(this.id);
}, false);
btn.addEventListener("click", function() {
 console.log("HaHa~");
}, false);

通过addEventListener()添加的事件只能通过removeEventListener()来删除。删除时传入的参数与添加时使用的参数应该保持一致。这也意味着通过addEventListener()添加的匿名函数将无法删除,因为无法将添加时传递的匿名函数传给removeEventListener(),即便在删除的时候写了一个一模一样的函数,但此时这个函数只是一个新的匿名函数。请看下面代码示例:

var btn = document.getElementById("myBtn");
// 无法删除匿名函数
btn.addEventListener("click", function() {
 console.log(this.id);
}, false);
btn.removeEventListener("click", function() {
 console.log(this.id);
}, false);

// 正确的添加和删除方式
function handler() {
 console.log(this.id);
}
btn.addEventListener("click", handler, false);
btn.removeEventListener("click", handler, false);

大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候才将事件处理程序添加到捕获阶段。JS高级程序设计上给出的建议是,如果不是特别需要,不建议在事件捕获阶段注册事件处理程序。

IE事件处理程序

IE实现了与DOM中类似的两个方法: attachEvent()和deleteEvent()。这两个方法接收两个参数,事件处理程序名称和事件处理程序。注意,第一个参数是事件处理程序名称而不是事件名称,也就是说在注册点击事件的处理程序时应该传入”onclick”而不是”click”,这里跟DOM的方法有些差别。另外,这两个方法注册的事件处理程序是在全局作用域中运行而不是元素作用域,this的值指向window。还有一点需要特别小心,通过attachEvent()方法也可以添加多个事件处理程序,但是它们的执行顺序却不是按照它们被添加的顺序,而是完全相反,跟DOM方法截然不同。突然觉得IE真的特别反人类~~~下面是代码示例:

var btn = document.getElementById("myBtn");
function handler1() { // ... }
function handler2() { // ... }
// 添加,触发点击事件时先执行handler2再执行handler1
btn.attachEvent("onclick", handler1);
btn.attachEvent("onclick", handler2);
// 删除
btn.deleteEvent("onclick", handler1);
btn.deleteEvent("onclick", handler2);

三、事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息,包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。

DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入事件处理程序中,无论指定事件处理程序时用的是DOM0还是DOM2的方法,都会传入event对象。event对象只有在事件处理程序执行期间才会存在,一旦事件处理程序执行完毕,event对象就会被销毁。下面是代码示例:

var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
 console.log(event.type); // "click"
}
btn.addEventListener("click", function(event) {
 console.log(event.type);
}, false);

event对象包含与创建它的特定事件有关的属性和方法,触发的事件类型不一样,可用的属性方法也有所不同。但是所有的事件都会有下列的属性或方法:

  • bubbles: 布尔值,表示事件是否冒泡
  • cancelable: 布尔值,表示是否可以取消事件的默认行为
  • currentTarget: 元素,事件处理程序当前正在处理事件的那个元素
  • defaultPrevented: 布尔值,表示是否调用过preventDefault()方法
  • detail: 整数,与事件相关的细节信息
  • eventPhase: 整数,调用事件处理程序的阶段,1表示捕获阶段,2表示目标阶段,3表示冒泡阶段
  • preventDefault(): 函数,取消事件的默认行为,cancelable为true时可以调用该方法
  • stopImmediatePropagation(): 函数,取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用
  • stopPropagation(): 函数,取消事件的进一步捕获或冒泡,bubbles为true时可以调用这个方法
  • target: 元素,事件的目标
  • trusted: 布尔值,为true时表示事件是浏览器生成的,否则表示事件是通过JS创建的
  • type: 字符串,被触发的事件类型
  • view: 与事件关联的抽象视图,等同于发生事件的window对象

下面代码示例展示了上述部分属性的用法,也可以帮助我们进一步理解事件流。假设页面中有一个按钮”myBtn”。当点击按钮时,this和currentTarget都等于body元素,因为事件处理程序是注册在body元素上。target的值却等于按钮元素,因为它是click事件的真正目标。由于按钮上没有注册事件处理程序,结果”click”事件冒泡到了document.body那里才得到处理。

document.body.onclick = function(event) {
 console.log(event.currentTarget === document.body); // true
 console.log(this === document.body); // true
 console.log(event.target === document.getElementById("myBtn")); // true
};

再看一个例子,下面代码中,stopPropagation()方法取消了事件的进一步捕获或冒泡。当我点击按钮时,本来应该会因为事件冒泡机制触发按钮和body元素上的点击事件处理程序,输出”From Bth …”和”From Body …”。现在点击事件在按钮元素上触发之后就被阻止继续在DOM层次中的传播,因此body上的事件处理程序不会被触发。

var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
 console.log("From Bth ...");
 event.stopPropagation(); // 停止事件传播
};
document.body.onclick = function() {
 console.log("From Body ...");
};

IE中的事件对象

在IE中,使用DOM0的方法添加事件处理程序时,event对象作为window对象的一个属性存在。如果是通过attachEvent()方法添加,则event对象是作为参数传入事件处理函数。下面是代码示例:

var btn = document.getElementById("myBtn");
btn.onclick = function() {
 var event = window.event;
 console.log(event.type); // "click"
};
btn.attachEvent("onclick", function(event) {
 console.log(event.type); // "click"
});

IE的event对象同样也包含与创建它的事件相关的属性和方法,这些属性和方法也会因为事件类型的不同而有所差异。但所有事件对象都会包含下列属性:

  • cancelBubble: 布尔值,可读可写,默认为false。将其设置为true时取消事件冒泡
  • returnValue: 布尔值,可读可写,默认为true。将其设置为false时取消事件的默认行为
  • srcElment: 元素,事件的目标元素,与DOM中的target属性相同
  • type: 字符串,事件类型

在IE中,事件处理程序的作用域是根据指定它的方式来确定,this的值不一定是指向事件的目标元素。因此,使用srcElement属性更具保险。请看下面代码实例,第一种方式中this的值为目标元素,而第二种方式,前面讲过这种方式的事件处理程序是在全局作用域中执行,因此this的值为window。

var btn = document.getElementById("myBtn");
btn.onclick = function() {
 console.log(window.event.srcElement === this); // true
}
btn.attachEvent("onclick", function(event) {
 console.log(event.srcElement === this); // false
});

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
利用js跨页面保存变量做菜单的方法
Jan 17 Javascript
基于jQuery的合并表格中相同文本的相邻单元格的代码
Apr 06 Javascript
详解JavaScript UTC时间转换方法
Jan 07 Javascript
JavaScript中的Number数字类型学习笔记
May 26 Javascript
使用Bootstrap typeahead插件实现搜索框自动补全的方法
Jul 07 Javascript
基于js里调用函数时,函数名带括号和不带括号的区别
Jul 28 Javascript
运用js教你轻松制作html音乐播放器
Apr 17 Javascript
AngularJS 在同一个界面启动多个ng-app应用模块详解
Dec 20 Javascript
bootstrap multiselect 多选功能实现方法
Jun 05 Javascript
Node.js+Express+Mysql 实现增删改查
Apr 03 Javascript
JS实现简单的文字无缝上下滚动功能示例
Jun 22 Javascript
vue调用语音播放的方法
Sep 27 Javascript
ES6新特性之字符串的扩展实例分析
Apr 01 #Javascript
Vue.js实战之利用vue-router实现跳转页面
Apr 01 #Javascript
ES6新特性之函数的扩展实例详解
Apr 01 #Javascript
vue-cli的webpack模板项目配置文件分析
Apr 01 #Javascript
ES6新特性之模块Module用法详解
Apr 01 #Javascript
Vue.js实战之组件之间的数据传递
Apr 01 #Javascript
ES6新特性之解构、参数、模块和记号用法示例
Apr 01 #Javascript
You might like
Search File Contents PHP 搜索目录文本内容的代码
2010/02/21 PHP
php学习之数据类型之间的转换代码
2011/05/29 PHP
php cli模式下获取参数的方法
2017/05/05 PHP
form自动提交实例讲解
2017/07/10 PHP
PHP获取星期几的常用方法小结
2018/12/18 PHP
建议大家看下JavaScript重要知识更新
2007/07/08 Javascript
深入理解JavaScript系列(16) 闭包(Closures)
2012/04/12 Javascript
Jquery写一个鼠标拖动效果实现原理与代码
2012/12/24 Javascript
javascript中clone对象详解
2014/12/03 Javascript
js实现同一页面可多次调用的图片幻灯切换效果
2015/02/28 Javascript
利用jQuery设计一个简单的web音乐播放器的实例分享
2016/03/08 Javascript
javascript运算符语法全面概述
2016/07/14 Javascript
node.js利用mongoose获取mongodb数据的格式化问题详解
2017/10/06 Javascript
纯javascript前端实现base64图片下载(兼容IE10+)
2018/09/14 Javascript
js canvas实现橡皮擦效果
2018/12/20 Javascript
JS中的算法与数据结构之队列(Queue)实例详解
2019/08/20 Javascript
整理Python中的赋值运算符
2015/05/13 Python
一个基于flask的web应用诞生 组织结构调整(7)
2017/04/11 Python
Pandas读取MySQL数据到DataFrame的方法
2018/07/25 Python
python实现简单flappy bird
2018/12/24 Python
Django实现单用户登录的方法示例
2019/03/28 Python
Python2和Python3的共存和切换使用
2019/04/12 Python
Python如何使用OS模块调用cmd
2020/02/27 Python
pytorch判断是否cuda 判断变量类型方式
2020/06/23 Python
HTML5实现获取地理位置信息并定位功能
2015/04/25 HTML / CSS
H5新属性audio音频和video视频的控制详解(推荐)
2016/12/09 HTML / CSS
乐天旅游台湾网站:Rakuten Travel TW
2017/06/01 全球购物
Myprotein法国官网:欧洲第一运动营养品牌
2019/03/26 全球购物
Java如何获得ResultSet的总行数
2016/09/03 面试题
卫校护理专业毕业生求职信
2013/11/26 职场文书
经理秘书找工作求职信
2013/12/19 职场文书
中层干部竞争上岗演讲稿
2014/01/13 职场文书
企业宣传方案
2014/03/04 职场文书
党支部综合考察材料
2014/05/19 职场文书
电子商务系毕业生自荐信
2014/05/29 职场文书
爱鸟护鸟的宣传语
2015/07/13 职场文书