DOM事件的核心基础知识以及事件对象

134 阅读3分钟

事件是我们日常开发过程中最常见的功能了,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.onloaddocument.ready区别(JQ中的$(document).ready())

之前看过部分JQ源码,其中包含$(document).ready()的处理

document.addEventListener("DOMContentLoaded", completed)

1)它是基于DOM2级事件中事件池监听实现事件绑定的,所以可以在相同页面中给事件绑定好多不同的方法,也就是可以使用多次

2)它监听的是DOMContentLoaded事件,等待DOM结构一加载完就会触发执行的,

而window.onload本身基于DOM0事件绑定,而且监听的是load事件,所以页面中不仅只能用一次,而且需要等到浏览器所有资源都加载完毕才会触发执行,触发的节点要晚于DOMContentLoaded