DOM事件

JavaScript和HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,一遍事件发生时执行相应的代码。

事件流

事件流描述的是从页面中接受事件的顺序。

事件流
事件流
  1. Capture Phase(事件捕获过程)当 DOM 事件发生时,它会从window节点一路跑下去直到触发事件元素的父节点为止,去捕获触发事件的元素。
  2. Target Phase(事件触发过程)当事件被捕获之后就开始执行事件绑定的代码
  3. Bubble Phase(冒泡过程)当事件代码执行完毕后,浏览器会从触发事件元素的父节点开始一直冒泡到window元素(即元素的祖先元素也会触发这个元素所触发的事件)

触发顺序: Capture -> Target -> Bubbling

补充:低版本IE不支持Capture Phase,不是任何的事件都有Bubble Phase,比如onload事件。

demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 添加Capture阶段事件
docuemnt.addEventListener('click',function(){
alert('capture:'+1);
},true);
tableNode.addEventListener('click',function(){
alert('capture:'+2);
},true);
tdNode.addEventListener('click',function(){
alert('capture:'+3);
},true);
// 添加Bubbling阶段事件
docuemnt.addEventListener('click',function(){
alert('bubble:'+1);
});
tableNode.addEventListener('click',function(){
alert('bubble:'+2);
});
tdNode.addEventListener('click',function(){
alert('bubble:'+3);
});

输出结果:

1
2
3
4
5
6
capture:1
capture:2
capture:3
bubble:3
bubble:2
bubble:1

事件注册&取消

事件就是用户或浏览器自身执行的某种动作。诸如click、load和mouseover,都是事件的名字。而响应某个的函数就叫做事件处理程序

DOM0级事件处理程序

1
2
/*事件注册*/
elem.onclick = clickHandler;
1
2
/*事件取消*/
elem.onclick = null;

DOM2级事件处理程序

1
2
/*事件注册*/
eventTarget.addEventListener(type, listener[,useCapture])
1
2
/*事件取消*/
eventTarget.removeEventListener(type, listener[,useCapture])

NOTE:useCapture 为设定是否为捕获过程,默认事件均为冒泡过程,只有 useCapture 为 true 时才会启用捕获过程。使用DOM2级方法可以添加多个事件处理程序

IE事件处理程序

1
2
/*事件注册*/
eventTarget.attachEvent(eventNameWithOn, callback)
1
2
/*事件取消*/
eventTarget.detachEvent(eventNameWithOn, callback)

事件触发

点击元素,按下按键均会触发 DOM 事件,当然也可以以通过代码来触发事件

W3C标准

1
2
/*事件触发*/
target.dispatchEvent(eventNameWithOn, callback)

IE私有

1
2
/*事件触发*/
target.fireEvent(eventNameWithOn, event)

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件类型以及其他与特定事件相关的信息。

通用属性和方法

属性/方法 说明
type 被触发事件的类型
target 事件的目标
currentTarget 其数据处理程序当前正在处理事件的那个元素
stopImmediatePropagation() 取消事件的进一步捕获或冒泡,同事阻止任何事件处理程序被调用
stopPropagaton() 取消事件的进一步捕获或者冒泡。如果bubbles为true,则可以使用这方法
preventDefault() 取消事件的默认行为。如果cancelable是TRUE,则可以使用这方法

NOTE:

  • target和currentTarget区别

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。
demo:

1
2
3
4
5
6
/*事件处理程序存在于按钮的父节点,而目标是按钮*/
document.body.onlick=function(event){
console.log(event.currentTarget===document.body);//true
console.log(this===document.body);
console.log(event.target===document.getElementById("myBtn"));
}
  • IE低版本的event被注册在 window 之上而非目标对象上。target属性被srcElement所替代。阻止冒泡和阻止默认行为亦有相对应的方法:event.cancelBubble=true阻止冒泡Event.returnValue=false阻止默认行为。

事件类型

事件类型
事件类型

MouseEvent事件类型

事件 冒泡 描述
click YES 在用户单击主鼠标按钮或者按下回车键时触发
dblclick YES 在用户双击主鼠标按钮时触发
mouseup YES 在用户释放鼠标按钮时触发
mousedowm YES 在用户按下任意鼠标按钮时触发
mousemove YES 当鼠标指针在元素内部移动时重复地接触
mouseenter NO 在鼠标光标从元素外部首次移动到元素范围内时触发。光标移动到后代元素不会触发
mouseleave NO 在位于元素上方的鼠标鼠标光标移动到元素范围之外时触发。光标移动到后代元素不会触发
mouseover YES 在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。
mouseout YES 在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。

NOTE:

  • 只有在同一个元素上相继触发mousedown和mouseleave才会触发click事件。只有触发两次以上click事件才会触发一次dblclick事件。

  • mouseout移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。而mouseleave光标移动到后代元素上不会触发。mouseover和mouseenter的区别类似。请参考以下链接:
    demo1 demo2

MouseEvent属性

属性 描述
clientX, clientX 事件发生时鼠标指正在视口中的水平和垂直坐标
screenX, screenY 事件发生时鼠标指针相对于整个屏幕的坐标信息
ctrlKey, shiftKey, altKey, metaKey 事件发生时用户是否按下了这几个键
button mousedown和mouseup触发时,检测按键信息

