浅谈DOM的事件绑定

事件绑定的方式

给 DOM 元素绑定事件分为两大类:在 html 中直接绑定 和 在 JavaScript 中绑定。

Bind in HTML

在 HTML 中绑定事件叫做内联绑定事件,HTML 的元素中有如onclick这样的on***属性,它可以给这个 DOM 元素绑定一个类型的事件,主要是这样的:

CLICK ME

这里的***有两种形式:

用字符串表示一段函数:

CLICK ME

点击可以发现,console:

1

​CLICK ME​

var a = 1; console.log(a); console.log(this);这一段字符串被当作 js 执行了,同时this指向当前这个点击的元素。

用函数名表示:

CLICK ME

这里需要添加script去定义函数:

functionfoo(_this) {

console.log(_this);

console.log(this);

}

可以观察到,console:

​CLICK ME​

window

这里的this指向了全局,传入的参数是点击的 DOM 元素,其实和第一种方式是一样的,都是在onclick=后面指向了一段js的字符串,不同的是在这个字符串中是执行了一个函数名,而这个函数我们在全局中定义了,所以点击的时候可以执行,然后传入的参数this也就是一样的道理了。

或者多说一句,这里的字符串才是真正赋值给onclick的函数,这里我们是在函数里面再执行了foo函数。

然而这内联的方式绑定时间不利于分离,所以一般我们不推荐这种做法,所以也就不再多阐述了

Bind in JavaScript

dom.onclick

先上栗子:

CLICK ME

document.getElementById('clickme').onclick =function (e) {

console.log(this.id);

console.log(e);

};

观察 console:

clickme

MouseEvent {isTrusted: true,screenX:65,screenY:87,clientX:65,clientY:13…}

首先,我们获取到了dom元素,然后给它的onclick属性赋值了一个函数;

点击dom我们发现那个函数执行了,同时发现函数中的this是指向当前的这个dom元素;

细细一想,其实这和前面用的在html中内联绑定函数是一样的,我们同样是给dom的onclick属性赋值一个函数,然后函数中的this指向当前元素,只是这个过程这里我们是在 js 中做的;

而还有一点区别就是前面的是赋值一段 js 字符串,这里是赋值一个函数,所以可以接受一个参数:event,这个参数是点击的事件对象。

用赋值绑定函数的一个缺点就是它只能绑定一次:

document.getElementById('clickme').onclick =function (e) {

console.log(this.id);

};

document.getElementById('clickme').onclick =function (e) {

console.log(this.id);

};

可以看到这里只打印了一次clickme。

addEventListener

这个才是我们需要重点介绍的一个函数

语法

target.addEventListener(type, listener[, useCapture]);

target: 表示要监听事件的目标对象,可以是一个文档上的元素Document本身,Window或者XMLHttpRequest;

type: 表示事件类型的字符串,比如:click、change、touchstart…;

listener: 当指定的事件类型发生时被通知到的一个对象。该参数必是实现 EventListener 接口的一个对象或函数。

useCapture: 设置事件的 捕获或者冒泡 (后文阐述) ,true: 捕获,false: 冒泡*,默认为false。

简单栗子

CLICK ME

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1);

functionfoo1(event) {

console.log(this.id);

console.log(event);

}

观察 console:

clickme

MouseEvent {isTrusted: true,screenX:37,screenY:88,clientX:37,clientY:14…}

监听函数中的this指向当前的dom元素,函数接受一个event参数。

绑定函数

绑定多个函数

addEventListener可以给同一个dom元素绑定多个函数:

CLICK ME

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1);

clickme.addEventListener('click', foo2);

functionfoo1(event) {

console.log(111);

}

functionfoo1(event) {

console.log(222);

}

可以看到 console:

111

222

我们可以看到两个函数都执行了,并且执行顺序按照绑定的顺序执行。

改变一下,如果我们的useCapture参数不同呢?

看下面 3 组对比:

1

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1,true);

clickme.addEventListener('click', foo2,true);

functionfoo1(event) {

console.log(111);

}

functionfoo1(event) {

console.log(222);

}

2

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1,true);

clickme.addEventListener('click', foo2,false);

functionfoo1(event) {

console.log(111);

}

functionfoo1(event) {

console.log(222);

}

3

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1,false);

clickme.addEventListener('click', foo2,true);

functionfoo1(event) {

console.log(111);

}

functionfoo1(event) {

console.log(222);

}

可以看到,console 并没有改变,所以执行顺序只和绑定顺序有关,和useCapture无关。

结论:

我们可以给一个dom元素绑定多个函数,并且它的执行顺序按照绑定的顺序执行。

同一个元素绑定同一个函数

我们给一个dom元素绑定同一个函数两次试试:

CLICK ME

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1);

clickme.addEventListener('click', foo1);

functionfoo1(event) {

console.log(111);

}

观察 console:

111

可以看到函数只执行了一次;

换一种方式:

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1,true);

clickme.addEventListener('click', foo1,false);

functionfoo1(event) {

console.log(111);

}

观察 console:

111

111

可以看到函数执行了两次。

结论:

