浏览器事件机制详解

目录

前言

事件类型

鼠标事件

表单事件

窗口事件

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 = handleClick2console.log("handleClick");}const handleClick2 = (e) => {e.target.onclick = nullconsole.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)/*outBoxinBoxtargetBox*/</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");})/*targetBoxinBoxoutBox*/</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.mzph.cn/news/81432.shtml

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

相关文章

Pyhton压缩JS代码

文章目录 1.安装依赖2.目录结构3.代码4.执行结果 1.安装依赖 pip install jsmin2.目录结构 3.代码 import jsmindef run(src_path, tgt_path):with open(src_path, "r", encodingutf-8) as input_file:with open(tgt_path, "w", encodingutf-8) as outpu…

【Java 基础篇】Java 字节流详解:从入门到精通

Java中的字节流是处理二进制数据的关键工具之一。无论是文件操作、网络通信还是数据处理&#xff0c;字节流都发挥着重要作用。本文将从基础概念开始&#xff0c;深入探讨Java字节流的使用&#xff0c;旨在帮助初学者理解和掌握这一重要主题。 什么是字节流&#xff1f; 在Ja…

Claude 使用指南 | 可与GPT-4媲美的语言模型

本文全程干货&#xff0c;让你轻松使用上claude&#xff0c;这也是目前体验cluade的唯一途径&#xff01;废话不多说&#xff0c;直接上教程&#xff0c;cluade的能力不逊于GPT4&#xff0c;号称是ChatGPT4.0最强竞品。相对Chatgpt来说&#xff0c;Claude不仅是完全免费的&…

springboot集成kafka

创建工程 父工程pom 父工程做了子工程管理和包管理 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocati…

深度解析NLP文本摘要技术:定义、应用与PyTorch实战

目录 1. 概述1.1 什么是文本摘要&#xff1f;1.2 为什么需要文本摘要&#xff1f; 2. 发展历程2.1 早期技术2.2 统计方法的崛起2.3 深度学习的应用2.4 文本摘要的演变趋势 3. 主要任务3.1 单文档摘要3.2 多文档摘要3.3 信息性摘要 vs. 背景摘要3.4 实时摘要 4. 主要类型4.1 抽取…

【Leetcode热题】打卡day1——10

目录 1、两数之和 - 哈希表 1、两数之和 - 哈希表 1. 两数之和 思路&#xff1a; 建立map&#xff0c;mp[nums[i]]i 存储值所对应的下标 顺序遍历每一个元素&#xff0c;先查找mp中是否存在与nums[i]匹配的值&#xff08;target-nums[i]&#xff09; 如果存在&#xff0c;则返…

伊朗市场最全开发攻略

伊朗是是古代波斯帝国的继承者&#xff0c;是中东和北非区第二大经济体&#xff0c;紧随沙特阿拉伯之后。它拥有庞大的人口、丰富的自然资源和逐渐增长的实力。也是世界上拥有最大储油量的国家之一&#xff0c;石油、天然气和铜等战略资源得天独厚。 南非的拉马福萨在2023年金…

Python爬虫自动切换爬虫ip的完美方案

在进行网络爬虫时&#xff0c;经常会遇到需要切换爬虫ip的情况&#xff0c;以绕过限制或保护自己的爬虫请求。今天&#xff0c;我将为你介绍Python爬虫中自动切换爬虫ip的终极方案&#xff0c;让你的爬虫更加高效稳定。 步骤一&#xff1a;准备爬虫ip池 首先&#xff0c;你需要…

设计模式之十:状态模式

状态模式通过改变对象内部的状态来帮助对象控制自己的行为。 这是一张状态图&#xff0c;其中每个圆圈都是一个状态。 最简单&#xff0c;第一反应的实现就是使用一个变量来控制状态值&#xff0c;并在方法内书写条件代码来处理不同情况。 package headfirst.designpatterns.…

为什么有了IP地址还需要MAC地址?

上午好&#xff0c;我的网工朋友。 今天想和你聊聊Mac地址。 到底啥是Mac地址&#xff1f;官方直译是媒体存取控制位址&#xff0c;是一个用来确认网络设备位置的位址。 在OSI模型中&#xff0c;第三层网络层负责IP地址&#xff0c;第二层数据链路层则负责MAC位址。 MAC地址…

