事件流
这一概念源自于对事件触发对象的思考。例如常见的点击事件,鼠标移动事件。这些事件发生之时,往往不只是点击或者移动到某一特定元素上。
比如点击某一个按钮,而它是由上一层的父标签,或许在上一层还有父标签甚至是整个页面。因此点击一个元素可以看成是同时点击了父标签或者整个页面。那么此时事件应该怎么响应到指定标签呢?
事件冒泡
即事件从指定元素开始传播到最外层的元素,并且该事件不仅会在指定元素上发生,还会在传播过过程中的每一个元素上发生。
1 | <html> |
- 事件冒泡: button -> div -> body -> html -> document
如上,再点击click之后,事件从 button 开始传播至 html ,再到 documet。这一个过程也称为事件冒泡
事件捕获
与事件冒泡刚好相反,事件从最外层的 documet 开始一直往里面,直到点击的元素才停止
1 | <html> |
- 事件冒泡: documet -> html -> body -> div -> button
如上,再点击click之后,事件从 documet 开始传播至 button。这一个过程也称为事件捕获
DOM 事件流
在上述两种事件确定的方式下,规定了事件处理的三个阶段。事件捕获阶段、处于目标阶段、事件冒泡阶段。同时 DOM明确规定
事件捕获阶段不会处理事件
处于目标阶段属于冒泡阶段的一部分,并且会触发事件。
然而实际上,几乎所有主流浏览器都支持在事件捕获阶段触发事件,它们并没有遵守规定
事件处理程序
主要是指 DOM 如何处理各种 HTML 上的程序。
对于所有浏览器来说,有两种标准用来操作事件的添加与删除,一种是 DOM2 事件处理程序,一种是 IE 事件处理程序。
DOM2 级事件处理
直接调用该 dom 对象的事件属性,并将相应的执行函数赋予它
addEventListener() 和 dom.on(事件) = 函数
利用 dom 对象的事件属性直接赋予一个执行函数;利用 addEventListener 添加,并且该方法可以添加多个
addEventListener() 有三个参数,参数 1 为事件、参数 2 为执行函数。参数 3 为一个布尔值,false 代表在冒泡阶段执行,true 表示在捕获阶段执行
1 | var doc = document.querySelector('div'); |
removeEventListener() 和 dom.on(事件) = null
利用直接设置事件属性为 null 来移出执行函数。利用 removeEventListener() 来移出,但前提是必须给定函数名
removeEventListener() 有三个参数,参数 1 为事件、参数 2 为执行函数。参数 3 为一个布尔值,false 代表在冒泡阶段执行,true 表示在捕获阶段执行
如下使用匿名函数定义的执行函数无法删除
1 | var doc = document.querySelector('div'); |
IE 级
attachEvent()
和 DOM 2 级一样用来添加事件,只有两个参数 “事件” 和 “处理函数”,不能设置冒泡或者捕获。因为 IE8 之前那只支持冒泡,所以就只能冒泡
同样可以添加多个执行函数
detachEvent()
和 DOM2 级一样,要删除必须给定函数名作为参数。无法删除通过匿名定义添加的执行函数。
封装跨浏览器的事件处理函数
因为 IE 独树一帜,又因为 IE 属于 windows 用户标配;所以兼容需要考虑。否则代码在 IE 上可能出问题
1 | const EventHandle = { |
事件对象
事件对象作为事件发生给予 js 获得相关信息的机会非常重要。程序要根据这些信息作出相应的响应。
DOM 事件对象
必定会传一个 event 对象给执行函数。可以直接作为参数使用。但有两种情况需要注意
- 通过直接在 html 元素上添加的事件,必须写明参数为 event,响应执行函数也要写明该参数
- 通过 addEventListener() 添加的事件,只需要在执行函数上写明参数就行。
1 | <div onclick="Test(event)"></div> |
1 | let doc = document.querySelector('header'); |
常用属性
- bubbles Boolean 表明事件能否冒泡
- cancelable Boolean 是否能取消事件的默认行为
- currentTarget Element 当前事件处理程序正在处理的元素
- defaultPrevented Boolean 为 true 表示调用了 preventDefault()
- eventPhase Integer 1 为捕获阶段 - 2 - 为处于目标阶段 - 3 为冒泡阶段
- preventDefault() 取消事件的默认行为,前提是 cancleable 为 true
- target 事件的目标,就是触发事件的对象
- type 事件类型
target 与 currentTarget
这里的 currentTarget 是指发生事件时,该事件所绑定的那个元素
而 target 从始至终就都是你点击或者移动或者其他触发事件行为的元素
1 | <div onclick="father(event)"> |
看上述这个例子。
情况一:点击 button 2;按照事件冒泡那么两个执行函数都会触发
- target: 两个函数的输出值都为 button 2;因为点击的是该元素
- currentTarge: son() 输出的为 button 1;father() 输出为 div
情况二:点击 button 1;只会触发 father()
- target: 输出为 button 1;因为就是点击在 button 1 上
- currentTarget:输出为 div;因为该执行函数就绑定在该元素上
情况三:点击 div;只触发 father()
target 和 currentTarget 都为 div
另外,执行函数中的 this 值指向 currentTarget;但是有个前提,this值要等于 currentTarget,那么必须是在target上或者事件是通过 sddEventListener 添加的。否则通过html元素直接绑定的方式this指向了 window 对象
eventPhase 和 stopPropagation
eventPhase 可以知道事件执行时处于哪个阶段
stopPropagation 可以阻值事件继续冒泡传播。我们知道一般事件是在处于目标阶段到冒泡阶段执行的。倘若不阻止冒泡,那么点击一个小按钮,一直回溯到 document。那么整个页面许多地方的点击事件都会触发,很显然我们不想这样。
1 | <div onclick="father(event)"> |
当然该方法同样可以阻止捕获,不过前提是绑定事件时指定他在捕获阶段触发。这样一来就不会继续捕获下去了
IE 事件对象
为什么不能统一呢,非要学两套
IE 事件对象与 DOM 级有一定差异
常用属性
- cancelable 默认值为 false,true 为取消冒泡。与 DOM 中 stopPropagation 相似
- returnValue 默认为 true,false 为取消事件默认行为,与 DOM 中 preventDefalut() 相似
- srcElement 事件目标,与 DOM target 相似
- type 事件类型
event 对象的获取
IE 中的 event 对象时作为 window 对象的一部分存在,可以通过 window.event 来获取
- 通过文档对象赋值的方法,必须要指定 window.event ,直接使用 event 会报错 undefined
1 | var doc = document.querySelector('#id'); |
- 通过 attachEvent() 添加的可以像 DOM 那样作为参数直接使用
1 | doc.attachEvent('click', function(event) { |
总结
执行函数中关于事件元素的信息都可以通过 event 获取,虽然 this 值有时也会等于 event 的部分属性。但是建议用 event,因为 this 的指向取决于外部执行环境,不能保证得到想要的值。
另外,IE 要没了。取而代之的是微软新浏览器 Edge ,这个浏览器好像已经统一了 DOM级规定的事件处理。原来 IE 的那些特有事件处理已经没有了
事件Type
常见的事件类型
UI事件
界面发生的事件
load 事件
当页面完全加载,包括所有图像、js 文件、产生式文件等外部资源。之后就会触发该事件。添加事件的方法?建议使用之前写的跨浏览器事件处理方法。当然也可以通过获取 dom 对象,并对其属性赋值,也可以直接在 html 元素上绑定。但是这两个方法下的 event 对象的使用有区别,特别是在 IE 浏览器上
- 在 window对象下触发整个页面的加载;
1 | window.addEventListener('load', function(event) { |
- 用来加载图片
绑定事件后,设置 img 的 src 即刻加载。可以用来做图片的预加载。
1 | let image = new Image(); |
- 用来加载 js 外部文件
1 | let js = document.createElement('script'); |
unload 事件
与 load 事件相反,一般页面切换后触发,可用来强制的引用清除,防止内存泄漏
1 | window.addEventListener('unload', function(event) { |
resize 事件
当页面大小发生改变时触发,可以用此来获取一些窗口属性,用来做响应式开发。但是大小改变检测很灵敏,所以需要做防抖
1 | window.addEventListener('resize', function(event) { |
scroll 事件
scrollLeft 和 scrollTop
这是存在于 document 上的两个属性,分别代表滚动条已经滚动的高度和宽度。他们与 clientWidth 、clienHeight一样都是页面视口的属性,并非整个浏览器创口属性。
该事件是在 window 对象上发生的,与 scrollTop,scrollLeft 有关。监听该事件可以用来做导航栏的变化,同样要做防抖,否则容易卡顿
1 | window.addEventListener('scroll', function(event) { |
焦点事件
焦点一般只鼠标的焦点,虽然可以检测鼠标事件来监控,但是焦点可以通过键盘移动所以有专门的事件监控。焦点事件标准不一,但是 DOM3 统一了一个标准并且规定了它的发生顺序
当一个元素移动到另一个元素会依次触发以下事件;一般 blur 与 focus 常见
- focusout 在失去焦点元素上触发,会冒泡
- focusin 在获取焦点元素上触发,会冒泡
- blur 在失去元素上触发,不会冒泡
- DOMFocusOut 在失去焦点元素上触发,会冒泡; Opera 专有
- focus 在获取焦点元素上触发,不会冒泡
- DOMFocusIn 在获取焦点元素上触发,会冒泡; Opera 专有
鼠标与滚轮事件
鼠标点击与移动
主要是点击、双击、光标移入、移出、暗下、放开的操作;
- mouseenter 首次移入元素内部触发,不冒泡
- mouseleave ,移出元素触发,不冒泡
- mousemove 在元素内重复移动触发
- mouseout 移入另一个元素触发
- mousedown 按下鼠标触发
- mouseup 释放鼠标按键触发
- click 点击,只有按下事件和放开事件发生后才会触发,只是按下不会触发
- dblclick 双击,当且仅当连续两次 click 时触发
触发顺序:
- mousedown
- mouseup
- click //一次点击
- mousedown
- mouseup
- click //二次点击
- dblclick //触发双击
一般可以用于轮播图或者自动播放,当鼠标移入将其停止,移出又自动播放
1 | var doc = document.querySelector('input'); |
滚轮
- mousewheel
可用于获取该事件发送时鼠标属性,在任何元素上通过鼠标滚动即可触发。可以检测页面滚动是否来自鼠标。该事件冒泡。
触摸屏
上述事件在移动端上又有所不同
- 并不支持 dblclick ,双击只会放大
- 轻击不可单击或者没有绑定 click 的元素什么事件也不会发生
- 在可点击或者绑定 click 的前提下点击会触发 mousemove 。若该事件改变了内容将不会在发生其他事件,否则可以发生 down、up、click 事件
- mousemove 也会触发 mouseenter 和 mouseout
- 手指滚动页面时会触发 mousewheel 和 scroll 事件
键盘和文本事件
键盘
- keydown 敲击任意键时触发,若按住不放则不断触发
- keypress 敲击字符健时触发,若按住不放则不断触发
- keyup 释放键盘触发
按下字符键时依次触发 keydown -> keypress -> keyup
按下非字符键时依次触发 keydown -> keyup
另外发生 keydown 和 keyup 时,也可以通过 event 对象获取相应的键值(ASCII码值)。keyCode
1 | let doc = document.querySelector('input'); |
textInput 事件
触发条件
- 必须在可编辑区编辑
- 输入实际字符的键,不会包括删除、退格键等等
可以通过 event.data 获得键盘实际输入值而非 ASCII 码值
1 | let doc = document.querySelector('input'); |
设备事件
设备事件不是值页面内的事件,而是移动设备本身事件,比如翻转、是否走动。目前的草案中有四种类型事件
orientationchange ,苹果给 safari 添加的事件用来检测设备从横向观察模式到纵向观察模式。
它的值存在于 window.orientation 中。0 为纵向、90 为向左旋转、-90 为向右旋转
MozOrientation ,firefox 未检测设备而引入,依靠 event 的 x,y,z 来确定方向。该事件可能被替代
deviceorientation
devicemotion
触摸与手势事件
这一类事件是移动设备的事件核心
触摸事件
- touchstart 手指触摸屏幕触发
- touchmove 手指在屏幕上连续滑动触发。可以调用 preventDefault() 来阻值滑动
- touchend 手指离开屏幕
- touchcancel 系统停止跟踪触发