JavaScript与HTML的交互是通过事件进行的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。

  • 事件流

    • 事件捕获
    • 事件冒泡
  • 事件处理程序
  • 事件委托

1. 事件流

如果单机页面上的某个按钮,认为单击事件不仅仅发生在按钮上。火炬话说,在单击按钮的同时,你也单击的按钮的容器元素,甚至也单击了整个页面。

事件流描述的是从页面中接收事件的顺序。有意思的是,IE和Netscape开发团队提出了几乎完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。

1.1 事件冒泡

IE的事件流叫做事件冒泡,即时间开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。以下面的HTML页面为例:

<!DOCTYPE>
<html>
<head>
<title> test </title>
</head>
<body>
<div id="myDiv">click me</div>
</body>
</html>

在这里,如果你单击了页面中的div元素,那么click事件会按照如下顺序传播:

  1. div
  2. body
  3. html
  4. document

如图所示:

所有的现代浏览器都支持事件冒泡,但在具体实现中略有差别.IE5.5及更早版本中事件冒泡会跳过<html>元素(从body直接跳到document)。IE9、Firefox、Chrome、和Safari则将事件一直冒泡到window对象。

1.2 事件捕获

Netscape团队提出的另一种事件流叫做事件捕获。思想是:不大具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预定目标之前捕获它。仍以上面的栗子,单击div元素就会以下列顺序触发click事件。

  1. document
  2. html
  3. body
  4. div

具体如图所示:

事件捕获流在IE9Safari,Chrome,OperaFireFox都得到支持,尽管DOM标准要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。由于老版本浏览器不支持,很少有人使用事件捕获。建议使用事件冒泡

1.3 DOM事件流

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

多数支持 DOM事件流的浏览器都实现了一种特定的行为;即使“DOM2 级事件”规范明确要求捕

获阶段不会涉及事件目标,但 IE9SafariChromeFirefoxOpera 9.5 及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。

注意IE9SafariChromeFirefoxOpera 都支持 DOM 事件流;IE8 及更早版本不支持 DOM 事件流。

2. 事件处理程序

事件就是用户或浏览器自身执行的某种动作。诸如 click 、 load 和 mouseover ,都是事件的名字。

而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。

2.1 HTML事件处理程序

某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。这个

特性的值应该是能够执行的 JavaScript 代码。例如,要在按钮被单击时执行一些 JavaScript,可以像下面这样编写代码:

<input type="button" value="Click Me" onclick="alert('Clicked')" />

当单击这个按钮时,就会显示一个警告框。这个操作是通过指定 onclick 特性并将一些 JavaScript

代码作为它的值来定义的。

2.2 DOM 0级 事件处理程序

通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性,为所有现代浏览器所支持。

每个元素(包括 window 和 document )都有自己的事件处理程序属性,这些属性通常全部小写,

例如 onclick 。将这种属性的值设置为一个函数,就可以指定事件处理程序,如下所示:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
};

在此,我们通过文档对象取得了一个按钮的引用,然后为它指定了 onclick 事件处理程序。但要

注意,在这些代码运行以前不会指定事件处理程序,因此如果这些代码在页面中位于按钮后面,就有可能在一段时间内怎么单击都没有反应。

使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在

元素的作用域中运行;换句话说,程序中的 this 引用当前元素。

2.3 DOM 2级事件处理程序

DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener() removeEventListener()所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true ,表示在捕获阶段调用事件处理程序;如果是 false ,表示在冒泡阶段调用事件处理程序。

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);

上面的代码为一个按钮添加了 onclick 事件处理程序,而且该事件会在冒泡阶段被触发(因为最

后一个参数是 false )。与DOM0级方法一样,这里添加的事件处理程序也是在其依附的元素的作用域中运行。使用 DOM2 级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。

通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移

除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过 addEventListener() 添加的匿名函数将无法移除

注意:IE9、Firefox、Safari、Chrome和 Opera 支持 DOM2 级事件处理程序。

2.4 IE事件处理程序

IE 实现了与 DOM 中类似的两个方法: attachEvent()detachEvent() 。这两个方法接受相同

的两个参数:事件处理程序名称与事件处理程序函数。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent() 添加的事件处理程序都会被添加到冒泡阶段。

要使用 attachEvent() 为按钮添加一个事件处理程序,可以使用以下代码。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});

