JS中的DOM0和DOM2事件
JS中的事件基础及核心原理以及项目实战
事件的定义
事件
:元素天生具备的行为方式(和写不写JS代码没有关系)【onclick不是事件,click才是事件,浏览器会把一些常用事件挂载到元素对象的私有属性上,让我们可以实现DOM0事件绑定】,当我们去操作元素的时候会触发元素的很多事件事件绑定
:给当前元素的某个事件绑定方法,目的是让当前元素某个事件被触发时,做出一些反应
事件绑定的两种方法
- DOM0级事件绑定
- curEle.onclick=function(){};
- DOM2级事件绑定
- 标准浏览器:curEle.addEventListener(‘click’,function(){},false)
- IE6-8:curEle.attachEvent(‘onclick’,function(){})
DOM0事件
常用事件汇总
[PC端]
- 表单元素常用的事件
- blur:失去焦点
- focus:获取焦点
- change:内容改变
- select:被选中事件
- 键盘常用事件行为
- keydown:键盘按下
- keyup:键盘抬起
- keypress:键盘按下(中文输入法不能触发,英文状态下可以触发,因为文本被输入到文本框中)
- 鼠标常用事件行为
- click:点击(不是单击)
- dblclick:双击(300ms之内连续触发两次点击事件)
- mouseover:鼠标滑过
- mouseout:鼠标滑出
- mouseenter: 鼠标进入
- mouseleave: 鼠标离开
- mousedown:鼠标按下(左键)
- mouseup:鼠标左键抬起
- mousewheel:鼠标滚轮滚动
- 其他常用事件行为
- load:加载成功
- error:加载失败
- scroll:
- resize:大小改变事件:window.onresize:浏览器窗口改变大小触发该事件
[移动端
移动端键盘一般都是虚拟键盘,虽然存在keydown,keyup,但兼容不好,使用input事件代替
移动端没有鼠标,所以鼠标类事件在移动端兼容性非常差,mouse类事件基本无法使用
移动端大部分事件是靠手指完成的,所以它独有手指事件
-单手指事件:touchstart touchmove touchend touchcancel
-多手指事件:gesturestart gestuerchange gestuerend…..
移动端还有很多事件是靠硬件完成的:手机传感器,陀螺仪,重力感应器等
移动端兼容click事件,它把click当做单击使用(会有300MS的延迟事件)
事件对象
对以下事件绑定的严谨版描述:基于DOM0级事件的绑定方式,给box的click事件绑定了一个方法。当手动触发click事件时,会把绑定的方法执行
box.onclick=function(e){
arguments[0]===e;//=>e就是事件对象
e=e||window.event;(兼容版获取事件对象的写法)
};
当元素的某个事件行为被触发,不仅会把之前绑定的方法执行,还会给绑定的方法传递一个值(浏览器默认传递的),我们把传递的这个值称为事件对象:(标准浏览器)
- 这个值是个对象类型的值,里面存储了很多的属性和方法,
- 这些存储的值都是当前本次操作的基本信息,例如:鼠标位置,触发行为类型。。。。【只和本次操作有关,一个页面中只有一个事件对象】
IE6-8下关于事件对象的机制
- 方法被触发执行时,浏览器并没有把事件对象当做值传递给函数;(e是undefined)
- IE6-8下的事件对象需要我们通过window.event来获取
鼠标事件对象(mouseevent)
clientX/clientY
:当前鼠标触发点距离当前窗口左上角
的X/Y坐标pageX/pageY
:当前鼠标触发点距离BODY左上角
的X/Y坐标(IE6-8中没有这两个属性)
兼容处理
1 | box.onckick=function(e){ |
- type:当前触发事件的类型
- target:事件源(当前鼠标操作的是那个元素)(IE6-8没有该属性,用srcElement这个属性代表事件源)
- preventDefault:作用:阻止事件的默认行为;(IE6-8没有该方法,使用 returnValue=false处理)
- stopPropagation:阻止事件的冒泡传播 [IE6-8不兼容,使用cancelBubble=true来处理]
键盘事件对象(keyboardEvent)
- code:当前按得键盘按键是哪一个 【IE6-8没有这个属性】key属性和code一样,储存形式不一样
- keyCode:存储的是当前按键的码值(大部分按键都有自己的码值)
- which:和keyCode一样,也是当前按键的码值(which不兼容IE6-8)
- 这几个码值需要记住(删除:8 、 回车:13、 空格:32、 tab :9、)
手指事件对象(TouchEvent)
touches&&changedTouches&&targetTouches:存储的是当前屏幕上每一个手指操作的位置信息
- touches:只有手指在屏幕上我们才能获取相关信息,手指离开后,这样touchend事件中我们就无法通过touches获取手指信息
- changedTouches:手指在屏幕上时,和touches一样可以获得相关信息,手指离开后也可以记录手指离开屏幕一瞬间所在的位置信息(最常用的)
1 | touchesEvent |
详细处理
1 | let oBox = document.querySelector('#box'); |
手机移动端的点击、长按、滑动等都是基于内置的原生的touchstart/touchmove/touchend事件模拟出来的一些效果,没有现成的事件
市场上有许多成熟的类库,我们直接使用即可
1、fastclick.js:目的就是解决了移动端click事件300ms的延迟问题
2、百度云touch手指事件库
3、hammer.js
4、zepto.js:提供移动端事件操作的板块,也是目前市场上使用率最高的
阻止事件的默认行为
A标签的默认行为及阻止
1.超链接:点击A标签可以实现页面的跳转
2、锚点定位:通过HASH值定位到当前页面的指定ID盒子位置
- 首先URL地址栏末尾追加了一个HASH;
- 如果地址栏包含hash值,浏览器在渲染页面后,会默认定位到hash值的位置
真实项目中我们想用A标签做一个普通按钮(优势:它的:hover样式是兼容所有浏览器的)此时,要把之前提到的两个默认行为阻止掉才可以
阻止A标签的默认行为
在HTML中阻止默认行为(常用)
在JS中阻止
- 单点击A标签时 首先会触发click事件
- 其次按照href中的地址页面进行跳转
link.onclick=function(){ return false;}//阻止页面行为
link.onclick=function(e){
e=e||window.event;
e.preventDefault?e.preventDefault():e.returnValue=false;
}
事件的传播机制
事件传播有三个阶段
Event.prototype:
1 | - 0 NONE:默认值,不代表任何意思 |
当前元素的某个事件行为被触发,它的所有的祖先元素(一直到document)的相关事件行为也会被触发执行(由里向外),我们把这种传播机制叫做冒泡传播
mouseover和mouseenter事件的区别
mouseover:鼠标滑到元素上:存在事件的冒泡传播机制 mouseenter:鼠标进入元素里;浏览器阻止了它的冒泡传播机制; 不同点
区别 | mouseover | mouseenter |
---|---|---|
区别 1 | 存在冒泡传播机制 | 冒泡传播机制被浏览器阻止 |
区别 2 | 当鼠标从父元素进入到子元素时,首先会触发父元素的mouseout事件,再触发子元素的mouseover事件,由于冒泡传播机制,导致父元素的mouseover事件也被触发 | 当鼠标从父元素进入到子元素时,并不会触发父元素的mouseout事件,但是触发了子元素的mouseenter事件,由于浏览器阻止了它的冒泡传播,所以父元素的该事件不会被触发 |
事件委托(very important)
原理
:利用事件的冒泡传播机制完成(mouseenter不存在冒泡传播)
当一个容器内的很多元素都要为同一事件绑定方法,那么我们只需要给外层容器的该事件绑定方法,当里层元素的事件被出发时,会通过冒泡传播机制传到最外层容器那里,触发外层容器绑定的方法执行,在方法执行时,我们只需要根据判断事件源的不同而做不同的事情
。(利用事件委托可提高50%左右的性能)
拖拽当中鼠标焦点丢失问题
在拖拽登陆窗口的案例中(11.25) 当鼠标移动速度过快时,鼠标离开了h3,因为盒子跟不上奔跑的速度,所以导致以下几个问题 - 鼠标在h3之外飞,不会触发H3的mousemove,盒子就不动了; - 鼠标在h3之外抬起,也不会触发h3的mouseup,那么原有绑定的dragmove无法被移除,鼠标重新进入h3,此时不管鼠标是否按下,都会触发H3的dragmove; 原因:鼠标飞出h3,再怎么操作就和h3没有关系
解决方案: 1.把mouseup和mousemove事件绑定给document,原因:不管鼠标怎么飞,都飞不出document;(使用箭头函数)只要你鼠标还在文档中,那么mousemove和mouseup永远生效 2.setCapture(不兼容谷歌,支持Ie)
DOM2事件绑定
1 | //=>标准浏览器 |
1 | //=>IE6-8浏览器 |
DOM1?
在DOM第一代升级迭代的时候,事件绑定依然沿用DOM0绑定的方式..so…
DOM0于DOM2事件绑定的区别
DOM0事件绑定的原理
- 给当前元素的某一私有属性(onXXX)赋值的过程;(之前属性默认值是null,如果我们赋值了一个函数,就相当于绑定了一个方法)
- 当我们赋值成功(赋值一个函数),此时浏览器会把DOM元素和赋值的的函数建立关联,以及建立DOM元素的行为监听,当某一行为被用户触发,浏览器会把赋值的函数执行;
DOM0事件绑定的特点
- 只有DOM元素天生拥有这个私有属性(onxxx事件私有属性),我们赋值的方法才叫事件绑定,否则属于设置自定义属性
- 移除事件绑定的时候,我们只需要赋值为null;
- 在DOM0事件绑定中,只能给当前元素的某一个事件行为绑定一个方法,绑定多个方法,最后一次的绑定的会替换前面绑定的
DOM2事件绑定的原理
- DOM2事件绑定使用的
addEventListener/attachEvent方法都是在eventTarget这个内置类的原型上定义的
,我们调用的时候,首先要通过原型链找到这个方法,然后执行完成事件绑定的效果 - 浏览器会给当前元素的某个事件行为开辟一个事件池(事件队列)【浏览器有一个统一的事件池,每个元素绑定的行为都放在这里,通过相关标志区分】,当我们通过
addEventListener/attachEvent进行事件绑定的时候,会把绑定的方法放在事件池中
; - 当元素的某一行为被触发,浏览器回到对应事件池中,把当前放在事件池的所有方法按序依次执行
特点
所有DOM0支持的行为,DOM2都可以用,DOM2还支持DOM0没有的事件行为(这样说比较笼统)
(核心)【浏览器会把一些常用事件挂载到元素对象的私有属性上,让我们可以实现DOM0事件绑定,DOM2:凡是浏览器给元素天生设置的事件在DOM2中都可以使用】
例如:onDOMContentLoaded
(所有的DOM0和IE6-8的DOM2都不支持)
onDOMContentLoaded//当前浏览器中的DOM结构加载完成,就会触发这个事件DOM2中可以给
当前元素的某一事件行为绑定多个不同方法
(因为绑定的所有方法都放在事件池中);事件的移除:
事件类型、绑定的方法、传播阶段三个完全一致
,才可以完成移除(因此在绑定方法时,尽量不要用匿名函数,否则不好移除)
1 | //=>ON:给当前元素的某个事件绑定某个方法 |
xxx.removeEventLister('click',function(){},false)
==DOM0和DOM2绑定的方法是毫无联系的(因为是两套完全不同的机制),即使绑定的方法相同,也是执行两次,谁先绑定,就先执行谁==
window.onload&&$(document).ready()的区别
window.onload:当浏览器中的所有资源(DOM结构、文本内容、图片)都加载完成,触发load事件;
- 它是基于DOM0的事件绑定机制完成的,所以在同一页面中只能为他绑定一个方法,绑定多个,以最后一个为主;
- 如果想在一个页面中使用多次,应该是基于DOM2绑定的;
1 | function fn1(){ |
$(function(){})或者$(document).ready(function(){});
当文档中的DOM结构加载完成,就会触发执行,在一个页面中可以使用多次
- JQ中提供的方法,JQ是基于onDOMContentLoaded这个事件完成操作的
- JQ中的事件绑定都是基于DOM2事件绑定的
- onDOMContentLoaded在IE6-8下attachEvent也是不支持的,JQ在IE6-8下使用readystatechange来完成
DOM2事件的兼容处理
语法上的兼容
标准: curEle.addEventListener(’type’,fn,false);
IE6-8: curEle.attachEvent(’on’+type,fn);顺序问题
标准:按照绑定的顺序依次执行
IE6-8:当事件行为被触发,IE6-8执行是乱序重复问题
标准:可以自动去重,已经存在的方法不允许再次添加
IE6-8:在向事件池中增加方法时没有去重机制this问题
标准:当事件行为被触发,方法中的this指向当前元素本身
IE6-8:当事件行为被触发,方法中的this指向window
IE6-8事件池机制vs标准浏览器事件池机制(比较规范的回答)
1、向事件池中添加方法时,标准浏览器是使用addEventListener,IE6-8使用的是attachEvent;而且标准浏览器有自动去重的机制,已经添加的方法不允许再次添加。IE6-8没有去重机制;
2、浏览器执行事件池中的方法时,不仅把方法执行,还把事件对象当作实参传递给给对应的方法,但是也是有区别的,IE6-8传递的事件对象的值和window.event是相同 的,因此存在兼容问题;
3.当事件行为被触发,标准浏览器是依次执行,方法中的this指向当前元素;IE6-8下,是乱序执行,且方法中的this指向window;
不兼容的本质
:IE6-8低版本浏览器对于他的内置事件池处理机制的不完善导致的。DOM2事件绑定兼容处理机制原理
:自己创建一个类似于标准浏览器的自定义事件池(针对IE6-8)
- on :手动创建一个自定义事件池,把需要绑定的方法全部存在自定义事件池中
- off: 把不需要绑定的方法从自定义事件池中移除
- run:把run放在内置事件池中,当行为触发,需要浏览器把run执行,在run中把自定义事件池的方法执行。
1 | //向事件池追加方法 |
Bind方法的封装
1 | //ES5封装 BIND |
1 | //ES6方法 |