DOM事件的工作原理

导读:
本文是teren对DOM事件知识点所做的进一步整理,整理资料主要参考DOM事件简介饥人谷课件,如果对DOM事件有什么不了解的地方,可以直接参考原文出处。
这篇笔记的目的有两个,一是作为自己整理的资料方便日后查阅,毕竟自己整理的资料用起来更加得心应手,思路更加契合自己;二是希望通过在撰写笔记过程中强化记忆;
如果这篇文章有什么能够帮助到各位读者,我要万分感谢原文的作者以及原文的翻译!!!
最后,这篇文章的整理结构如目录所示。

目录
1.预备知识
监听事件
移除事件
2.事件阶段
3.event对象
4.事件的操作
停止事件传播
阻止浏览器的默认行为
自定义事件
代理事件监听
5.FQA
DOM 0事件和DOM 2事件
事件流的三种模型
IE兼容性


1. 预备知识

监听事件

在为节点添加事件时,推荐使用addEventListener接口(IE的使用attachEvent接口)

node.addEventListener(eventname,callback[,useCapture])
  • eventname:监听事件的名称,如
    click/mouseover/mouseout/mousedown/mouseup/load/unload等事件

  • callback:事件触发时被调用的函数,此时会自动产生一个event对象,作为第一个参数传入callback

var ul = document.querySelector('ul')
ul.addEventListener('click',function(event){console.log(event.target)})

关于event对象后面详细讲解

  • useCapture:决定回调函数(callback)是否在“捕获(capture)”阶段被触发,,默认是false,即在冒泡阶段被触发
    关于事件阶段后面详解,现在给个范例演示,可以看完事件阶段章节后再“回调”查看(~ ̄▽ ̄)~
    demo: usecapture
移除事件

移除事件使用removeEventListener接口

node.removeEventListener(eventname,callback)

值的注意的是:
通过addEventListener添加的事件处理程序只能通过removeEventListener移除,移除时参数与添加的时候相同;
这也就意味着:在移除事件时回调函数不能为匿名函数,因为匿名函数虽然方法体一样,但是句柄(可以理解为函数名)却不相同
也就是说当初定义回调函数必须以以下形成出现

var ul  = document.querySelector('ul')
//method 1
function printHello(){
  alert('hello world')
}
//method 2
handler = function (){
  alert('hello world 2')
}
ul.addEventListener('click',printHello);
ul.addEventListener('click',handler);
ul.addEventListener('click',function(){alert('hello world 3')})
ul.removeEventListener('click',printHello);
//此时你要移除第3个hello world,那么你无法码下去
//ul.removeEventListener('click',)

demo : removeEvent

2.事件阶段

以一个例子去描述事件阶段

  <ul>
    <li id="demo1">demo1</li>
    <li id="demo2">demo2</li>
    <li id="demo3">demo3</li>
  </ul>
var demo2 = document.querySelector('#demo2')
demo2.addEventListener('click',callback)
function callback(){
  console.log('demo')
}

给li#demo2节点添加click事件,在点击li#demo2节点时,点击事件不是直接在该节点直接发生,而是分为三个事件阶段:

  • click事件从html文档的根节点window流向目标节点li#demo2(捕获阶段)
  • 然后在目标节点上click事件触发(目标阶段)
  • 最后再返回到文档的根节点(冒泡阶段)


    事件阶段

demo:event phases
小结:
事件触发的整个过程可分为三个阶段:

  • 捕获阶段
    事件的第一个阶段是捕获阶段。事件从文档的根节点出发,随着DOM树的结构向事件的目标节点流去。途中经过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达事件的目标节点。

类似水流一样,从源头流向目的地

  • 目标阶段
    当事件到达目标节点的,事件就进入了目标阶段。事件在目标节点上被触发,然后会逆向回流,直到传播至最外层的文档节点
    【注】
    或许有人会疑问?事件在目标节点被触发,那么设置usecapture还有什么用处呢?
    我的理解是:
    你为节点设置事件是一回事,你触发事件时是另一回事;
    节点的事件触发时点可分为上述三个阶段,在乎你怎么设置
    下面代码中,li#demo2一定是在目标阶段被触发,而ul则在乎你的设置,下例设置为捕获阶段被触发
