(事件阶段)Event Phases

Event对象在event第一次触发的时候被创建出来,并且一直伴随着事件在DOM结构中流转的整个生命周期。event对象会被作为第一个参数传递给事件监听的回调函数。我们可以通过这个event对象来获取到大量当前事件相关的信息:

  • type (String) — 事件的名称
  • target (node) — 事件起源的DOM节点
  • currentTarget?(node) — 当前回调函数被触发的DOM节点(后面会做比较详细的介绍)
  • bubbles (boolean) — 指明这个事件是否是一个冒泡事件(接下来会做解释)
  • preventDefault(function) — 这个方法将阻止浏览器中用户代理对当前事件的相关默认行为被触发。比如阻止<a>元素的click事件加载一个新的页面
  • stopPropagation (function) — 这个方法将阻止当前事件链上后面的元素的回调函数被触发,当前节点上针对此事件的其他回调函数依然会被触发。(我们稍后会详细介绍。)
  • stopImmediatePropagation (function) — 这个方法将阻止当前事件链上所有的回调函数被触发,也包括当前节点上针对此事件已绑定的其他回调函数。
  • cancelable (boolean) — 这个变量指明这个事件的默认行为是否可以通过调用event.preventDefault来阻止。也就是说,只有cancelable为true的时候,调用event.preventDefault才能生效。
  • defaultPrevented (boolean) — 这个状态变量表明当前事件对象的preventDefault方法是否被调用过
  • isTrusted (boolean) — 如果一个事件是由设备本身(如浏览器)触发的,而不是通过JavaScript模拟合成的,那个这个事件被称为可信任的(trusted)
  • eventPhase (number) — 这个数字变量表示当前这个事件所处的阶段(phase):none(0), capture(1),target(2),bubbling(3)。我们会在下一个部分介绍事件的各个阶段
  • timestamp (number) — 事件发生的时间

此外事件对象还可能拥有很多其他的属性,但是他们都是针对特定的event的。比如,鼠标事件包含clientX和clientY属性来表明鼠标在当前视窗的位置

冒泡阶段(Bubble Phase)

事件在目标元素上触发后,并不在这个元素上终止。它会随着DOM树一层层向上冒泡,直到到达最外层的根节点。也就是说,同一个事件会依次在目标节点的父节点,父节点的父节点。。。直到最外层的节点上被触发。

将DOM结构想象成一个洋葱,事件目标是这个洋葱的中心。在捕获阶段,事件从最外层钻入洋葱,穿过途径的每一层。在到达中心后,事件被触发(目标阶段)。然后事件开始回溯,再次经过每一层返回(冒泡阶段)。当到达洋葱表面的时候,这次旅程就结束了。

冒泡过程非常有用。它将我们从对特定元素的事件监听中释放出来,相反,我们可以监听DOM树上更上层的元素,等待事件冒泡的到达。如果没有事件冒泡,在某些情况下,我们需要监听很多不同的元素来确保捕获到想要的事件。

绝大多数事件会冒泡,但并非所有的。当你发现有些事件不冒泡的时候,它肯定是有原因的。不相信?你可以查看一下相应的规范说明

停止传播(Stopping Propagation)

可以通过调用事件对象的stopPropagation方法,在任何阶段(捕获阶段或者冒泡阶段)中断事件的传播。此后,事件不会在后面传播过程中的经过的节点上调用任何的监听函数。

child.addEventListener('click', function(event) {
event.stopPropagation();
}); parent.addEventListener('click', function(event) {
// If the child element is clicked
// this callback will not fire
});

调用event.stopPropagation()不会阻止当前节点上此事件其他的监听函数被调用。如果你希望阻止当前节点上的其他回调函数被调用的话,你可以使用更激进的event.stopImmediatePropagation()方法。

child.addEventListener('click', function(event) {
event.stopImmediatePropagation();
}); child.addEventListener('click', function(event) {
// If the child element is clicked
// this callback will not fire
});

阻止浏览器默认行为

当特定事件发生的时候,浏览器会有一些默认的行为作为反应。最常见的事件不过于link被点击。当一个click事件在一个<a>元素 上被触发时,它会向上冒泡直到DOM结构的最外层document,浏览器会解释href属性,并且在窗口中加载新地址的内容。

