• 浏览器事件机制详解


    目录

    前言

    事件类型

    鼠标事件

    表单事件

    窗口事件

    DOM事件

    多媒体事件

    拖拽与放置事件

    移动设备事件

    剪切板事件

    错误事件

    过渡、动画事件

    事件监听

    on+event

    addEventListener(event)

    事件触发

    事件流程

    捕获阶段

    目标阶段

    冒泡阶段

    事件对象

    总结

    相关代码:


    前言

    浏览器的事件机制是web前端面试及开发过程中绕不开的话题,可以说一切用户操作或者浏览器的行为都离不了事件,它允许开发者通过JS处理用户的操作,并处理操作逻辑,将结果反馈给用户。本篇文章将深入浏览器事件的运行机制,和大家一起探讨其强大的功能及广泛的用法

    事件类型

    浏览器的事件,参考事件列表大致可以分为以下几类:

    鼠标事件

    常见的鼠标事件有

    1. click:点击元素时触发
    2. dblclick:双击元素时触发
    3. mouseover:鼠标指针进入元素时触发
    4. mouseout:鼠标指针离开元素时触发
    5. mousemove:鼠标在元素上移动时触发
    6. mousedown:按下鼠标按钮时触发
    7. mouseup:释放鼠标按钮时触发
    8. contextmenu:右键点击元素时触发
    9. wheel:当使用鼠标滚轮时触发。

    键盘事件

    常见的键盘事件:

    1. keydown:按下键盘上的键时触发
    2. keyup:释放键盘上的键时触发
    3. keypress:按住键不放时持续触发

    表单事件

    常用的表单事件:

    1. submit:用户提交表单时触发
    2. input:在表单元素中输入内容时触发
    3. change:表单元素的值发生改变时触发(适用于输入框、下拉列表等)
    4. focus:元素获得焦点时触发
    5. blur:元素失去焦点时触发

    窗口事件

    窗口事件一般作用于window对象上,常用的事件有:

    1. load:页面完全加载时触发
    2. unload:页面即将被卸载时触发
    3. resize:窗口大小发生变化时触发
    4. scroll:页面滚动时触发

    DOM事件

    Dom一般用于监听文档操作:

    1. DOMContentLoaded:DOM树构建完成,但在所有资源加载完成前触发
    2. DOMNodeInserted:新的子节点插入到元素中时触发
    3. DOMNodeRemoved:子节点从元素中移除时触发
    4. DOMAttrModified:元素属性发生变化时触发

    多媒体事件

    多媒体事件一般在audio,video标签上进行操作:

    1. play:媒体开始播放时触发
    2. pause:暂停播放时触发
    3. ended:当媒体播放结束时触发

    拖拽与放置事件

    当给元素增加draggable属性时,标签就具备被拖拽功能,我们可以使用以下事件监听标签拖拽操作:

    1. dragstart:拖拽操作开始时触发
    2. drag:在拖拽过程中持续触发
    3. dragend:当拖拽操作结束时触发
    4. dragenter:被拖拽的元素进入目标元素时触发
    5. dragover:被拖拽元素在目标元素上移动时触发
    6. dragleave:被拖拽的元素离开目标元素时触发
    7. drop:拖拽的元素在目标元素上释放时触发

    移动设备事件

    移动事件类似鼠标操作:

    1. touchstart:当用户触摸屏幕时触发
    2. touchmove:在用户滑动触摸屏幕时持续触发
    3. touchend:当用户停止触摸屏幕时触发
    4. touchcancel:在触摸事件被取消时触发

    剪切板事件

    剪切板事件是当用户选中文本时在标签上操作剪切板的动作:

    1. cut:用户执行剪切操作时触发
    2. copy:用户执行复制操作时触发
    3. paste:用户执行粘贴操作时触发。

    错误事件

    错误事件一般出现在加载文件,资源,音视频,链接等地方:

    1. error:当资源加载或操作过程中出现错误时触发

    过渡、动画事件

    当标签使用过渡或者动画时,会触发以下事件:

    1. animationstart:当CSS动画开始时触发
    2. animationend:当CSS动画结束时触发
    3. animationiteration:当CSS动画重复播放时触发
    4. transitionstart:当CSS过渡开始时触发
    5. transitionend:当CSS过渡结束时触发

    事件监听

    事件监听有两种方式,分别是on+event和addEventListener(event)两种方式(event表示上面的事件名称)

    on+event

    on+event可以直接在HTML标签中使用,添加参数event可以将事件对象传递到函数中

    1. <button onclick="handleClick(event)">点击button>
    2. <button onclick="handleClick()">点击2button>
    3. <script>
    4. const handleClick = (e) => {
    5. console.log(e);
    6. }
    7. script>

    在JS中也可以通过element.on+event的方式给标签添加事件监听

    1. <body>
    2. <button id="btn3">点击3button>
    3. <script>
    4. const handleClick = (e) => {
    5. console.log(e);
    6. }
    7. btn3.onclick = handleClick
    8. script>
    9. body>

    在JS中使用on+event的方式给标签添加事件监听时,后面的监听函数会覆盖前面的,当我们要取消监听时直接给on+event赋值null即可,思考下面的代码:

    1. <body>
    2. <button id="btn3">点击3button>
    3. <script>
    4. const handleClick = (e) => {
    5. e.target.onclick = handleClick2
    6. console.log("handleClick");
    7. }
    8. const handleClick2 = (e) => {
    9. e.target.onclick = null
    10. console.log("handleClick2");
    11. }
    12. btn3.onclick = (e) => {
    13. console.log("onclick");
    14. e.target.onclick = handleClick
    15. }
    16. script>
    17. body>

    addEventListener(event)

    当我们使用on+event给标签添加事件时,后定义的函数会覆盖前面的,这就会导致函数耦合度变高,维护性降低,举个例子,下面的代码中点击btn3时就会触发三个fn,此时如果我需要解除fn1的执行,只能修改onclick函数的逻辑,如何解决这种问题呢?

    1. <body>
    2. <button onclick="handleClick(event)">点击button>
    3. <button onclick="handleClick()">点击2button>
    4. <button id="btn3">点击3button>
    5. <script>
    6. btn3.onclick = (e) => {
    7. fn1()
    8. fn2()
    9. fn3()
    10. }
    11. const fn1 = () => {
    12. console.log(111);
    13. }
    14. const fn2 = () => {
    15. console.log(222);
    16. }
    17. const fn3 = () => {
    18. console.log(333);
    19. }
    20. script>
    21. body>

    JS提供了element.addEventListener(event,fn)的方式用于给标签添加事件监听,使用addEventListener函数可以给标签添加多个事件钩子函数

    1. <body>
    2. <button id="btn1">点击button>
    3. <button id="btn2">点击2button>
    4. <script>
    5. btn1.addEventListener("click", handleClick1)
    6. btn1.addEventListener("click", handleClick3)
    7. btn2.addEventListener("click", handleClick2)
    8. function handleClick1(e) {
    9. console.log(e);
    10. console.log("handleClick1");
    11. }
    12. function handleClick2(e) {
    13. console.log(e);
    14. console.log("handleClick2");
    15. }
    16. function handleClick3(e) {
    17. console.log(e);
    18. console.log("handleClick3");
    19. }
    20. script>
    21. body>

    通过removeEventListener移除某个事件的监听,思考下面的代码,我实现了一个事件的once功能,执行一次后立即注销事件

    1. <body>
    2. <button id="btn1">点击button>
    3. <script>
    4. btn1.addEventListener("click", handleClick1)
    5. function handleClick1(e) {
    6. console.log("handleClick1");
    7. btn1.removeEventListener("click", handleClick1)
    8. }
    9. script>
    10. body>

    addEventListener第二个参数除了可以是函数外,还可以传入一个对象,当其为对象类型时,可以传入一个handleEvent属性充当事件函数

    1. btn1.addEventListener("click", {
    2. handleEvent: handleClick2
    3. })

    addEventListener的第三个参数是一个布尔值或配置对象,用于指定更多选项,当其类似是布尔时,true表示监听捕获阶段反之则是监听冒泡;当这个参数传入的是一个对象时,里面包含以下配置:

    • capture:布尔值,指定事件是否在捕获阶段触发(true)还是在冒泡阶段触发(false)
    • once:布尔值,指定事件是否仅在第一次触发时调用监听器
    • passive: 布尔值,指定监听器是否为 passively(如果为 true,则浏览器知道监听器不会调用 preventDefault())
    1. <body>
    2. <button id="btn1">点击button>
    3. <script>
    4. btn1.addEventListener("click", handleClick3, {
    5. once: true,
    6. passive: true,
    7. capture: true
    8. })
    9. function handleClick3(e) {
    10. console.log("handleClick3");
    11. }
    12. script>
    13. body>

    这三个配置默认值都是false

    事件触发

    事件触发有两种方式,分别是用户操作或者浏览器触发的和手动使用触发器触发,手动触发的两种方式分别使用element的event名和dispatchEvent进行触发事件

    第一种是使用event名直接调用标签的操作,比如:click(),focus()等

    1. <body>
    2. <button id="btn1">按钮1button>
    3. <button id="btn2">按钮2button>
    4. <button id="btn3">按钮3button>
    5. <script>
    6. btn1.addEventListener("click", (e) => {
    7. console.log("点击按钮1");
    8. btn2.click()
    9. })
    10. btn2.addEventListener("click", (e) => {
    11. console.log("点击按钮2");
    12. btn3.click()
    13. })
    14. btn3.addEventListener("click", (e) => {
    15. console.log("点击按钮3");
    16. })
    17. script>
    18. body>

    第二种是使用dispatchEvent进行事件抛发

    1. // 创建自定义点击事件
    2. const clickEvent = new Event('click');
    3. // 手动触发自定义事件
    4. btn3.dispatchEvent(clickEvent);

    完整代码:

    1. <button id="btn1">按钮1button>
    2. <button id="btn2">按钮2button>
    3. <button id="btn3">按钮3button>
    4. <script>
    5. btn1.addEventListener("click", (e) => {
    6. console.log("点击按钮1");
    7. btn2.click()
    8. })
    9. btn2.addEventListener("click", (e) => {
    10. console.log("点击按钮2");
    11. // 创建自定义点击事件
    12. const clickEvent = new Event('click');
    13. // 手动触发自定义事件
    14. btn3.dispatchEvent(clickEvent);
    15. })
    16. btn3.addEventListener("click", (e) => {
    17. console.log("点击按钮3");
    18. })
    19. script>

    事件流程

    一个完整的事件流程包括三个阶段,分别是捕获(Captur),目标(Target)和冒泡(Bubbling),过程如下图。

    任何在以下三种阶段注册的事件监听器会在每个阶段被触发。

    捕获阶段

    事件首先从根元素(通常是html)向下传播到目标元素的过程。在这个阶段,事件会依次经过DOM树中的每个祖先元素,直到达到事件的目标元素。

    在捕获阶段增加的事件监听函数会在事件的捕获阶段触发,在上文我们提到了给addEventListener增加配置项可以使handler在事件捕获时调用,思考下面的代码:

    1. <body>
    2. <div id="outBox">
    3. <div id="inBox">
    4. <div id="targetBox">
    5. 点击
    6. div>
    7. div>
    8. div>
    9. <script>
    10. outBox.addEventListener("click", (e) => {
    11. console.log("outBox");
    12. }, true)
    13. inBox.addEventListener("click", (e) => {
    14. console.log("inBox");
    15. }, true)
    16. targetBox.addEventListener("click", (e) => {
    17. console.log("targetBox");
    18. }, true)
    19. /*
    20. outBox
    21. inBox
    22. targetBox
    23. */
    24. script>
    25. body>

    目标阶段

    一旦事件达到了目标元素,就会在目标元素上触发事件。这是事件的目标,通常是用户交互的对象。

    冒泡阶段

    事件从目标元素开始,然后向上冒泡到根元素的过程。在这个阶段,事件会依次经过DOM树中的每个祖先元素,直到达到根元素。

    冒泡阶段与捕获相反,在冒泡阶段增加的事件监听函数会在事件的冒泡阶段触发,将addEventListener的capture配置项改成false或者默认不传配置参数,就会使handler回调在事件冒泡时触发,思考下面的代码:

    1. <body>
    2. <div id="outBox">
    3. <div id="inBox">
    4. <div id="targetBox">
    5. 点击
    6. div>
    7. div>
    8. div>
    9. <script>
    10. outBox.addEventListener("click", (e) => {
    11. console.log("outBox");
    12. })
    13. inBox.addEventListener("click", (e) => {
    14. console.log("inBox");
    15. })
    16. targetBox.addEventListener("click", (e) => {
    17. console.log("targetBox");
    18. })
    19. /*
    20. targetBox
    21. inBox
    22. outBox
    23. */
    24. script>
    25. body>

    tips:使用on属性监听标签的事件时,不支持在捕获阶段的监听

    事件对象

    事件对象是指事件的钩子函数的event参数,常用的属性有:

    • type:表示事件的类型,如"click"、"keydown"等
    • target:表示触发事件的元素,即事件的目标元素
    • currentTarget:表示当前正在处理事件的元素,通常是事件监听器所附加的元素
    • preventDefault():该方法用于取消事件的默认行为,例如阻止点击链接跳转或提交表单
    • stopPropagation():该方法用于停止事件的传播,阻止事件冒泡或捕获,只会执行目标阶段的handler
    • eventPhase:表示事件所处的阶段,可以是捕获阶段、目标阶段或冒泡阶段
    • timeStamp:表示事件的时间戳,通常是事件发生时的毫秒数
    • clientX和clientY:表示事件发生时的鼠标指针在视口(浏览器窗口)中的坐标
    • pageX和pageY:表示事件发生时的鼠标指针在页面文档中的坐标
    • keyCode和key:表示键盘事件的按键代码和按键的名称
    • targetTouches和changedTouches:表示触摸事件中涉及的触摸点信息。

    总结

    浏览器事件机制是前端开发中非常重要的一部分,本文深入探讨了浏览器事件机制的各个方面,包括不同类型的事件、事件监听的方式、事件触发方式、事件流程以及事件对象的使用。对于开发者来说,熟悉浏览器事件机制是非常重要的,它可以帮助开发者实现各种交互和动态效果,提升用户体验。

    以上就是文章全部内容了,感谢你看到了最后,如果觉得文章不错的话,还望三连支持一下,谢谢!

    相关代码:

    myCode: 基于js的一些小案例或者项目 - Gitee.com

  • 相关阅读:
    Linux服务器如何暂停程序与恢复进程
    R语言(3) 数据框操作
    Android 系统启动 <init 进程> 笔记【1】
    HCL Domino LEAP与新的软件下载门户站点
    AndroidStudio配置adb环境变量和adb的使用
    《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(12)-Fiddler设置IOS手机抓包,你知多少???
    开题报告:基于java摄影图片分享网站系统 毕业设计论文开题报告模板
    【数据聚类】第三章第三节3:类K-Means算法之模糊K-均值算法(FCM算法)
    2.Node- 回调函数的用法
    实验室管理系统
  • 原文地址:https://blog.csdn.net/time_____/article/details/132498780