var ul = document.querySelector('ul')
var demo2  = document.querySelector('#demo2')
function printUl(){alert('Ul')}
function printList(){
  alert('List')
}
//当点击li#demo2时,到了目标阶段触发li#demo2的click事件
//当你为ul的usecapture设置true时,意味着ul在捕获阶段触发click事件
ul.addEventListener('click',printUl,true)
demo2.addEventListener('click',printList)

demo:how to recognize event phases

  • 冒泡阶段
    事件在目标元素上触发后,并不在这个元素上终止。它会随着DOM树一层层向上冒泡,直到到达最外层的根节点

3.event对象

event对象是在事件第一次触发时候被创建,并且一直伴随着事件在DOM结构中流转的整个生命周期。
event对象会被作为第一个参数传递给事件监听的回调函数。
event对象中包含大量当前事件相关的信息:

属性/方法 备注
type 事件名称
target 事件的目标节点
currentTarget 事件触发时的当前节点
bubbles 判断节点的事件是否是在冒泡阶段捕获
preventDefault(function) 阻止浏览器中用户代理对当前事件的相关默认行为被触发。比如阻止a元素的click事件加载一个新的页面
cancelable(boolean) 指明这个事件的默认行为是否可以通过调用event.preventDefault来阻止,即只有cancelable为true的时候,调用event.preventDefault才能生效
stopPropagation(function) 阻止当前事件流上后面的元素的回调函数被触发,当前节点上针对此事件的其他回调函数依然会被触发
stopImmediatePropagation(function) 阻止当前事件流上后面所有的回调函数被触发,也包括当前节点上针对此事件已绑定的其他回调函数
eventPhase(number) 表示当前这个事件所处的阶段(phase):none(0), capture(1),target(2),bubbling(3)
timeStamp(number) 事件发生的时间

下面将一些简单的属性放在下面的示例中,复杂的方法将在事件操作章节单独罗列
demo : event simple property

4.事件的操作

停止事件传播

通过调用事件对象的stopPropagation方法,在任何阶段(捕获阶段或者冒泡阶段)中断事件的传播;
此后,事件不会在后面传播过程中的经过的节点上调用任何的监听函数;
demo:stopPropagation
但event.stopPropagation()不会阻止当前节点上此事件其他的监听函数被调用。如果你希望阻止当前节点上的其他回调函数被调用的话,你可以使用更激进的event.stopImmediatePropagation()
方法;
demo:stopImmediatePropagation

阻止浏览器的默认行为

当特定事件发生的时候,浏览器会有一些默认的行为作为反应。例如,使用a元素时会自动添加click事件,当a元素上click事件触发时,它会向上冒泡直到DOM结构的最外层document,浏览器会解释href属性,并且在窗口中加载新地址的内容。
如果我们需要阻止浏览器针对点击事件的默认行为,可以调用event.preventDefault()
demo:preventDefault

自定义事件

【注】
知道有这么一回事,这篇不详讲。

代理事件监听

所谓代理事件监听,指的是不直接在监听的目标节点上添加事件监听函数,而是通过其他的节点代为监听目标节点的事件;

举个例子:
如果有一个列表ul包含了100个子元素li,它们都需要对click事件做出相似的响应,那么我们可能需要查询这100个子元素,并分别为他们添加上事件监听器。这样的话,我们就会产生100个独立的事件监听器

代理事件监听可以让我们更简单的处理这种情况。我们不去监听所有的子元素的click事件,相反,我们监听他们的父元素ul。当一个li元素被点击的时候,这个事件会向上冒泡至ul,触发回调函数。我们可以通过检查事件的event.target属性来判断具体是哪一个li被点击了。
这样一来,仅仅使用了一个上层的事件监听器,并且我们不需要在为添加元素而考虑它的事件监听问题
demo:事件代理
但是在实际代理事件监听中,我们往往使用jQuery提供的on()方法去实现事件代理
demo:事件代理-on()方法

5.FQA

DOM 0级事件处理程序和DOM 2级事件处理程序