在web应用中,开发人员经常希望能够自行管理导航(navigation)信息,而不是通过刷新页面。为了实现这个目的,我们需要阻止浏览器针对点击事件的默认行为,而使用我们自己的处理方式。这时,我们就需要调用event.preventDefault().

anchor.addEventListener('click', function(event) {
event.preventDefault();
// Do our own thing
});

我们可以阻止浏览器的很多其他默认行为。比如,我们可以在HTML5游戏中阻止敲击空格时的页面滚动行为,或者阻止文本选择框的点击行为。

调用event.stopPropagation()只会阻止传播链中后续的回调函数被触发。它不会阻止浏览器的自身的行为。

自定义事件

浏览器并不是唯一能触发DOM事件的载体。我们可以创建自定义的事件并把它们分派给你文档中的任意节点。这些自定义的事件和通常的DOM事件有相同的行为。

var myEvent = new CustomEvent("myevent", {
detail: {
name: "Wilson"
},
bubbles: true,
cancelable: false
}); // Listen for 'myevent' on an element
myElement.addEventListener('myevent', function(event) {
alert('Hello ' + event.detail.name);
}); // Trigger the 'myevent'
myElement.dispatchEvent(myEvent);

在元素上合成不可信任的(untrusted)DOM事件(如click)来模拟用户操作也是可行的。这个在对DOM相关的代码库进行测试的时候特别有用。如果你对此感兴趣的话,在Mozilla Developer Network上有一篇相关的文章

note::

  • CustomEvent接口在IE 8以及IE更低版本不可用
  • 来自Twitter的Flight框架使用了自定义事件进行模块间通信。它强调了一种高度解耦的模块化架构

代理事件监听:

代理事件监听可以让你使用一个事件监听器去监听大量的DOM节点的事件,在这种情况下,它是一种更加方便并且高性能的事件监听方法。举例来说,如果 有一个列表<ul>包含了100个子元素<li>,它们都需要对click事件做出相似的响应,那么我们可能需要查询这100个 子元素,并分别为他们添加上事件监听器。这样的话,我们就会产生100个独立的事件监听器。如果有一个新的元素被添加进去,我们也需要为它添加同样的监听 器。这种方式不但代价比较大,维护起来也比较麻烦。

代理事件监听可以让我们更简单的处理这种情况。我们不去监听所有的子元素的click事件,相反,我们监听他们的父元素<ul>。当一 个<li>元素被点击的时候,这个事件会向上冒泡至<ul>,触发回调函数。我们可以通过检查事件的event.target属 性来判断具体是哪一个<li>被点击了。下面我们举个简单的例子来说明:

var list = document.querySelector('ul');

list.addEventListener('click', function(event) {
var target = event.target; while (target.tagName !== 'LI') {
target = target.parentNode;
if (target === list) return;
} // Do stuff here
});

这样就好多了,我们仅仅使用了一个上层的事件监听器,并且我们不需要在为添加元素而考虑它的事件监听问题。这个概念很简单,但是非常有用。

但是我并不建议你在你的项目中使用上面的这个粗糙的实现。相反,使用一个事件代理的JavaScript库是更好的选择,比如 FT Lab的ftdomdelegate。如果你在使用jQuery,你可以在调用.on()方法的时候,将一个选择器作为第二个参数的方式来轻松的实现事件代理

// Not using event delegation
$('li').on('click', function(){}); // Using event delegation
$('ul').on('click', 'li', function(){});

一些实用的事件

load

load事件可以在任何资源(包括被依赖的资源)被加载完成时被触发,这些资源可以是图片,css,脚本,视频,音频等文件,也可以是document或者window。

image.addEventListener('load', function(event) {
image.classList.add('has-loaded');
});

onbeforeunload

window.onbeforeunload让开发人员可以在想用户离开一个页面的时候进行确认。这个在有些应用中非常有用,比如用户不小心关闭浏览器的tab,我们可以要求用户保存他的修改和数据,否则将会丢失他这次的操作。

window.onbeforeunload = function() {
if (textarea.value != textarea.defaultValue) {
return 'Do you want to leave the page and discard changes?';
}
};

需要注意的是,对页面添加onbeforeunload处理会导致浏览器不对页面进行缓存?,这样会影响页面的访问响应时间。 同时,onbeforeunload的处理函数必须是同步的(synchronous)。

在手机Safari上阻止窗口抖动

