给 DOM 元素绑定事件分为两大类:在 html 中直接绑定 和 在 JavaScript 中绑定。javascript
在 HTML 中绑定事件叫作内联绑定事件,HTML 的元素中有如 onclick
这样的 on***
属性,它能够给这个 DOM 元素绑定一个类型的事件,主要是这样的:html
<div onclick="***">CLICK ME</div> |
这里的 ***
有两种形式:java
<div onclick="var a = 1; console.log(a); console.log(this);">CLICK ME</div> |
点击能够发现,console:浏览器
1 |
var a = 1; console.log(a); console.log(this);
这一段字符串被看成 js 执行了,同时 this
指向当前这个点击的元素。dom
<div onclick="foo(this)">CLICK ME</div> |
这里须要添加 script
去定义函数:函数
<script> |
能够观察到,console:post
<div onclick="var a = 1; console.log(a); console.log(this);">CLICK ME</div> |
这里的 this
指向了全局,传入的参数是点击的 DOM 元素,其实和第一种方式是同样的,都是在 onclick=
后面指向了一段js的字符串,不一样的是在这个字符串中是执行了一个函数名,而这个函数咱们在全局中定义了,因此点击的时候能够执行,而后传入的参数 this
也就是同样的道理了。ui
或者多说一句,这里的字符串才是真正赋值给 onclick
的函数,这里咱们是在函数里面再执行了 foo
函数。this
然而这内联的方式绑定时间不利于分离,因此通常咱们不推荐这种作法,因此也就再也不多阐述了spa
先上栗子:
<div id="clickme">CLICK ME</div> |
document.getElementById('clickme').onclick = function (e) { |
观察 console:
clickme |
首先,咱们获取到了 dom
元素,而后给它的 onclick
属性赋值了一个函数;
点击 dom
咱们发现那个函数执行了,同时发现函数中的 this
是指向当前的这个 dom
元素;
细细一想,其实这和前面用的在 html
中 内联 绑定函数是同样的,咱们一样是给 dom
的 onclick
属性赋值一个函数,而后函数中的 this
指向当前元素,只是这个过程这里咱们是在 js 中作的;
而还有一点区别就是前面的是赋值一段 js 字符串,这里是赋值一个函数,因此能够接受一个参数:event
,这个参数是点击的事件对象。
用赋值绑定函数的一个缺点就是它只能绑定一次:
document.getElementById('clickme').onclick = function (e) { |
能够看到这里只打印了一次 clickme
。
这个才是咱们须要重点介绍的一个函数
target.addEventListener(type, listener[, useCapture]); |
target
: 表示要监听事件的目标对象,能够是一个文档上的元素 Document
自己,Window
或者 XMLHttpRequest
;type
: 表示事件类型的字符串,好比: click
、change
、touchstart
…;listener
: 当指定的事件类型发生时被通知到的一个对象。该参数必是实现 EventListener 接口的一个对象或函数。useCapture
: 设置事件的 捕获或者冒泡 (后文阐述) ,true
: 捕获,false
: 冒泡*,默认为 false
。 <div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
观察 console:
clickme |
监听函数中的 this
指向当前的 dom
元素,函数接受一个 event
参数。
addEventListener
能够给同一个 dom
元素绑定多个函数:
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
能够看到 console:
111 |
咱们能够看到两个函数都执行了,而且执行顺序按照绑定的顺序执行。
改变一下,若是咱们的 useCapture
参数不一样呢?
看下面 3 组对比:
var clickme = document.getElementById('clickme'); |
var clickme = document.getElementById('clickme'); |
var clickme = document.getElementById('clickme'); |
能够看到,console 并无改变,因此执行顺序只和绑定顺序有关,和 useCapture
无关。
结论:
咱们能够给一个 dom
元素绑定多个函数,而且它的执行顺序按照绑定的顺序执行。
咱们给一个 dom
元素绑定同一个函数两次试试:
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
观察 console:
111 |
能够看到函数只执行了一次;
换一种方式:
var clickme = document.getElementById('clickme'); |
观察 console:
111 |
能够看到函数执行了两次。
结论:
咱们能够给一个 dom
元素绑定同一个函数,最多只能绑定 useCapture
类型不一样的两次。
addEventListener
只支持到 IE 9,因此为了兼容性考虑,在兼容 IE 8 以及如下浏览器能够用 attachEvent
函数,和 addEventListener
函数表现同样,除了它绑定函数的 this
会指向全局这个缺点之外
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
观察 console:
111 |
可见执行顺序只和绑定顺序有关
与事件绑定相对应的就是事件解绑了。
对于 Bind in HTML 和 dom.onclick
绑定的事件均可以用 dom.onclick = null
来解绑事件。
经过 addEventListener
绑定的事件能够使用 removeEventListener
来解绑, removeEventListener
接受的参数和 addEventListener
是同样的
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
这里发现事件并无取消绑定,发现 removeEventListener
的 useCapture
的参数原来和绑定时候传入的不一致,咱们改为 false
以后发现事件取消了。
结论:
对于使用 removeEventListener
函数解绑事件,须要传入的 listener
useCapture
应和 addEventListener
一致才能够解绑事件。
与 attachEvent
对应
<!DOCTYPE html> |
事件开始时由最具体的元素接受,而后逐级向上传播到较为不具体的节点。
好比上面的 HTML ,冒泡的顺序: div3 -> div3 -> div1 -> body -> html -> document (-> window)
事件捕获的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件。与事件冒泡的顺序相反。
好比上面的 HTML ,捕获的顺序: document -> html -> body -> div1 -> div2 -> div3
DOM事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获,为截获事件提供机会。而后是实际的目标接受事件。最后一个阶段是时间冒泡阶段,能够在这个阶段对事件作出响应。
咱们来作几个小对比:
<div id="wrap"> |
|
console:
222 |
这里两个事件都是冒泡类型,因此是从内到外;
|
console:
111 |
这里两个事件都是捕获类型,因此是从外到内;
|
console:
222 |
wrap 事件是冒泡,clickme 事件是捕获,根据 dom 的事件流,先执行了捕获阶段(这里是目标阶段)再到冒泡阶段。
|
console:
111 |
clickme 事件是冒泡,wrap 事件是捕获,根据 dom 的事件流,先执行了捕获阶段(这里是目标阶段)再到冒泡阶段。
<div id="wrap"> |
|