addEventListener

addEventListener-开始

前面零散地写了些关于 addEventListener 的内容,觉得比较散,有些地方可能也说得不够清楚明白,所以决定以连载的形式从头到尾再写一篇。

addEventListener 用于注册事件处理程序,IE 中为 attachEvent,我们为什么讲 addEventListener 而不讲 attachEvent 呢?一来 attachEvent 比较简单,二来 addEventListener 才是 DOM 中的标准内容。

简介

addEventListener 为文档节点、document、window 或 XMLHttpRequest 注册事件处理程序,在以前我们一般是 <input type="button" onclick="...",或 document.getElementById("testButton").onclick = FuncName, 而在 DOM 中,我们用 addEventListener(IE 中用 attachEvent)。

语法

target.addEventListener(type, listener, useCapture);
  • target 文档节点、document、window 或 XMLHttpRequest。
  • type 字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。
  • listener 实现了 EventListener 接口或者是 JavaScript 中的函数。
  • useCapture 是否使用捕捉,看了后面的事件流一节后就明白了,一般用 false。

示例

function Go()
{
    //...
}

document.getElementById("testButton").addEventListener("click", Go, false);

或者 listener 直接就是函数

document.getElementById("testButton").addEventListener("click", function () { ... }, false);

addEventListener-事件流

说到 addEventListener 不得不说到事件流,先说事件流对后面的解释比较方便。

当一个事件发生时,分为三个阶段:

捕获阶段 从根节点开始顺序而下,检测每个节点是否注册了事件处理程序。如果注册了事件处理程序,并且 useCapture 为 true,则调用该事件处理程序。(IE 中无此阶段。)

目标阶段 触发在目标对象本身注册的事件处理程序,也称正常事件派发阶段。

冒泡阶段 从目标节点到根节点,检测每个节点是否注册了事件处理程序,如果注册了事件处理程序,并且 useCapture 为 false,则调用该事件处理程序。

举例

<div id="div1">
  <div id="div2">
    <div id="div3">
      <div id="div4">
      </div>
    </div>
  </div>
</div>

如果在 d3 上点击鼠标,事件流是这样的:

捕获阶段 在 div1 处检测是否有 useCapture 为 true 的事件处理程序,若有,则执行该程序,然后再同样地处理 div2。

目标阶段 在 div3 处,发现 div3 就是鼠标点击的节点,所以这里为目标阶段,若有事件处理程序,则执行该程序,这里不论 useCapture 为 true 还是 false。

冒泡阶段 在 div2 处检测是否有 useCapture 为 false 的事件处理程序,若有,则执行该程序,然后再同样地处理 div1。

注意,上述捕获阶段和冒泡阶段中,实际上 div1 之上还应该有结点,比如有 body,但这里不讨论。

addEventListener-第三个参数 useCapture

addEventListener 有三个参数:第一个参数表示事件名称(不含 on,如 "click");第二个参数表示要接收事件处理的函数;第三个参数为 useCapture,本文就讲解它。

<div id="outDiv">
  <div id="middleDiv">
    <div id="inDiv">请在此点击鼠标。</div>
  </div>
</div>

<div id="info"></div>

var outDiv = document.getElementById("outDiv");
var middleDiv = document.getElementById("middleDiv");
var inDiv = document.getElementById("inDiv");
var info = document.getElementById("info");
 
outDiv.addEventListener("click", function () { info.innerHTML += "outDiv" + "<br>"; }, false);
middleDiv.addEventListener("click", function () { info.innerHTML += "middleDiv" + "<br>"; }, false);
inDiv.addEventListener("click", function () { info.innerHTML += "inDiv" + "<br>"; }, false);

上述是我们测试的代码,根据 info 的显示来确定触发的顺序,有三个 addEventListener,而 useCapture 可选值为 true 和 false,所以 2*2*2,可以得出 8 段不同的程序。

  • 全为 false 时,触发顺序为:inDiv、middleDiv、outDiv;
  • 全为 true 时,触发顺序为:outDiv、middleDiv、inDiv;
  • outDiv 为 true,其他为 false 时,触发顺序为:outDiv、inDiv、middleDiv;
  • middleDiv 为 true,其他为 false 时,触发顺序为:middleDiv、inDiv、outDiv;
  • ……