在Financial Times中,我们使用了一个简单的event.preventDefault相关的技巧防止了Safari在滚动的时候出现的抖动。(手机端开发接触的不多,所以可能有所误解,如果错误,请了解的同学提点一下。)

document.body.addEventListener('touchmove', function(event) {
event.preventDefault();
});

需要提醒的是这个操作同时也会阻碍正常的原生滚动条的功能(比如使用overflow:scroll)。为了使得内部的子元素在需要的时候能够使用 滚动条的功能,我们在支持滚动的元素上监听这个事件,并且在事件对象上设置一个标识属性。在回调函数中,在document这一层,我们通过对这个扩展的isScrollable标识属性来判断是否对触摸事件阻止默认的滚动行为。

// Lower down in the DOM we set a flag  内部的dom元素我们设置一个标识
scrollableElement.addEventListener('touchmove', function(event) {
event.isScrollable = true;
}); // Higher up the DOM we check for this flag to decide
// whether to let the browser handle the scroll
//外部的document对象上我们根据标识判断是否阻止该元素的默认的原生滚动
document.addEventListener('touchmove', function(event) {
if (!event.isScrollable) event.preventDefault();
});

在IE8即一下的版本中,我们是不能操作事件对象的。作为一个变通方案,我们将一些扩展的属性设置在event.target节点对向上。

resize

在一些复杂的响应式布局中,对window对象监听resize事件是非常常用的一个技巧。仅仅通过css来达到想要的布局效果比较困难。很多时候,我们需要使用JavaScript来计算并设置一个元素的大小。

window.addEventListener('resize', function() {
// update the layout
});

我推荐使用防抖动的回调函数来统一调整回调的频率,从而防止布局上极端抖动的情况出现。

transitionend

现在在项目中,我们经常使用CSS来执行一些转换和动画的效果。有些时候,我们还是需要知道一个特定动画的结束时间。

el.addEventListener('transitionEnd', function() {
// Do stuff
});

一些注意点:

  • 如果你使用@keyframe动画,那么使用animationEnd事件,而不是transitionEnd。
  • 跟很多事件一样,transitionEnd也向上冒泡。记得在子节点上调用event.stopPropagation()或者检查event.target来防止回调函数在不该被调用的时候被调用。
  • 事件名目前还是被各种供应商添加了不同的前缀(比如webkitTransitionEnd, msTransitionEnd等等)。使用类似于Modernizr的库来获取正确的事件前缀。

animtioniteration

animationiteration事件会在当前的动画元素完成一个动画迭代的时候被触发。这个事件非常有用,特别是当我们想在某个迭代完成后停止一个动画,但又不是在动画过程中打断它。

function start() {
div.classList.add('spin');
} function stop() {
div.addEventListener('animationiteration', callback); function callback() {
div.classList.remove('spin');
div.removeEventListener('animationiteration', callback);
}
}

如果你感兴趣的话,我在博客中有另一篇关于animationiteration事件的文章

error

当我们的应用在加载资源的时候发生了错误,我们很多时候需要去做点什么,尤其当用户处于一个不稳定的网络情况下。Financial Times中,我们使用error事件来监测文章中的某些图片加载失败,从而立刻隐藏它。由于“DOM Leven 3 Event”规定重新定义了error事件不再冒泡,我们可以使用如下的两种方式来处理这个事件。

imageNode.addEventListener('error', function(event) {
image.style.display = 'none';
});

不幸的是,addEventListener并不能处理所有的情况。我的同事Kornel给了我一个很好的例子,说明确保图片加载错误回调函数被执行的唯一方式是使用让人诟病内联事件处理函数(inline event handlers)。

<img src="http://example.com/image.jpg" onerror="this.style.display='none';" />

原因是你不能确定绑定error事件处理函数的代码会在error事件发生之前被执行。而使用内联处理函数意味着在标签被解析并且请求图片的时候,error监听器也将并绑定

大部分内容转自 http://blog.jobbole.com/52430/  原作者:陈 鑫伟

