浏览器事件机制详解

news2024/9/25 17:19:41

目录

前言

事件类型

鼠标事件

表单事件

窗口事件

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可以将事件对象传递到函数中

<button onclick="handleClick(event)">点击</button>
<button onclick="handleClick()">点击2</button>
<script>
    const handleClick = (e) => {
        console.log(e);
    }
</script>

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

<body>
    <button id="btn3">点击3</button>
    <script>
        const handleClick = (e) => {
            console.log(e);
        }
        btn3.onclick = handleClick
    </script>
</body>

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

<body>
    <button id="btn3">点击3</button>
    <script>
        const handleClick = (e) => {
            e.target.onclick = handleClick2
            console.log("handleClick");
        }
        const handleClick2 = (e) => {
            e.target.onclick = null
            console.log("handleClick2");
        }
        btn3.onclick = (e) => {
            console.log("onclick");
            e.target.onclick = handleClick
        }
    </script>
</body>

addEventListener(event)

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

<body>
    <button onclick="handleClick(event)">点击</button>
    <button onclick="handleClick()">点击2</button>
    <button id="btn3">点击3</button>
    <script>
        btn3.onclick = (e) => {
            fn1()
            fn2()
            fn3()
        }
        const fn1 = () => {
            console.log(111);
        }
        const fn2 = () => {
            console.log(222);
        }
        const fn3 = () => {
            console.log(333);
        }
    </script>
</body>

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

<body>
    <button id="btn1">点击</button>
    <button id="btn2">点击2</button>
    <script>
        btn1.addEventListener("click", handleClick1)
        btn1.addEventListener("click", handleClick3)
        btn2.addEventListener("click", handleClick2)
        function handleClick1(e) {
            console.log(e);
            console.log("handleClick1");
        }
        function handleClick2(e) {
            console.log(e);
            console.log("handleClick2");
        }
        function handleClick3(e) {
            console.log(e);
            console.log("handleClick3");
        }
    </script>
</body>

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

<body>
    <button id="btn1">点击</button>
    <script>
        btn1.addEventListener("click", handleClick1)
        function handleClick1(e) {
            console.log("handleClick1");
            btn1.removeEventListener("click", handleClick1)
        }
    </script>
</body>

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

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

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

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

这三个配置默认值都是false

事件触发

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

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

<body>
    <button id="btn1">按钮1</button>
    <button id="btn2">按钮2</button>
    <button id="btn3">按钮3</button>
    <script>
        btn1.addEventListener("click", (e) => {
            console.log("点击按钮1");
            btn2.click()
        })
        btn2.addEventListener("click", (e) => {
            console.log("点击按钮2");
            btn3.click()
        })
        btn3.addEventListener("click", (e) => {
            console.log("点击按钮3");
        })
    </script>
</body>

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

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

完整代码:

<body>
    <button id="btn1">按钮1</button>
    <button id="btn2">按钮2</button>
    <button id="btn3">按钮3</button>
    <script>

        btn1.addEventListener("click", (e) => {
            console.log("点击按钮1");
            btn2.click()
        })
        btn2.addEventListener("click", (e) => {
            console.log("点击按钮2");
            // 创建自定义点击事件
            const clickEvent = new Event('click');
            // 手动触发自定义事件
            btn3.dispatchEvent(clickEvent);
        })
        btn3.addEventListener("click", (e) => {
            console.log("点击按钮3");
        })
    </script>
</body>

事件流程

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

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

捕获阶段

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

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

<body>
    <div id="outBox">
        <div id="inBox">
            <div id="targetBox">
                点击
            </div>
        </div>
    </div>
    <script>
        outBox.addEventListener("click", (e) => {
            console.log("outBox");
        }, true)
        inBox.addEventListener("click", (e) => {
            console.log("inBox");
        }, true)
        targetBox.addEventListener("click", (e) => {
            console.log("targetBox");
        }, true)
        /*
        outBox
        inBox
        targetBox
        */
    </script>
</body>

目标阶段

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

冒泡阶段

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

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