首先,了解一下DOM的分级。
DOM是HTML与XML的应用编程接口(API),DOM将整个页面映射为一个由层次节点组成的文件,有1级、2级、3级共3个级别。
1级DOM
1级DOM,由DOM核心与DOM HTML两个模块组成。
DOM核心能映射以XML为基础的文档结构,允许获取和操作文档的任意部分。
DOM HTML通过添加HTML专用的对象与函数对DOM核心进行了扩展。
2级DOM
鉴于1级DOM仅以映射文档结构为目标,DOM 2级面向更为宽广。通过对原有DOM的扩展,2级DOM通过对象接口增加了:
DOM视图:描述跟踪一个文档的各种视图(使用CSS样式设计文档前后)的接口;
DOM事件:描述事件接口;
DOM样式:描述处理基于CSS样式的接口;
DOM遍历与范围:描述遍历和操作文档树的接口;
3级DOM
3级DOM通过引入统一方式载入和保存文档和文档验证方法对DOM进行进一步扩展
"0级"DOM
需要注意的是并没有标准被称为0级DOM,它仅是DOM历史上一个参考点(0级DOM被认为是在Internet Explorer 4.0 与Netscape Navigator4.0支持的最早的DHTML)

也就是说:
DOM 0级事件处理程序是 通过javascript制定事件处理程序的传统方式,具体实现方式是:

var btn = document.getElementById("btn");            
btn.onclick = function(){                
alert(this.id);//this指定当前元素btn  
}
删除DOM0事件处理程序,
只要将对应事件属性置为null即可。btn.onclick = null;

DOM 0级事件处理程序的优点是简单且具有跨浏览器的优势,缺点是一个事件处理程序只能对应一个处理函数

DOM2级事件处理程序是在2级DOM中规定的API,通过addEventListener(IE为attachEvent)去监听事件,具体实现方式是:

var btn = document.getElementById("btn");
function handler(){
  alert(this.id)//this指定当前元素btn
}
btn.addEventListener('click',handler)

demo:addEventListener
同时制定了删除事件处理程序的方法
removeEventListener(IE为detachEvent),关于removeEventListener的注意事项请详见上文移除事件章节;
至于attachEvent与addEventListener的区别详见后文IE兼容性
addEventListener的优点是一个事件处理程序能对应多个处理函数,缺点是存在兼容性问题。

事件流的三种模型

所谓事件流,指的是页面捕获事件的顺序,目前有三种模型:

  • IE的事件冒泡:当发生事件时,目标节点先捕获,然后逐级向上传播到父节点,即事件监听处于冒泡阶段

  • Netscape的事件捕获:当发生事件时,最先触发父节点的事件监听函数,然后逐渐向下传播到目标节点,即事件监听处于捕获阶段

  • 2级DOM规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段

IE兼容性

IE并不支持addEventListener和removeEventListener方法,而是实现了两个类似的方法:
attachEvent(eventname,callback)
detachEvent(eventname,callback)
由于IE指支持事件冒泡,所以添加的程序会被添加到冒泡阶段。
【注意】
IE的事件监听的方法与addEventListener方法不同之处包括:
eventname必须包含on以及没有usecapture;
同时,使用attachEvent方法和addEventListener主要区别在于事件处理程序的作用域。采用addEventListener,事件处理程序会在其所属元素的作用域内运行。使用attachEvent,事件处理程序会在全局作用域内运行,因此this等于window。

var btn = document.getElementById("btn");
function handler(){
  alert(this.id)//this指定window
}
btn.attachEvent('onclick',handler)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信。DOM支持大量的事件,本节介绍DOM的事件编程。...
    周花花啊阅读 592评论 0 3
  • 声明:本文来源于http://www.webzsky.com/?p=731我只是在这里作为自己的学习笔记整理一下(...
    angryyan阅读 6,940评论 1 6
  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 3,022评论 1 10
  • 事件绑定的方式 给 DOM 元素绑定事件分为两大类:在 html 中直接绑定 和 在 JavaScript 中绑定...
    Bruce_zhuan阅读 1,025评论 0 6
  • 可能自己太爱自己女朋友琪宝宝了,每天不敢接近她,因为我知道自己不够优秀,我很自卑自己不能让她炫耀自己男朋友多么厉害...
    维小凯阅读 293评论 0 0