事件是我们日常开发过程中最常见的功能了,click、focus、blur......
几乎我们每天都在写事件,
但是你真的了解事件吗?
事件的原理到底是什么?
DOM0和DOM2事件有什么区别?
这次我们就来彻底搞个明白
我们先在页面上画一个盒子
<style>
#box {
box-sizing: border-box;
width: 100px;
height: 100px;
background: lightcoral;
}
</style>
<body>
<div id="box"></div>
<script>
box.onclick = function () {
console.log('OK');
};
</script>
</body>
很明显,当我们点击这个盒子的时候,控制台输出'OK'
那么这个事件触发的时候都经历了什么呢?
事件是元素(或者浏览器)天生自带的行为,只要行为触发,则会触发相关的事件行为;我们原有基于xxx.onxxx=function(){}属于事件绑定:给某个事件行为绑定方法,再行为触发的时候可以通知方法执行
【事件】
* 鼠标事件
* click 点击(PC) 单击(移动端)
* dblclick 双击
* contextmenu 鼠标右键
* mousedown 按下
* mouseup 抬起
* mousemove 移动
* mouseenter/mouseleave 进入和离开
* mouseover/mouseout 滑过和滑出
* wheel 滚轮滚动
* 表单事件
* focus/blur 获取或者失去焦点
* input 正在输入内容,内容改变
* change 内容改变
* checked/selected 选中
* 键盘事件
* keydown 按下
* keyup 抬起
* keypress 长按
* 手指事件
* 单手指:touchstart/touchmove/touchend
* 多手指事件:gesture
* 其它事件
* offline/online 断网或者连网
* animationstart/end/iteration CSS3Animation动画事件
* transitionstart/end/run CSS3Transition动画事件
* fullscreenchange 全屏切换
* resize 窗口大小改变
* scroll 滚动条滚动
* load 加载完
* error 加载失败
* timeout 加载超时
* progress 加载中
* abort 加载中断
* H5中的拖拽事件
* dragstart 拖拽开始
* drag 拖拽中
* dragend 拖拽结束
* dragenter 进入到目标区域
* dragleave 离开目标区域
* dragover 在目标区域中拖动
* drop 在目标区域释放
* 媒体事件(Audio/Video)
* canplay/canplaythrough
* complete
* volumechange 声音改变
* play 播放
* pause 暂停
* playing 播放中
事件分为DOM0级事件,和DOM2级事件
1.DOM0级事件绑定
xxx.onxxx=function(){}
【DOM0事件绑定的原理】
- 每一个DOM元素对象都有很多内置的私有属性,其中包含onxxx这样事件类的私有属性
- DOM0事件绑定原理就是给这些事件类私有属性赋值(当我们触发相关事件行为,浏览器会帮助我们把赋值的函数触发执行)
- 特点1:如果不存在某个事件类型的私有属性,则无法基于这种方式做事件绑定(例如 DOMContentLoaded [等到DOM资源加载完触发的事件])
- 特点2:只能给当前元素的某个事件类型绑定一个方法(私有属性只能赋值一个值)
box.onclick = function () {
console.log('OK');
}
拿刚才这个例子举例,我们在控制台可以看到,box自带很多onxxx这样事件类的私有属性
2.DOM2级事件绑定
在浏览器中,有一个内置类EventTarget,在其原型上有一些方法,
EventTarget.prototype:
addEventListener/removeEventListener/dispatchEvent
所有的DOM元素对象(含window)都是EventTarget的实例
非标准浏览器(IE<=8):attachEvent/detachEvent
xxx.addEventListener/removeEventListener('xxx',function(){},false)
【DOM2事件绑定的原理】
- 利用浏览器的事件池机制来完成事件监听和绑定的
例如:我们有四个事件绑定
box.addEventListener('click',fn1,false);
box.addEventListener('click',fn2,false);
box.addEventListener('mouseenter',fn1,false);
link.addEventListener('click',fn2,false);
在绑定这四个事件时,会被依次传入事件池
在这些的事件绑定当中,两个事件绑定只要有一个栏位的类型不一致,就不会发生冲突
例如:给box绑定了两个click,但是绑定的方法不是函数,那么就不会发生冲突。
由此可以总结出以下特点:
-
特点1:所有事件类型都可以基于这种方式进行事件绑定( 例如 window.addEventListener('DOMContentLoaded',function(){}) )
-
特点2:可以给当前元素的某一个事件类型绑定多个不同的方法
box.addEventListener('click', function () { console.log('DOM2=>OK'); }); box.addEventListener('click', function () { console.log('DOM2=>OK again'); })
来看一道经典的面试题:
问:window.onload和document.ready区别(JQ中的$(document).ready())
之前看过部分JQ源码,其中包含$(document).ready()的处理
document.addEventListener("DOMContentLoaded", completed)
1)它是基于DOM2级事件中事件池监听实现事件绑定的,所以可以在相同页面中给事件绑定好多不同的方法,也就是可以使用多次
2)它监听的是DOMContentLoaded事件,等待DOM结构一加载完就会触发执行的,
而window.onload本身基于DOM0事件绑定,而且监听的是load事件,所以页面中不仅只能用一次,而且需要等到浏览器所有资源都加载完毕才会触发执行,触发的节点要晚于DOMContentLoaded