<body>
    <div id="outBox">
        <div id="inBox">
            <div id="targetBox">
                点击
            </div>
        </div>
    </div>
    <script>
        outBox.addEventListener("click", (e) => {
            console.log("outBox");
        })
        inBox.addEventListener("click", (e) => {
            console.log("inBox");
        })
        targetBox.addEventListener("click", (e) => {
            console.log("targetBox");
        })
        /*
        targetBox
        inBox
        outBox
        */
    </script>
</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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1019235.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记一次 .NET 某电力系统 内存暴涨分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友找到我&#xff0c;说他生产上的程序有内存暴涨情况&#xff0c;让我帮忙看下怎么回事&#xff0c;最简单粗暴的方法就是让朋友在内存暴涨的时候抓一个dump下来&#xff0c;看一看大概就知道咋回事了。 二&#xff1a;Windbg 分…

Stream之实现原理分析

文章目录 1 Stream原理1.1 引言1.2 操作分类1.3 操作分类例子分析1.4 一种直白的实现方式1.5 Stream流水线解决方案1.5.1 操作如何记录1.5.2 操作如何叠加1.5.3 叠加之后的操作如何执行1.5.4 执行后的结果在哪里 1 Stream原理 1.1 引言 我们已经学会如何使用 Stream API&…

(vue的入门

vue的入门 一. Vue是什么二. Vue的特点及优势三. 使用Vue的详细步骤四. Vue的基本语法五. Vue的生命周期 一. Vue是什么 Vue&#xff08;发音为/“vjuː”/&#xff0c;类似于"view"&#xff09;是一套用于构建用户界面的渐进式JavaScript框架。它是一个开源的、轻量…

[字符串和内存函数]strcmp字符串函数的详解和模拟

strcmp函数 strcmp函数是一个用于比较两个字符串的C标准库函数。它的原型为&#xff1a; int strcmp(const char* str1, const char* str2);strcmp函数会比较str1和str2两个字符串的字符序列&#xff0c;并返回一个整数值来表示它们之间的大小关系。返回值的含义如下&#xff…

2023-简单点-IOU计算

机器视觉中的坐标体系 注意区分x,y坐标系和row,col排布 IOU交集 代码 def IOU(RecA, RecB):recA是坐标形式是[X[左上点],y[左上点],x[右下点],y[右下点]]#找到交集框的左上和右下点&#xff0c;可以计算交集面积xA max(RecA[0], RecB[0])yA max(RecA[1], RecB[1])xB min(R…

R reason ‘拒绝访问‘的解决方案

Win11系统 安装rms的时候报错&#xff1a; Error in loadNamespace(j <- i[[1L]], c(lib.loc, .libPaths()), versionCheck vI[[j]]) : namespace Matrix 1.5-4.1 is already loaded, but > 1.6.0 is required## 安装rms的时候报错&#xff0c;显示Matrix的版本太低…

SmFeN钐铁氮稀土永磁材料

钕铁硼作为第三代稀土永磁材料&#xff0c;因其优异的磁性能而获得了广泛应用。但钕铁硼磁体也存在居里温度低&#xff0c;矫顽力温度系数大以及化学稳定性差等缺点&#xff0c;并且镨、钕、镝、铽稀土资源的巨量消耗引发了人们对环境破坏和稀土资源保障可持续性的担忧。因此磁…

小红书产品文案怎么创作,达人投放技巧总结

每一个文案都有一个10万的梦。该如何快速写出爆款产品文案&#xff0c;让消费者在读到文案的第一分钟&#xff0c;就被产品深深吸引呢&#xff0c;今天来给大家分享下小红书产品文案怎么创作&#xff0c;达人投放技巧总结&#xff01; 一、文案的三大关键 影响一篇文案阅读量的…

天翎知识管理系统:强大的权限管理功能,保障知识安全

编者按&#xff1a; 知识管理系统的权限管理功能&#xff0c;可以帮助企业实现对知识库的精细化管理&#xff0c;保证知识库的安全性和稳定性。本文将介绍天翎知识管理系统的权限管理体系&#xff0c;通过权限管理&#xff0c;控制用户的编辑和审核权限&#xff0c;从而保证知识…

05. OpenFeign 服务调用

Spring Cloud 微服务系列文章&#xff0c;点击上方合集↑ 1. 简介 微服务架构中使用OpenFeign进行服务调用&#xff0c;OpenFeign提供了一种简洁的方式来定义和处理服务间的调用。 OpenFeign作为一个声明式的、模块化的HTTP客户端&#xff0c;通过接口的定义和注解的使用&…

docker安装es docker安装Elasticsearch windows linux

下载Elasticsearch和Kibana镜像docker pull elastic/elasticsearch:8.8.2 docker pull elastic/kibana:8.8.2 2. 设置max_map_countwindows&#xff1a; wsl -d docker-desktop sysctl -w vm.max_map_count262144 exit linux&#xff1a;cat /proc/sys/vm/max_map_count sys…

32:TX Text Control ActiveX/ASP.NET/WinForms/WPF Crack

TX Text Control ActiveX 32.0 添加操作“普通”样式表的能力。 2023 年 9 月 14 日 - 15:38新版本 特征 脚注- 在文档中插入与 Microsoft Word 兼容的脚注。脚注是一种文字处理功能&#xff0c;允许用户在页面底部插入附加信息。 可编辑的[普通]样式表- 添加了操作[普通]样式的…

虚拟人运营 | 金融品牌如何借助数字人IP撬动年轻圈层?

近年来&#xff0c;金融行业在不断尝试寻找一种新方式&#xff0c;去探索触及Z世代年轻圈层&#xff0c;数字人作为数字化时代的新介质&#xff0c;成为了金融业链接年轻人的新载体。 在银行的应用场景里&#xff0c;主要打造智能客服、数字员工、虚拟主播等。如浦发银行数字员…

李宏毅hw-6利用GAN生成动漫图像

一、查漏补缺、熟能生巧&#xff1a; 1.什么是转置卷积convTranspose、以及这种转置卷积怎么使用&#xff1a; &#xff08;1&#xff09;具体的原理直接看李沐老师的那个演示&#xff0c;非常清晰&#xff1a; 47 转置卷积【动手学深度学习v2】_哔哩哔哩_bilibili &#x…

无涯教程-JavaScript - INT函数

描述 INT函数将数字四舍五入到最接近的整数。 语法 INT (number)争论 Argument描述Required/OptionalNumberThe real number you want to round down to an integer.Required 适用性 Excel 2007,Excel 2010,Excel 2013,Excel 2016 Example JavaScript 中的 INT函数 - 无…

2023Web前端逻辑面试题

1、现有9个小球&#xff0c;已知其中一个球比其它的重&#xff0c;如何只用天平称2次就找出该球&#xff1f; ①把9个球分成三份&#xff0c;三个一份&#xff1b; ②拿出其中两份进行称量&#xff1b;会分为两种情况 若拿出的两份小球称量结果&#xff0c;重量相等&#xff1b…

idea 启动命令过长

报错: 运行 MyBatisPlusGenerator35Template 时出错。命令行过长。 缩短命令行并重新运行。 解决:

【JVM】Java类的加载机制!

一、类的生命周期 类加载过程包含&#xff1a;加载、验证、准备、解析和初始化 &#xff0c;一共包括5 个阶段。 &#xff08;1&#xff09;加载&#xff1a; 简单来说就是将java类的字节码文件加载到机器内存中。在加载类时&#xff0c;Java虚拟机必须完成以下3件事情&…

Matlab图像处理-HSI模型

HSI模型 HSI模型是从人的视觉系统出发&#xff0c;直接使用颜色三要素色调(Hue)、饱和度(Saturation)和亮度&#xff08;Intensity&#xff09;来描述颜色。 亮度是指人眼感知光线的明暗程度。光的能量越大&#xff0c;亮度就越大。 色调是颜色最重要的属性。 它决定了颜色的…

深入了解Vue.js框架:构建现代化的用户界面

目录 一.Vue前言介绍 二.Vue.js框架的核心功能与特性 三.MVVM的介绍 四.Vue的生命周期 五.库与框架的区别 1.库&#xff08;Library&#xff09;&#xff1a; 2.框架&#xff08;Framework&#xff09;&#xff1a; 六.Vue常用指令演示 1.v-model 2.v-on:click&…