最终得出如下结论:

  • true 的触发顺序总是在 false 之前;
  • 如果多个均为 true,则外层的触发先于内层;
  • 如果多个均为 false,则内层的触发先于外层。

下面提供全部代码,您可以更改其中的 true、false 值,来进行测试。注意,不适用于 IE。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<meta http-equiv="Content-Language" content="zh-cn" />
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>useCapture</title>
<style type="text/css">
#outDiv
{
    padding:10px 10px 10px 10px;
    border:1px solid red;
}

#middleDiv
{
    padding:10px 10px 10px 10px;
    border:1px solid green;
}

#inDiv
{
    padding:10px 10px 10px 10px;
    border:1px solid blue;
}
</style>
</head>

<body>

<div id="outDiv">
  <div id="middleDiv">
    <div id="inDiv">请在此点击鼠标。</div>
  </div>
</div>

<div id="info"></div>

<script language="javascript" type="text/javascript">
<!--

var outDiv = document.getElementById("outDiv");
var middleDiv = document.getElementById("middleDiv");
var inDiv = document.getElementById("inDiv");
var info = document.getElementById("info");
 
outDiv.addEventListener("click", function () { info.innerHTML += "outDiv" + "<br>"; }, false);
middleDiv.addEventListener("click", function () { info.innerHTML += "middleDiv" + "<br>"; }, false);
inDiv.addEventListener("click", function () { info.innerHTML += "inDiv" + "<br>"; }, false);
//-->
</script>

</body>

</html>

addEventListener-event 对象的属性和方法

事件触发时,会将一个 Event 对象传递给事件处理程序,比如:

document.getElementById("testText").addEventListener("keydown", function (event) { alert(event.keyCode); }, false);

事件类型

DOM 事件类型是分为 UIEvent、UIEvent:KeyEvent、UIEvent:MouseEvent,不同的事件有不同的属性和方法,但常用的来说我们都不会用错,比如我们不会在鼠标事件中去取键盘值(Ctrl、Alt、Shift 除外),所以我们没有必要深究。

该对象的属性和方法有:

view 只读,对象,发生事件的 Window 对象。

type 只读,字符串。比如鼠标点击事件的类型:click。

eventPhase 只读,数字,事件流正经历的阶段。1-捕获,2-目标,3-冒泡。

target 只读,对象,派发事件的目标对象。比如鼠标是点击在哪个按钮上的。

currentTarget 只读,对象,当前正在调用监听器的对象,也就是当前 addEventListener 是绑定在哪个对象上的。

timeStamp 只读,数字,用毫秒表示事件发生时距计算机开机的时间。


cancelable 只读,布尔,处理事件的默认行为是否可以停止。主要针对一些系统事件,如果值为 true,则 event 的 preventDefault 方法可以使用,否则不可用。

preventDefault() 阻止浏览器的默认行为,比如在文本框中打字触发 keydown,如果 keydown 事件处理程序中调用了 preventDefault(),所打的字就不会跑到文本框中去,注意,此时不要弹出 alert 对话框,否则可能不起作用。IE 中在事件处理程序中用 return false 实现类似功能。


bubbles 只读,布尔,事件是否开启冒泡功能。

stopImmediatePropagation 这个东西在 JavaScript 中是个属性,而不是方法,布尔,但具体测试并未发现其用途,不知是不是 bug。

stopPropagation() 停止当前的事件流传播,但不会停止当前正在处理的对象。IE 中用 event.cancelBubble =  true 实现类似功能。

cancelBubble 布尔,是否取消冒泡,不建议使用,用 stopPropagation() 代替。

preventBubble() 阻止冒泡,不建议使用,用 stopPropagation() 代替。

preventCapture() 阻止捕获,不建议使用,用 stopPropagation() 代替。


detail 只读,数字,提供时间的额外信息,对于 click 事件、mousedown 事件和 mouseup 事件,这个字段代表点击的次数。

