概述
DOM的事件操作(监听和触发)都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信
的浏览器内置对象也部署了这个接口,比如XMLHttpRequest
、AudioNode
、AudioContext
)。
该接口主要提供三个实例方法:
addEventListener
:绑定事件的监听函数removeEventListener
:移除事件的监听函数dispatchEvent
:触发事件
DOM事件级别分类
DOM0级、DOM2级、DOM3级事件。
DOM0级事件
2步:
1、先找到DOM节点
2、把处理函数赋值给该节点对象的事件属性
eg:
<div id="box"></div>
<script>
var box = document.getElementById('box')
box.onclick = function () {
console.log("1")
}
</script>
解除事件:把null赋值给事件属性。document.getElementById('box').onclick = null;
缺点: 无法给一个对象添加多个事件处理函数。
DOM2级事件
1、EventTarget.addEventListener()
:用于在当前节点或对象上,定义一个特定事件的监听函数。一旦这个事件发生,就会执行监听函数。该方法没有返回值。
target.addEventListener(type, listener[, useCapture]);
2、使用addEventListener:
<div id="box"></div>
<script>
var box = document.getElementById('box')
function greet() {
console.log('Hello World');
}
box.addEventListener('click', greet, false)
</script>
addEventListener参数含义:
第一个参数type
: 事件名称,即:事件属性去掉on,大小写敏感
第二个参数listener
:事件处理函数/监听函数,事件发生时,会调用该监听函数
第三个参数useCapture
:布尔值,表示监听函数是否在捕获阶段触发,默认是false(监听函数只在冒泡阶段被触发)。
此外,第二个参数除了监听函数,还可以是一个具有handleEvent
方法的对象:
box.addEventListener('click', {
handleEvent: function (event) {
console.log('click')
}
})
第三个参数除了布尔值useCapture
,还可以是一个属性配置对象,该对象的具体属性如下:
capture
: 布尔值, 表示该事件是否在捕获阶段触发监听函数once
: 布尔值,表示监听函数是否只触发一次,然后就自动移除passive
: 布尔值,表示监听函数不会调用事件的preventDefault
方法,如果监听函数调用了,浏览器将忽略这个要求,并在监控台输出一行警告。
比如:
box.addEventListener('click', function (event) {
// 只执行一次的代码
}, {once: true})
3、 优点:可以随意添加多个处理函数。注意,执行顺序与添加顺序相关,即先添加先触发。如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener
方法手动去除)。
function hello() {
console.log('Hello');
}
document.addEventListener('click', hello, false);
document.addEventListener('click', hello, false);
执行上面代码,点击文档只会输出一行Hello。
4、如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数,下面代码通过匿名函数,向监听函数print传递了一个参数:
function print(x) {
console.log(x);
}
var el = document.getElementById('div1');
el.addEventListener('click', function () { print('Hello'); }, false);
5、监听函数内部的this
,指向当前事件所在的那个对象,下面代码中,监听函数内部的this
指向事件所在的对象dvBox
:
// HTML 代码:
// <div id="dvBox">
// <p></p>
// </div>
var dvBox = document.getElementById('dvBox')
dvBox.addEventListener('click', function (e) {
console.log(this.nodeName);
}, false);
6、 EventTarget.removeEventListener()
用来移除addEventListener
方法添加的事件监听函数,该方法没有返回值。
与addEventListener
方法完全一致,且removeEventListener
方法移除的监听函数,必须是addEventListener
方法添加的那个监听函数,而且必须在同一个元素节点,否则无效。
7、EventTarget.dispatchEvent()
该方法在当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault()
,则返回值为false
,否则为true
。
target.dispatchEvent(event) // 参数是一个Event对象的实例
box.addEventListener('click', hello, false);
var event = new Event('click');
box.dispatchEvent(event);
上面代码在当前节点触发了click事件
根据dispatchEvent
方法的返回值,判断事件是否被取消了:
var canceled = !cb.dispatchEvent(event);
if (canceled) {
console.log('事件取消');
} else {
console.log('事件未取消');
}
注意:IE8及一下,需要使用attachEvent
和detachEvent
来添加和移除事件,它们只包含两个参数,第一个是事件属性(包含on),第二个是处理函数,不支持事件捕获所以没有第三个参数。因为IE8以及更早的浏览器版本只支持事件冒泡。
DOM3级事件
在DOM2基础上增加了更多的事件类型
- UI事件,当用户与页面上的元素交互时触发,如:load、scroll
- 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
- 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
- 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
- 文本事件,当在文档中输入文本时触发,如:textInput
- 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
- 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
- 变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
事件冒泡与事件捕获
首先看一个结构:
<html>
<body>
<div>
<span>
目标内容
</span>
</div>
</body>
</html>
给最里面的目标内容绑定事件,就会有一个事件源和目标之间的事件流,此例中,事件流的方向为window -> document -> html -> body -> div -> span -> 目标 -> span -> div -> body -> html -> document -> window
,整个事件流分为两个部分,以事件目标为界限,从window到目标这个过程为事件捕获,从目标回到window的过程叫事件冒泡。
事件默认的处理阶段为冒泡阶段。实际开发中,经常会利用到事件冒泡,也经常需要阻止事件冒泡,这就涉及到事件对象event的相关内置方法和属性。
注意:
- 不是所有的事件都能冒泡,如:blur、focus、load、unload都不能
- 不同的浏览器,阻止冒泡的方式也不一样,在w3c标准中,通过
event.stopPropagation()
完成, 在IE中则是通过自身的event.cancelBubble=true
来完成。
event事件对象
事件处理函数会回调一个参数event,代表当前事件对象,event中有很多常用的方法和属性:
preventDefault
阻止默认行为,比如当点击submit按钮时候,可以采用此方法阻止表单提交。stopPropagation
停止事件冒泡,需要防止事件冒泡带来的负面影响的时候就要使用该方法。stopImmediatePropagation
阻止后续事件,该方法除了阻止事件冒泡外在当前事件被绑定多个处理程序的时候,后续的处理程序也会被阻止。currentTarget
此属性返回当前事件所绑定的对象。target
此属性返回当前触发事件的对象,注意target是触发事件的对象,是真正的事件源,同样以上面的HTML为例,给div绑定一个事件,点击带文字的span后,target是span,而currentTarget是div。
为什么target是span,而currentTarget是div?
因为默认情况下的事件处理阶段是事件冒泡阶段,点击span会冒泡到div去触发div上绑定的事件,所以span是真正的事件源,是触发事件的对象,而div是当前绑定该事件的对象。
应用:利用事件冒泡实现事件委托:
<ul id="fruit">
<li>苹果</li>
<li>香蕉</li>
<li>西瓜</li>
<li>草莓</li>
</ul>
如果想要实现点击每个li标签就能打印出文本内容,只需要利用事件冒泡即可:
var fruit= document.getElementById('fruit');
function log(e) {
console.log(e.target.innerText);
}
fruit.addEventListener('click', log, false);
对event.preventDefault()
阻止默认事件的理解: 这里的默认事件指的是HTML标签的默认事件,与JavaScript没有关系, 比如a标签,点击的时候就是默认打开href属性对应的链接,会阻止默认事件但是依然会发生事件冒泡。
事件委托
什么是事件委托? — 将元素的事件委托给它的父级或者更外级元素处理。
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress
。
不适合事件委托的:mouseover
和mouseout
虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。此外,focus
,blur
之类的,本身就没用冒泡的特性,自然就不能用事件委托了。
参考链接:
EventTarget 接口