在 IE 中使用 attachEvent() 与使用 DOM0 级方法的主要区别在于事件处理程序的作用域。在使

DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用 attachEvent() 方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window 。

使用 attachEvent() 添加的事件可以通过 detachEvent() 来移除,条件是必须提供相同的参数。

与 DOM 方法一样,这也意味着添加的匿名函数将不能被移除。不过,只要能够将对相同函数的引用传给 detachEvent() ,就可以移除相应的事件处理程序。

2.5 跨浏览器的事件处理程序

为了以跨浏览器的方式处理事件,不少开发人员会使用能够隔离浏览器差异的 JavaScript 库,还有

一些开发人员会自己开发最合适的事件处理的方法。自己编写代码其实也不难,只要恰当地使用能力检测即可。要保证处理事件的代码能在大多数浏览器下一致地运行,只需关注冒泡阶段。

第一个要创建的方法是 addHandler() ,它的职责是视情况分别使用 DOM0 级方法、DOM2 级方

法或 IE 方法来添加事件。这个方法属于一个名叫 EventUtil 的对象,本书将使用这个对象来处理浏览器间的差异。 addHandler() 方法接受 3 个参数:要操作的元素、事件名称和事件处理程序函数。

addHandler() 对应的方法是 removeHandler() ,它也接受相同的参数。这个方法的职责是移

除之前添加的事件处理程序——无论该事件处理程序是采取什么方式添加到元素中的,如果其他方法无效,默认采用 DOM0 级方法。

var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
};

addHandler() removeHandler() 没有考虑到所有的浏览器问题,例如在 IE 中的作用域问题。

不过,使用它们添加和移除事件处理程序还是足够了。此外还要注意,DOM0 级对每个事件只支持一个事件处理程序。

3. 事件委托

3.1 什么是事件委托?

事件委托,也称事件代理(Event Delegation),是JS中常用绑定事件的技巧。事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

3.2 为什么要使用事件委托?

众所周知,浏览器的内存是有限的,如果很多的dom需要添加事件处理,直接给相应dom设事件处理程序的话,十分影响性能。

可以想象,如果有100个li,每个li都有相同的click事件,首先我们需要使用for循环访问所有li元素,然后给他们添加事件,这样,会使得访问dom的次数增加,其次,每个li元素一个函数,占用内存也会相当大。每一个函数都是对象,对象越多占用内存越大,性能越差。同时,访问dom次数的越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能。

3.3 事件委托原理

事件委托是利用事件的冒泡原理来实现,将事件加到 父元素 或 祖先元素上,触发执行效果。

3.4 事件委托实现案例

如下所示,需要实现点击li元素,弹出123:

<ul id="list">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>

一般方法:

window.onload = function(){
var list = document.getElementById("list");
var lists = list.getElementsByTagName('li');
for(var i=0;i<lists.length;i++){
lists[i].onclick = function(){
alert(123);
}
}
}

事件委托

window.onload = function(){
var list = document.getElementById("list");
list.onclick = function() {
alert(123);
}
}

5. 小结

事件是将Javascript与网页联系在一起的主要方式。为了将JS事件进行梳理,本文一一阐述了事件冒泡,事件捕获,DOM事件流,循序渐进的将事件流这一脉络梳理;然后,将事件处理程序包括HTML事件处理程序、DOM0级事件处理程序、IE事件处理程序、DOM2级事件处理程序等一一列举,并给出了跨浏览器事件处理方案;最后,讲述了事件委托这一JS中常用技巧,并列举了使用这一技术的好处。希望在阅读本文后,读者对于JS事件有一个清晰的认知。