isChar 只读,布尔,按下的按键值是否是字符,比如按下 Ctrl 键时,就返回 false。不过您在 Firefox 中测试时,该值总是 false,Firefox 官方已经说明这是一个 bug。

altKey 只读,布尔,是否按下了 Alt 键。

ctrlKey 只读,布尔,是否按下了 Ctrl 键。

shiftKey 只读,布尔,是否按下了 Shift 键。

metaKey 只读,布尔,是否按下了 Meta 键。


下面一些属性很有意思,请仔细区别。

charCode 只读,数字,字符(英文、数字、符号)的 Unicode 值。

  • 只用于 keypress。

keyCode 只读,数字,键盘按键值。

  • 用于 keypress 时:返回非字符按键值(除 Ctrl、Shift、Alt、Caps Lock、单行文本框中按向上键等);
  • 用于 keydown、keyup 时:返回任意键值。

button 只读,数字,鼠标按键值。

  • 用于 click 时:0-左键。
  • 用于 mousedown、mouseup 时:0-左键,1-中间键(滚轮),2-右键。

which 只读,数字,键盘按键值或鼠标按键值。

  • 用于 keypress 时:等同于 charCode + 回退键 + 回车键;
  • 用于 keydown、keyup 时:返回任意键值;
  • 用于 click 时:1-左键,与 button 的值略有区别。
  • 用于 mousedown、mouseup 时:1-左键,2-中间键(滚轮),3-右键,与 button 的值略有区别。

可以看出,which 只有一点没有包括:那就是 keypress 时,不如 keyCode 那么全,但实际上,keypress 事件中用于非字符键的情况较少,所以一般还是用 which 代替全部。

addEventListener-有用的笔记

为什么用 addEventListener

  • 可以对同一物件的同一事件绑定多个事件处理程序。
  • 可以通过事件流三个阶段更好地控制何时触发事件处理程序。
  • 工作于 DOM 元素,而不仅是 HTML 元素。

事件分发时添加 eventListener

不会立即触发 eventListener,可能会在下一个事件流(比如冒泡阶段)中触发。

多个相同的 eventListener

如下,三个参数完全相同,并且第二个参数不是匿名函数。

document.getElementById("myBox").addEventListener("click", Go, false);
document.getElementById("myBox").addEventListener("click", Go, false);
document.getElementById("myBox").addEventListener("click", Go, false);

会抛弃多余的,只保留一个,对应的 removeEventListener 也只用一次就可以了(removeEventListener 用法和 addEventListener 完全相同)。

但如果是第二个参数是匿名函数,比如:

document.getElementById("outDiv").addEventListener("click", function () { document.getElementById("info").innerHTML += "1";}, false);
document.getElementById("outDiv").addEventListener("click", function () { document.getElementById("info").innerHTML += "1";}, false);
document.getElementById("outDiv").addEventListener("click", function () { document.getElementById("info").innerHTML += "1";}, false);

则三个均有效,并且无法用 removeEventListener 除去。

this

事件处理程序中,this 变成了触发事件的控件,但我们仍推荐用 event.target 或 event.currentTarget。

早期的事件监听

在 DOM0 中,我们用 obj.onclick = FuncName,由于兼容性好,应用非常广泛,只是功能不如 addEventListener 强大。

内存问题

前面提到了许多使用域名函数的地方,有时这是没办法的,请参见在各浏览器中动态添加事件-参数篇,但这会导致内存问题。

一旦事件绑定之后,该绑定代码作用域的变量就都保留下来,不会被 JavaScript 引擎回收,这可能会引起占用大量内存的问题,由于 removeEventListener 无法删除匿名函数的事件处理程序,只有在物件(比如按钮)去除之后,该内存才可能得到回收。