NOTE:

  • metaKey在Windows上对应Windows键,在mac上对应Cmd键
  • button可取三个值,0:主鼠标按钮,1:鼠标滚轮按钮,2:次鼠标按钮

MouseEvent顺序

demo:从元素 A 上方移动过
mousemove -> mouseover(A) -> mouseenter(A) -> mousemove(A) -> mouseout(A) -> mouseleave(A)+

demo:点击元素
mousedown -> [mousemove] -> mouseup -> click

WheelEvent事件类型

事件 冒泡 描述
wheel YES 在用户滚动鼠标滚轮或操作其他类似的输入设备时触发

WheelEvent属性

属性 描述
deltaX 水平滚动量 只读
deltaY 垂直滚动量 只读
deltaZ z轴滚动量. 只读
deltaMode 增量值的单位
名称 描述
DOM_DELTA_PIXEL 0x00 增量值的单位为像素
DOM_DELTA_LINE 0x01 增量值的单位为行
DOM_DELTA_PAGE 0x02 增量值的单位为页

FocusEvent事件类型

事件 冒泡 描述
blur NO 失去焦点时触发
focus NO 获得焦点时触发
focusin YES 即将获得焦点时触发
focusout YES 即将失去焦点触发

NOTE:

  • 触发focusin事件,relatedTarget对应的是失去焦点的节点。触发focusout事件,relatedTarget对应的是得到焦点的节点。
  • 当焦点从页面中的一个元素移动到另一个元素,会依次触发focusout->focusing->blur->focus

InputEvent事件类型

事件 冒泡 描述
beforeInput YES 当<input> 或 <textarea>元素将要改变元素值时触发
input YES 当 <input> 或 <textarea>元素的值更改时触发

KeyboardEvent事件类型

事件 冒泡 描述
keyup YES 当用户释放键盘上的键时触发
keydown YES 当用户按下键盘上的任意键时触发
keypress YES 当用户按下键盘上的任意键时触发

KeyboardEvent属性

属性 描述
key 发生keydown和keyup时,key属性会对应键盘上一个特定的键。返回字符串
code 发生keypress时,Code属性会对应键盘上一个特定的键。返回字符串
ctrlKey, shiftKey, altKey, metaKey 事件发生时用户是否按下了这几个键
repeat 长按时repeat会赋值为true
keyCode 发生keydown和keyup时,keyCode属性会对应键盘上一个特定的键。返回ASCII编码
charCode 发生keypress时,charCode属性会对应键盘上一个特定的键。返回ASCII编码
which keyCode或者charCode的ASCII编码

Event事件类型

事件 冒泡 描述
load NO 一个资源及其相关资源已完成加载
unload NO 文档或一个依赖资源正在被卸载
error NO 一个资源的加载失败
select NO 文本被选中
abort NO 一个资源的加载已中止

NOTE:

  • 当用户离开页面,点击某个页面链接,在地址栏中键入新的URL,使用前进或后退按钮,关闭浏览器,重新加载页面会触发unload事件
  • img指向错误的src触发error事件
  • img在加载中按了ESC触发abort事件
  • 使用Image对象在客户端预先加载图像 eg:var image=new Image();image.src="smile.gif";

UIEvent事件类型

事件 冒泡 描述
resize NO 窗口或框架被重新调整大小
scroll NO/YES 当文档被滚动时发生的事件

事件委托

事件委托利用了事件冒泡,只指定了一个事件处理程序,就可以管理某一类型的所有事件。优点:需要管理的事件处理函数更少。减少占用的内存空间,提升整体性能。增加与删除子节点可以不额外处理事件。缺点:需要管理的事件处理函数更少。

1
2
3
4
5
6
<ul id=parent1>
<li><span>1<span></li>
<li><span>2<span></li>
<li><span>3<span></li>
<li><span>2<span></li>
</ul>
1
2
3
4
5
6
7
var ul = document.querySelector('ul');
ul.addEventListener('click', function(event) {
let el = event.target;
while (!el.matches('li')&&event.target!==event.currentTarget) {
el = el.parentNode;
}
});

NOTE:判断获取target值判断是否等于所要求的值,若不符合则将父节点赋值给target直到target的值等于currentTarget的值。

事件模拟和自定义事件

事件模拟

方法一

1
2
3
4
5
6
var event = new MouseEvent('click', {
'bubbles': true,
'cancelable': true
});
var cb = document.getElementById('checkbox');
cb.dispatchEvent(event);

方法二

1
2
3
4
var event = document.createEvent('Event');
event.initEvent('build', true, true);
document.addEventListener('build', doSomething, false);
document.dispatchEvent(event);
事件类型 事件初始化方法
UIEvents event.initUIEvent
MouseEvents event.initMouseEvent
MutationEvents event.initMutationEvent
HTMLEvents event.initEvent
Event event.initEvent
CustomEvent event.initCustomEvent
KeyboardEvent event.initKeyEvent

自定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
var myEvent = new CustomEvent("myevent", {
detail: {
foo: "bar"
},
bubbles: true,
cancelable: false
});
el.addEventListener('myevent', function(event) {
console.log('Hello ' + event.detail.foo);
});
el.dispatchEvent(myEvent);