达梦数据库阻塞与死锁查询

一、数据库阻塞 1.查询被阻塞的信息和引起阻塞的信息 SELECT SYSDATE STATTIME, DATEDIFF(SS, S1.LAST_SEND_TIME, SYSDATE) SS , 被阻塞的信息 WT , S1.SESS_ID WT_SESS_ID, S1.SQL_TEXT WT_SQL_TEXT, S1.STATE WT_STATE, S1.TRX_ID WT_TRX_ID, S1.USER_NAME WT_USER_NAME, …

NSS [HNCTF 2022 WEEK2]ohmywordpress(CVE-2022-0760)

NSS [HNCTF 2022 WEEK2]ohmywordpress&#xff08;CVE-2022-0760&#xff09; 题目描述&#xff1a;flag在数据库里面。 开题&#xff1a; 顺着按钮一直点下去会发现出现一个按钮叫安装WordPress 安装完之后的界面&#xff0c;有一个搜索框。 F12看看network。 又出现了这个…

ATF(TF-A) EL3 SPMC威胁模型-安全检测与评估

安全之安全(security)博客目录导读 ATF(TF-A) 威胁模型汇总 目录 一、简介 二、评估目标 1、数据流图 三、威胁分析 1、信任边界 2、资产 3、威胁代理 4、威胁类型 5、威胁评估 5.1 端点在直接请求/响应调用中模拟发送方FF-A ID 5.2 端点在直接请求/响应调用中模拟…

XUbuntu22.04之查找进程号pidof、pgrep总结(一百九十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Unity工具——LightTransition(光照过渡)

需求描述 在游戏中&#xff0c;开发者为了让玩家更直接地看到待拾取的物品从而为其添加一种闪烁效果&#xff0c;或者模拟现实中闪烁的灯光效果&#xff0c;我能够想到的一种方案则是通过控制光照强度来实现&#xff0c;那么本篇文章我们就尝试通过这个方案来实现一下&#xff…

LabVIEW通过IEC61508标准验证ITER联锁系统

LabVIEW通过IEC61508标准验证ITER联锁系统 保护环境要求系统能够保护机器免受工厂系统故障或机器危险操作造成的严重损坏。负责此功能的ITER系统是联锁控制系统&#xff08;ICS&#xff09;。该系统通过中央联锁系统&#xff08;CIS&#xff09;监督和控制不同的工厂联锁系统&…

react | react-router-dom v6 结合 antd 面包屑 |嵌套路由

大致需求图示如上&#xff1a; 需求&#xff1a; 1. 点击page2默认进入/page2/中国 2. 在中国界面选择省份&#xff0c;进入浙江省 3. 在浙江省中选择市&#xff0c;进入杭州市 4. 选择大学&#xff0c;进入浙江大学 5. 点击面包屑中某个tab&#xff0c;进入对应tab界面&…

Ubuntu22.04_如何调试ROS2_humble的源代码

这里的源码&#xff0c;是指的ros2 humble的官方源码。如果是自己手撸的节点或相关源码&#xff0c;请参考本人以前的贴子&#xff0c; Ubuntu20.04vscode快速调试ROS通用程序_ubuntu20.04vscode那个版本和ros 兼容_高精度计算机视觉的博客-CSDN博客 Ubuntu20.04&#xff0b;…

HTML 知识扫盲

写在前面 HTML 是一门超文本标记语言&#xff0c;不管你听没听说过 HTML&#xff0c;但在网上冲浪的途中你无时不刻都在与它接触&#xff0c;他遍布在每个你看得到的互联网的角落。其实对于笔者而言&#xff0c;我已经断断续续地学习过这门语言&#xff0c;经过时间的磋磨&…

利用cms主题构造木马(CVE-2022-26965)

简介 CVE-2022-26965是Pluck CMS 4.7.16版本存在一个远程shell上传执行漏洞。 攻击者可利用此漏洞通过构造恶意的主题包进行上传并执行&#xff0c;未经授权访问服务器&#xff0c;造成潜在的安全隐患。 过程 1.打开环境&#xff0c;查看源码&#xff0c;发现login.php 2.进…