addEventListener的更多相关文章

  1. Dom addEventlistener与id 绑定事件的区别(续)

    addEventListener 第三个参数为 useCapture. 以一个例子说明. <div id="div1" style="background: blu ...

  2. dom addeventlistener与id 绑定事件的区别

    文档中有写. //addEventListener() 方法用于向指定元素添加事件句柄. //提示: 使用 removeEventListener() 方法来移除 addEventListener() ...

  3. 兼容8事件绑定与解绑addEventListener、removeEventListener和ie的attachEvent、detachEvent

    兼容8事件绑定与解绑addEventListener.removeEventListener和ie的attachEvent.detachEvent   ;(function(){ // 事件绑定 bi ...

  4. addEventListener详解

    为什么需要addEventListener? 先来看一个片段: html代码 <div id="box">追梦子</div> 用on的代码 window.o ...

  5. Javascript中addEventListener和attachEvent的区别

    在利用javascript为DOM Element添加事件处理程序时,如果要想下兼容IE6,7时,就不得不考虑addEventListener与attachEvent的异同. 1.首先说下addEve ...

  6. addEventListener和attachEvent的区别

    addEventListener共有3个参数,如下所示:element.addEventListener(type,listener,useCapture); 参数 参数说明 element 要绑定事 ...

  7. addEventListener 的另类写法

    addEventListener 参数如下 addEventListener(type, listener[, useCapture]); type,事件名称 listener,事件处理器 useCa ...

  8. [No00006A]Js的addEventListener()及attachEvent()区别分析【js中的事件监听】

    1.添加时间监听: Chrom中: addEventListener的使用方式: target.addEventListener(type, listener, useCapture); target ...

  9. js中addEventListener中第3个参数

    addEventListener中的第三个参 数是useCapture, 一个bool类型.当为false时为冒泡获取(由里向外),true为capture方式(由外向里). <div id=& ...

  10. 浅谈 原生javaScript&&react 实现全局触摸按钮(附带对addeventlistener的了解)

    1.采用原生javaACript 实现全局触摸按钮 首先在控制台输出,观察事件有哪些关于触摸的字段可以使用,然后拿这些字段的数据开始来写方法. 因为要做的是全局触摸按钮,我需要拿到的是按钮时时的坐标位 ...

随机推荐

  1. C#中调用c++的dll具体创建与调用步骤,亲测有效~

    使用的工具是VS2010哦~其他工具暂时还没试过 我新建的工程名是my21dll,所以会生成2个同名文件.接下来需要改动的只有画横线的部分 下面是my21dll.h里面的... 下面的1是自动生成的不 ...

  2. C# 使用WinRar命令压缩和解压缩

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  3. HDU 1541 Stars (树状数组)

    Problem Description Astronomers often examine star maps where stars are represented by points on a p ...

  4. dom 规划(html和xml)

    html dom与xml dom关联: 什么是 DOM? DOM 是 W3C(万维网联盟)的标准. DOM 定义了訪问 HTML 和 XML 文档的标准: "W3C 文档对象模型 (DOM) ...

  5. JavaEE(15) - JPA实体继承

    1. 实体继承映射的三种策略 #1. 整个类层次对应一张表 #2. 连接子类 #3. 每个具体类对应一张表 2. 使用抽象实体 3. 使用非实体父类 4. 重定义子类实体的外键列 ---------- ...

  6. Pro Aspnet MVC 4读书笔记(1) - Your First MVC Application

    Listing 2-1. The default contents of the HomeController class using System; using System.Collections ...

  7. 使用Xcode和Instruments调试解决iOS内存泄漏

    尽管iOS 5.0加入版本号之后ARC机制,由于相互引用关系是复杂的.内存泄漏可能仍然存在.于是,懂原理是非常重要的. 这里讲述在没有ARC的情况下,怎样使用Instruments来查找程序中的内存泄 ...

  8. 怎样批量把excel中已显示的科学计数法取消

    作者:iamlaosong 把一文本文档拷贝到EXCEL中时,当中一列数字所有变成科学计数法,这些数事实上是条码号,不需进行运算,怎样能够取消科学计算法,将数字显示成原来的样子呢?一般方法例如以下: ...

  9. IBM Java架构师的技能

    一天,群里飘过一个IBM招聘信息.我看过之后,也只是如此而已. 大家好!我是XXX,IBM招聘java架构师,如今还有38个名额 学历大专以上即可,英语能面试交流的.项目有非常多到时候依据您面试会详谈 ...

  10. Matrix (二维树状数组)

    Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the ...