我们可以给一个dom元素绑定同一个函数,最多只能绑定useCapture类型不同的两次。

IE下使用attachEvent/detachEvent

addEventListener只支持到 IE 9,所以为了兼容性考虑,在兼容 IE 8 以及以下浏览器可以用attachEvent函数,和addEventListener函数表现一样,除了它绑定函数的this会指向全局这个缺点以外

事件执行顺序的 PK

1. addEventListener 和 dom.onclick

CLICK ME

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1,true);

clickme.onclick = foo2;

clickme.addEventListener('click', foo3,true);

functionfoo1(event) {

console.log(111);

}

functionfoo2(event) {

console.log(222);

}

functionfoo3(event) {

console.log(333);

}

观察 console:

111

222

333

可见执行顺序只和绑定顺序有关

2. addEventListener 间的比较

见上文

事件的解绑

与事件绑定相对应的就是事件解绑了。

1. 通过 dom 的 on** 属性设置的事件

对于 Bind in HTML 和dom.onclick绑定的事件都可以用dom.onclick = null来解绑事件。

2. removeEventListener

通过addEventListener绑定的事件可以使用removeEventListener来解绑,removeEventListener接受的参数和addEventListener是一样的

CLICK ME

var clickme =document.getElementById('clickme');

clickme.addEventListener('click', foo1);

functionfoo1(event) {

console.log(111);

}

clickme.removeEventListener('click', foo1,true);

这里发现事件并没有取消绑定,发现removeEventListener的useCapture的参数原来和绑定时候传入的不一致,我们改成false之后发现事件取消了。

结论:

对于使用removeEventListener函数解绑事件,需要传入的listeneruseCapture应和addEventListener一致才可以解绑事件。

3. detachEvent

与attachEvent对应

DOM 事件

Document

事件冒泡

事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点。

比如上面的 HTML ,冒泡的顺序: div3 -> div3 -> div1 -> body -> html -> document (-> window)

事件捕获

事件捕获的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件。与事件冒泡的顺序相反。

比如上面的 HTML ,捕获的顺序: document -> html -> body -> div1 -> div2 -> div3

DOM事件流

DOM事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获,为截获事件提供机会。然后是实际的目标接受事件。最后一个阶段是时间冒泡阶段,可以在这个阶段对事件做出响应。

回到 addEventListener

我们来做几个小对比:

CLICK ME

栗子 1

document.getElementById('wrap').addEventListener('click', foo1,false);

document.getElementById('clickme').addEventListener('click', foo2,false);

functionfoo1(event) {

console.log(111);

}

functionfoo2(event) {

console.log(222);

}

console:

222

111

这里两个事件都是冒泡类型,所以是从内到外;

栗子 2

document.getElementById('wrap').addEventListener('click', foo1,true);

document.getElementById('clickme').addEventListener('click', foo2,true);

functionfoo1(event) {

console.log(111);

}

functionfoo2(event) {

console.log(222);

}

console:

111

222

这里两个事件都是捕获类型,所以是从外到内;

栗子 3

document.getElementById('wrap').addEventListener('click', foo1,false);

document.getElementById('clickme').addEventListener('click', foo2,true);

functionfoo1(event) {

console.log(111);

}

functionfoo2(event) {

console.log(222);

}

console:

222

111

wrap 事件是冒泡,clickme 事件是捕获,根据 dom 的事件流,先执行了捕获阶段(这里是目标阶段)再到冒泡阶段。

栗子 4

document.getElementById('wrap').addEventListener('click', foo1,true);

document.getElementById('clickme').addEventListener('click', foo2,false);

functionfoo1(event) {

console.log(111);

}

functionfoo2(event) {

console.log(222);

}

console:

111

222

clickme 事件是冒泡,wrap 事件是捕获,根据 dom 的事件流,先执行了捕获阶段(这里是目标阶段)再到冒泡阶段。

阻止 冒泡/捕获

CLICK ME

document.getElementById('wrap').addEventListener('click', foo1);

document.getElementById('clickme').addEventListener('click', foo2);

functionfoo1(event) {

console.log(111);

}

functionfoo2(event) {

event.stopPropagation();

console.log(222);

}

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

推荐阅读更多精彩内容

  • dom对象的innerText和innerHTML有什么区别? innerHTML指的是从对象的起始位置到终止位置...
    coolheadedY阅读 485评论 0 0
  • DOM0级和DOM2级在事件监听使用方式上有什么区别? DOM0级事件监听:用JavaScript指定事件处理程序...
    LeeoZz阅读 369评论 0 1
  • 声明:本文来源于http://www.webzsky.com/?p=731我只是在这里作为自己的学习笔记整理一下(...
    angryyan阅读 6,972评论 1 6
  • dom对象的innerText和innerHTML有什么区别?Node.innerText这个API不是W3C标准...
    老虎爱吃母鸡阅读 441评论 0 0
  • 《热爱生命》 作者:汪国真 我不去想, 是否能够成功 , 既然选择了远方 , 便只顾风雨兼程。 我不去想, 能否赢...
    哇咔咔呀阅读 304评论 0 0