一文梳理JS事件的更多相关文章

  1. 一文梳理JavaScript 事件循环(Event Loop)

    事件循环(Event Loop),是每个JS开发者都会接触到的概念,但是刚接触时可能会存在各种疑惑. 众所周知,JS是单线程的,即同一时间只能运行一个任务.一般情况下这不会引发问题,但是如果我们有一个 ...

  2. 一文梳理JavaScript中的this

    最近零零碎碎看了许多关于this的文章,本着"好记性不如烂笔头"的思想,特在这里整理一下this有关的知识点.[长文警告!!!] 接下来,笔者将按照以下目录对this进行阐述: t ...

  3. js事件技巧方法整合

    window.resizeTo(800,600); //js设置浏览器窗口尺寸 window.open (function(){ resizeTo(640,480);//设置浏览器窗口尺寸 moveT ...

  4. js 事件大全

    Js事件大全一般事件 事件 浏览器支持 描述onClick IE3|N2|O3 鼠标点击事件,多用在某个对象控制的范围内的鼠标点击onDblClick IE4|N4|O 鼠标双击事件onMouseDo ...

  5. 原生JS事件绑定方法以及jQuery绑定事件方法bind、live、on、delegate的区别

    一.原生JS事件绑定方法: 1.通过HTML属性进行事件处理函数的绑定如: <a href="#" onclick="f()"> 2.通过JavaS ...

  6. JS事件

    JS事件:  声明:为了事件对象event跨浏览器兼容: var oEvent==ev||event;      所以在下面用到 event 的地方都用 oEvent 代替  1)doucument的 ...

  7. JS事件(事件冒泡和事件捕获)

    事件流:描述的是在页面中接收事件的顺序 事件冒泡:由最具体的元素接收,然后逐级向上传播至最不具体的元素的节点(文档) 事件捕获:最不具体的节点先接收事件,而最具体的节点应该是最后接收事件 DOM中:用 ...

  8. js事件监听机制(事件捕获)总结

    在前端开发过程中我们经常会遇到给页面元素添加事件的问题,添加事件的js方法也很多,有直接加到页面结构上的,有使用一些js事件监听的方法,由于各个浏览器对事件冒泡事件监听的机制不同,le浏览器只有事件冒 ...

  9. js事件冒泡和捕捉

    (1)冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发. IE 5.5: div -> body -> document IE 6.0: div ...

随机推荐

  1. vue项目中关闭eslint的方法

    非常简单的操作方法!不用再去为了烦人的代码标准报错而苦恼了! 方法一:在项目根目录下增加 vue.config.js 文件 添加以下代码: module.exports = { lintOnSave: ...

  2. 当.Net成为大厂门槛代码小白该何去何从?

    掌握.Net已成为进入大厂的通行牌.越来越多的互联网软件公司开始使用.Net Core,根据去年数据显示腾讯.网易.顺丰.携程.中通.申通.同程艺龙.微医.233网校.问卷星.金蝶等关键业务已经在往. ...

  3. FPGA开发工具套餐搭配推荐及软件链接 (更新于2020.03.16)

    一.Xilinx(全球FPGA市场份额最大的公司,其发展动态往往也代表着整个FPGA行业的动态) (1) Xilinx官方软件下载地址链接: https://china.xilinx.com/supp ...

  4. Hive 集成 Hudi 实践(含代码)| 可能是全网最详细的数据湖系列

    公众号后台越来越多人问关于数据湖相关的内容,看来大家对新技术还是很感兴趣的.关于数据湖的资料网络上还是比较少的,特别是实践系列,对于新技术来说,基础的入门文档还是很有必要的,所以这一篇希望能够帮助到想 ...

  5. jchdl - GSL实例 - Mux4(使用Mux)

    https://mp.weixin.qq.com/s/GrYJ4KXEFRoLLmLnAGoMSA 原理图 ​​ 参考链接 https://github.com/wjcdx/jchdl/blob/ma ...

  6. 【Hadoop】namenode与secondarynamenode的checkpoint合并元数据

    Checkpoint Node(检查点节点) NameNode persists its namespace using two files: fsimage, which is the latest ...

  7. 重装ArchLinux后修改GRUB配置不生效问题的解决

    重装ArchLinux后修改GRUB配置不生效问题的解决 mount指令看一下挂载,或者vim /etc/fstab看一下有没有/boot,看看fstab是不是没写进去.... 我特喵昨天重装完Arc ...

  8. Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)

    833. 字符串中的查找与替换 对于某些字符串 S,我们将执行一些替换操作,用新的字母组替换原有的字母组(不一定大小相同). 每个替换操作具有 3 个参数:起始索引 i,源字 x 和目标字 y.规则是 ...

  9. Java实现 蓝桥杯VIP 算法训练 统计单词个数

    题目描述 给出一个长度不超过200的由小写英文字母组 成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个).要求将此字母串分成k份 (1< k< =40),且每份中 ...

  10. Java实现N*N矩阵旋转(360度)

    N*N矩阵旋转 Description 给你一个n*n的矩阵,你的任务是将它逆时针旋转角度d. [输入] 输入的第一个数为T,表示接下来有T组数据. 每组数据的格式如下: 第一行为两个整数n,d.1& ...