js Event对象的更多相关文章

  1. JS:event对象下的target属性和取消冒泡事件

    1.target 通过获取DOM元素 var box = document.getElementById("box"); document.box.onclick = functi ...

  2. 图解Js event对象offsetX, clientX, pageX, screenX, layerX, x区别

    通过 3 张图和 1 张表格,轻松区别 JavaScript Event 对象中的offsetX, clientX, pageX, screenX, layerX, x等属性. 一.测试代码如下: & ...

  3. Js event对象offsetX,pageX,screenX,clientX详解

       平时在测量元素位置时难以确定,下面给出具体的event对象中的各种属性,以便日后使用.   检测相对于浏览器的位置:clientX和clientY     当鼠标事件发生时,鼠标相对于浏览器左上 ...

  4. JS中的event 对象详解

    JS中的event 对象详解   JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...

  5. 前端程序员的蜕变——JS的 event 对象属性、使用实例、兼容性处理(极大提高代码效率、减少代码量)

    下面讨论一下 js 中的 Event 对象,主要从以下三个方面详细的描述(点击标题可跳转到对应部分): 1.什么是event 2.怎么用event,用他该注意什么,几个简单实际应用 3.event在不 ...

  6. 初始js闭包&事件的冒泡和捕获&EVENT对象

    一.初识闭包 function a(){   var n = 0;   this.inc = function () {     n++;     console.log(n);   }; } var ...

  7. js event事件对象概括

    事件是用户或者浏览器自身执行的动作,而响应某个事件的函数就叫做事件处理程序或者叫事件侦听器. 定义事件处理程序可以大致分为以下三种: 一.html事件处理程序 元素支持的每种事件都可以用一个与之对应的 ...

  8. [JS学习笔记]Event对象

    写在前面 学习和总结JS时会伴随性的生成一些dome,其中包含一些动态输出的结果和标注. 之前通过鸡贼的办法实现了在博客中执行JS,但很多时候需要一张干净的页面编写dome,所以尝试通过一些在线的JS ...

  9. JS的event对象--知识点总结

    Event描述:event代表事件的状态,例如触发event对象的元素.鼠标的位置及状态.按下的键等等. 需要注意的是:event对象只在事件发生的过程中才有效. event的某些属性只对特定的事件有 ...

随机推荐

  1. php 操作 mysql 实现批量执行mysql语句 mysql文件

    <?php /** * 批量运行sql文件 * 正则分隔是重点 preg_split("/;[\r\n]+/", filecontent) */ $config = requ ...

  2. Android内购订单验证 --- php实现

    直接上代码: function googleVerify($sdata,$google_public_key) { $sdata = json_decode($sdata,true); $in_app ...

  3. 【Hadoop/Hive/mapreduce】系列之如何删除HIVE 表格的分区

    今天的一个业务场景就是要把三年的数据从第一天不停的融合起来,每一天作为表格一个新的分区.由于空间有限,数据量很大,可能每天数据都是几十个G的大小.所以我需要做的一点就是在融合这一天之后,删除一天的分区 ...

  4. 重头开始学Qt——day1

    以前学过Qt,但只是为了学分,现在重学. 1. 认识Qt应用程序框架 直接新建一个项目后,自动生成了一下文件,下面一个个理解. (1)main.cpp main.cpp中特别的是QApplicatio ...

  5. 7,vim

    vim与程序员 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主动的以字体 ...

  6. android studio 首字母提示 设置 大小写敏感

    在使用Android studo 编写程序时, 刚开始,关键字提示 首字母 设置了 大小写敏感,小写字母只能提示小写字母开头的,大写字母只能提示大写字母开始的,比较麻烦,在网上搜了下,解决办法如下: ...

  7. NetCore log4net 集成以及配置日志信息不重复显示或者记录

    NetCore log4net 集成,这是一个很常见而且网上大批大批的博文了,我写这个博文主要是为了记录我在使用过程中的一点小收获,以前在使用的过程中一直没有注意但是其实网上说的不清不楚的问题. 官方 ...

  8. Java-JNA使用心得

    自上个月20号,历时整整一个月,终于找到工作入职了. 然后这段时间一直看公司的框架还有业务方面的东西.其实由于给分配了一个研究Java调用C语言接口的问题,导致框架业务方面的东西还不熟,然后现在手上又 ...

  9. 利用split方法计算字符串中出现字母最多的次数

    最近练习一些简单的算法题,知道自己很不聪明,但却没想到用了这么久,划算不划算是个需要考虑的问题, 其中有个算法是:统计一个字符串出现最多的字母,网上很多自己的见解,但是才疏学浅,有些地方看的有点困难, ...

  10. css3 3D

    开通黄钻 Css3 -3D效果<!DOCTYPE html><html lang="en"><head> <meta charset=&q ...