web前端高级JavaScript - 事件和事件绑定的基础知识梳理 | 8月更文挑战

584 阅读7分钟

事件

  • 什么是事件

事件是浏览器赋予元素的默认行为,也可以理解为事件是天生具备的,不论我们是否为其绑定方法,当某些行为触发的时候,相关的事件都会被触发执行,只不过如果没有为其绑定方法,当事件被触发的时候将什么都不做。

  • 浏览器赋予元素的事件行为

事件参考

  • 鼠标事件
    • 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;
	}
}

mouseover与mouseenter的区别

在这里插入图片描述