事件
- 什么是事件
事件是浏览器赋予元素的默认行为,也可以理解为事件是天生具备的,不论我们是否为其绑定方法,当某些行为触发的时候,相关的事件都会被触发执行,只不过如果没有为其绑定方法,当事件被触发的时候将什么都不做。
- 浏览器赋予元素的事件行为
- 鼠标事件
- click 点击事件(PC:点击N次就触发N次事件) 单击事件(移动端:300ms内没有发生第二次点击操作,算作单击事件行为,所以click在移动端有300ms的延迟)
- dblclick 双击事件
- contextmenu 鼠标右键点击事件
- mousedown 鼠标按下
- mouseup 鼠标抬起
- mousemove 鼠标移动
- mouseover 鼠标滑入
- mouseout 鼠标滑出
- mouseenter 鼠标进入
- mouseleave 鼠标离开
- wheel 鼠标滚轮滚动
- 键盘事件
- keydown 键盘按下
- keyup键盘抬起
- keypress 长按(除了Shift/Fn/CapsLock键之外)
- 手指事件 [Touch Event 弹手指事件模型]
- touchstart 手指按下
- touchmove 手指移动
- touchend 手指松开 [Gesture Event多手指事件]
- 表单事件
- focus 获得焦点
- blur 失去焦点
- submit 表单提交(前提:表单元素都包含在form中,并且点击的按钮时submit)
- reset 表单重置
- select 下拉框内容被选中
- change 内容改变
- input 文本框内容改变事件
- 资源事件
- load 加载成功(window.onload / img.onload)
- error 加载失败
- beforeunload 资源卸载之前(window.onbeforeunload 页面关闭前触发)
- CSS3动画事件
- transitionstart transition动画开始
- transitionrun transition动画运行中
- transitionend CSS过度完成(transition动画结束)
- 视图事件
- resize 元素或浏览器大小改变
- scroll滚动事件
事件绑定
- 什么是事件绑定
给元素默认的事件行为绑定方法,这样可以在行为被触发时执行该方法
- DOM0级事件绑定
- 语法: [元素].on[事件] = [函数] document.body.onclick = function(){}
- 移除绑定: 赋值为null或其他非函数值即可 document.body.onclick = null
- 原理:每个DOM元素对象的私有属性上都有很多类似于onxxx的私有属性,我们给这些代表事件私有属性赋值,就是DOM0级事件绑定
- 如果没有对应事件的私有属性值(如:DOMContentLoaded)则无法通过这种方式实现事件绑定
- 只能给当前元素的某个事件行为绑定一个方法,绑定多个,最后一个会覆盖前面的方法
- 好处是:执行效率高,而且开发使用起来方便
- DOM2级事件绑定
- 语法:[元素].addEventListener([事件], [方法], [捕获/冒泡]) document.body.addEventListener('click', fn1, false);
- 移除事件:[元素].removeEventListener([事件], [方法], [捕获/冒泡]) document.body.removeEventListener('click', fn1, false);
- 原理:每一个DOM元素都是基于原型链的查找机制,找到EventTarget.prototype上的addEventListener/removeEventListener等方法,基于这些方法实现事件的绑定和移除;DOM2事件绑定采用事件池机制
- DOM2事件绑定,绑定的方法一般都不是匿名函数,主要目是方便移除事件的时候使用
- 凡是浏览器提供的事件行为,都可以基于这种模式完成事件的绑定和移除(如:window.onDomContentLoaded是不行的,因为没有个私有的事件属性,但是我们可以用window.addEventListener('DomContentLoaded', func))来绑定
- 可以给当前元素的每个事件类型绑定多个不同的方法(进入到事件池),这样当事件行为触发,会从事件池中依次(按绑定顺序)取出对应的方法执行。
事件对象
- 什么是事件对象
给当前元素的某个事件行为绑定方法,当事件行为触发不仅会把绑定的方法执行,而且还会给方法默认传递一个实参,这个实参就是事件对象。 事件对象: 存储当前事件操作及触发的相关信息(事件对象浏览器本身记录的,记录的是当前这次操作的信息,跟在哪个函数中无关)
- 鼠标事件对象:MouseEvent
- clientX/clientY 鼠标触发点距离当前窗口的X/Y轴坐标
- pageX/pageY 鼠标触发点距离Body的X/Y轴坐标
- type 事件类型
- target/srcElement 获取当前事件源(当前操作的元素)
- path 传播路径
- preventDefault() / returnValue = false;阻止默认行为
- stopPropagation() / cancelBubble = false; 阻止冒泡传播
- 键盘事件对象: KeyboardEvent
- which/keycode 获取按键的键盘码
- 方向键:左37上38右39下40
- Space 32
- BackSpace 8
- Del 46
- Enter 13
- Shift 16
- Ctrl 17
- Alt 18
- altKey 是否按下alt键(组合键)
- ctrlKey 是否按下ctrl键(组合键)
- shiftKey 是否按下shift键(组合键)
- 手指事件对象:TouchEvent
- changedTouches / targetTouches / touches 都是用来记录手指信息的,平时常用的是changedTouches
- 手指按下、移动或离开屏幕changedTouches 都存储了对应的手指信息,离开屏幕后,存储的是最后一次手指在屏幕中的信息;而toucheszh在手指离开屏幕后就没有任何信息了;获取的结果都是一个TouchList集合,记录每一根手指的信息
- ev.changedTouches[0] 记录第一根手指的信息
- clientX / clientY 同鼠标事件
- pageX / pageY 同鼠标事件
阻止事件的默认行为
浏览器会赋予元素很多默认行为(例如:鼠标右键,键盘按下输入内容等),我们可以基于ev.preventDefault()方法来禁用这些默认行为
window.onContextMenu = function(ev){
ev.preventDefault();//阻止鼠标默认的右键菜单
//这里可以实现自定义的右键菜单
}
事件的传播
事件的传播分为三个阶段
- 捕获阶段 CAPTURING_PHASE
- 从最外层元素一直向里逐级查找,直到找到事件源为止,目的是为冒泡阶段的传播提供路径;ev.path存放的就是捕获阶段收集到的传播路径
- 目标阶段(当前事件源) AT_TARGET
- 把当前事件源的相关事件行为触发
- 冒泡阶段 BUBBLE_PHASE
- 按照捕获阶段收集的传播路径,不仅仅当前事件源的相关事件行为触发,而且从内到外其祖先所有元素的相关事件行为都会触发(如果事件绑定了方法,则方法也会被执行)
- DOM0级事件绑定中给元素事件行为绑定的方法,都是在目标阶段/冒泡阶段触发
- DOM2级事件绑定可以控制绑定的方法在捕获阶段触发(但没有实际意义,一般不会这么用)
- 元素.addEventListener(事件,方法,false/true);
- 最后一个参数默认是false:控制方法在冒泡阶段触发执行,true则表示在捕获阶段触发执行
- 阻止冒泡传播
- ev.stopPropagation() 或 ev.cancelBubble = false;
事件委托
事件委托/事件代理:利用事件的冒泡传播机制(核心/前提),我们可以把一个“容器A“中所有后台元素的某个“事件行为E“触发要做的操作,委托给A的事件行为E,这样后期只要触发A中任何元素的E行为,都会传播到A上,把给A绑定的方法执行;在方法执行的时候,基于事件源不同做不同的处理
- 性能提高60%左右
- 可以操作动态绑定的元素
- 某些需求必须基于它完成
因为点击事件行为存在冒泡传播机制,所以不论点击哪个元素,最后都会传播到BODY上,触发Body的CLICK事件行为,把为其绑定的方法执行,在方法执行接收到的事件对象中,有一个target/srcElement属性(事件源),可以知道当前操作的是哪个元素,我们此时方法中完全可以根据事件源的不同,做不同的处理,这就是事件委托机制。
document.body.onclick = function(ev){
let target = ev.target,
targetClass = target.className;
if(targetClass === 'inner'){
console.log('点击了inner元素');
return;
}else if(targetClass === 'outer'){
console.log('点击了outer元素');
return;
}else if(targetClass === 'box'){
console.log('点击了box元素');
return;
}
}