JavaScript事件机制

news2024/12/23 9:33:29

        JavaScript事件机制描述的是事件在DOM里面的传递顺序,以及可以对这些事件做出如何的响应。

DOM事件流存在三个阶段:

①事件捕获阶段(从window对象传导到目标节点)、

②处于目标阶段(在目标节点上触发)、

③事件冒泡阶段(从目标节点传导回window对象)。

        在现代浏览器中,事件传播包括两个阶段:捕获阶段和冒泡阶段。默认情况下,事件首先处于捕获阶段,然后进入目标元素,最后再冒泡到更高层次的父元素。

事件绑定

        要想让JavaScript对用户的操作作出响应,首先要对DOM元素绑定事件处理函数。所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称。

在JavaScript中,有三种常用的绑定事件的方法:

  • 在DOM元素中直接绑定(使用onXxx属性[如onclick]可以直接在DOM元素上绑定事件处理函数)
<input type="button" value="click me" onclick="hello()"/> <!--点击按钮时,调用hello函数--> 
<script>
    function hello(){alert("Hello world!");}
</script>
  • 在JavaScript代码中绑定
<input type="button" value="click me" id="btn" />
<script>
    document.querySelector("#btn").onclick = function(){//为按钮元素设置onclick事件处理程序
        alert("Hello world!");
    }
</script>
  • 绑定事件监听函数(使用addEventListener方法可以为一个事件源绑定多个事件处理函数,并可以指定是否在事件捕获阶段执行处理函数)
<input type="button" value="click me" id="btn" />
<script>
    document.querySelector("#btn").addEventListener("click",function(){//按钮元素添加第一个click事件监听器,使用捕获阶段(第三个参数为false),这是默认的事件处理阶段 
        alert("Hello world!");
    },false);
    document.querySelector("#btn").addEventListener("click",function(){//为按钮元素添加第二个click事件监听器,使用冒泡阶段(第三个参数为true),这是事件传播的开始阶段
        alert("Hello world!");
    },true);
</script>

事件处理常见问题与方案详解

  • 1. 事件冒泡与事件捕获的冲突

问题:当在DOM树的不同层级上注册了相同类型的事件处理程序时,可能会因为事件冒泡或事件捕获而导致不期望的行为。

解决方案:明确指定事件处理程序的执行阶段(冒泡或捕获),并在需要时使用event.stopPropagation()来阻止事件进一步传播。

  • 2. 默认行为的阻止

问题:某些事件具有默认行为,如表单的提交、超链接的跳转等。如果不加以处理,这些默认行为可能会干扰事件处理程序的执行。

解决方案:使用event.preventDefault()来阻止事件的默认行为。

  • 3. 事件委托的误用

问题:事件委托可以减少代码量,但如果不正确地使用,可能会导致事件处理程序无法正确执行。

解决方案:确保事件处理程序能够正确地识别和处理实际触发事件的元素。使用event.target来获取实际触发事件的元素,并根据需要执行相应的操作。

  • 4. 内存泄漏

问题:在事件处理程序中引用外部变量或对象时,如果不正确地管理这些引用,可能会导致内存泄漏。

解决方案:确保在不需要时解除对外部变量或对象的引用,可以使用空值(null)来替代引用,或使用闭包来管理引用。

  • 5. 跨浏览器兼容性问题

问题:不同浏览器对事件处理的支持程度不同,可能会导致代码在某些浏览器上无法正常工作。

解决方案:使用事件标准化库(如jQuery)来简化事件处理,并确保代码在多种浏览器上的兼容性。另外,也可以手动编写兼容性代码,检查浏览器对特定事件或方法的支持情况,并相应地调整代码。

  • 6. 事件处理程序的执行顺序问题

问题:当多个事件处理程序被注册到同一个对象上时,它们的执行顺序可能会导致不期望的结果。

解决方案:明确指定事件处理程序的执行顺序,可以使用addEventListener的第三个参数(useCapture)来控制执行阶段,并用event.target识别实际触发事件的元素。true捕获阶段,false(或省略)冒泡阶段。

事件冒泡与事件捕获

        事件冒泡是指当一个元素上的事件被触发后,事件会从该元素开始沿着DOM树向上冒泡到更高层次的父元素,直至达到根节点。这意味着如果一个子元素上的事件被触发,其父元素上绑定的相同事件也会被触发。事件冒泡是默认的事件传播方式。事件冒泡顺序是由内到外进行事件传播,直到根节点。在处理子元素事件时特别有用,因为它允许我们在父元素上设置事件处理程序来统一处理多个子元素的事件。

document.getElementById('button').addEventListener('click', function(event) {  
  console.log('button clicked (bubbling)');  
}, false); //为button添加事件处理程序,只在冒泡阶段执行  
document.getElementById('inner').addEventListener('click', function(event) {  
  console.log('inner div clicked (bubbling)');  
}, false); //为inner div添加事件处理程序,只在冒泡阶段执行  
document.getElementById('outer').addEventListener('click', function(event) {  
  console.log('outer div clicked (bubbling)');  
}, false); //为outer div添加事件处理程序,只在冒泡阶段执行  
//****输出****:
//button clicked (bubbling)  
//inner div clicked (bubbling)  
//outer div clicked (bubbling)

屏蔽事件冒泡:

        使用event.stopPropagation()方法可以阻止事件继续向上冒泡,从而阻止父元素上绑定的相同事件的触发。

        事件捕获是事件冒泡的另一种模式。在事件捕获中,事件会从根节点开始,依次向下沿着DOM树传播,直至达到事件的目标元素。然后,事件才会在目标元素上触发。通俗的理解就是,当鼠标点击或触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。在处理顶层元素(如document对象)上的事件时特别有用,因为它允许我们在事件到达目标元素之前进行拦截和处理。

document.getElementById('button').addEventListener('click', function(event) {  
  console.log('button clicked (capturing)');  
}, true); //为button添加事件处理程序,只在捕获阶段执行   
document.getElementById('inner').addEventListener('click', function(event) {  
  console.log('inner div clicked (capturing)');  
}, true); //为inner div添加事件处理程序,只在捕获阶段执行   
document.getElementById('outer').addEventListener('click', function(event) {  
  console.log('outer div clicked (capturing)');  
}, true); //为outer div添加事件处理程序,只在捕获阶段执行  
//****输出****:
//outer div clicked (capturing)  
//inner div clicked (capturing)  
//button clicked (capturing)

        在实际开发中,通常会选择在冒泡阶段处理事件,因为大多数浏览器都支持事件冒泡,这也是处理事件委托等常见模式的推荐方式。然而,在某些情况下,如需要阻止事件进一步传播或需要更早地处理事件时,我们可能会使用事件捕获。

事件回调机制

        事件回调是当某个特定的事件发生时执行的函数或代码块。例如,当用户点击一个按钮时,可能会触发一个click事件,然后执行与该事件关联的回调函数。

        回调通常是一个函数,它作为参数传递给其他函数,并在某个特定条件满足时由那些函数调用。在事件驱动的编程中,当某个事件发生时(如点击按钮、页面加载完成等),与该事件相关联的回调函数就会被执行。

        事件回调机制是一种异步编程模式,允许在特定事件发生时执行特定的函数或代码块。这种机制允许代码在异步操作中保持响应性,因为它允许程序在等待某些操作(如网络请求)完成时继续执行其他任务。

<!DOCTYPE html>  
<html lang="en">  
<head>  
<meta charset="UTF-8">  
<title>事件回调示例</title>  
</head>  
<body>  
    <button id="myButton">点击我</button>  
    <script>  
        //定义一个回调函数  
        function handleButtonClick() {  
          alert('按钮被点击了!');  
        }  
        //获取按钮元素  
        var button = document.getElementById('myButton');  
        //为按钮添加点击事件监听器,并将回调函数作为参数传递给另一个函数(事件监听器)
        button.addEventListener('click', handleButtonClick); //按钮被点击时,会被调用
    </script>  
</body>  
</html>

事件循环机制

        事件循环是JavaScript引擎用于处理异步事件和回调函数的机制。

一次事件循环的执行:

执行当前宏任务:事件循环首先从宏任务队列(也称为任务队列)中取出第一个任务执行。这包括了诸如setTimeout、setInterval、I/O 操作、用户交互事件(如点击或键盘事件)等任务。

执行所有微任务:当前宏任务执行完毕后,事件循环会检查微任务队列。如果队列中有微任务(例如,由Promise.then()或MutationObserver等产生的任务),事件循环会依次执行队列中的所有微任务,直到微任务队列清空。微任务的执行是连续的,不会中断。

渲染UI(如果需要):在浏览器环境中,一旦微任务队列清空,浏览器会检查是否需要执行UI渲染。通常,浏览器的UI渲染会在执行完所有微任务之后,下一个宏任务开始之前进行。

继续下一个宏任务:完成当前宏任务、所有微任务以及可能的UI渲染之后,事件循环会回到第一步,从宏任务队列中取出下一个任务,开始新一轮的执行。

在同一次事件循环中,微任务(Microtasks)总是在当前宏任务(Macrotasks)之执行。

①执行一个 宏任务(栈中没有就从事件队列中获取)
②执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
③宏任务执行完毕后,立即执行当前微任务队列中的所有 微任务(依次执行)
④当前宏任务执行完毕,开始检查渲染,然后 GUI线程接管渲染
⑤渲染完毕后,JS线程继续接管,开始 下一个宏任务(从事件队列中获取)

        上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

事件委托原理与实现

        事件委托其实也叫事件代理。事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果,可以通过使用事件代理,将绑定多个事件的操作变为只绑定一次的操作它允许将事件处理程序绑定到父元素而不是每个子元素上,通过在父元素上监听事件,可以通过事件冒泡的方式捕获所有子元素上触发的事件,从而避免直接为每个子元素都绑定事件处理程序。

优点:

①提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用。

②动态添加或移除子元素时,不需要重新绑定事件处理程序,因为事件处理程序是在父元素上绑定的。

③只要定义一个监听函数,就能处理多个子节点的事件,且以后再添加子节点,监听函数依然有效。

注意事项:

①性能考虑:虽然事件委托可以减少内存占用和提高性能,但过多的事件冒泡可能会导致性能下降,尤其是在大型DOM树中。

②事件处理逻辑:需要确保事件处理逻辑能够正确地识别和处理实际触发事件的子元素。

③不支持事件捕获:事件委托通常用于冒泡阶段,不支持事件捕获阶段。

事件委托实现:

①确定事件源:首先,确定要委托事件的父元素或祖先元素。

②绑定事件:使用addEventListener方法,在父元素或祖先元素上绑定事件处理程序。

③事件处理:在事件处理程序中,使用event.target来识别实际触发事件的子元素。

④条件判断:根据event.target来执行相应的逻辑。这通常涉及检查event.target是否匹配特定的子元素或具有特定的属性。

⑤执行操作:如果条件满足,则执行相应的操作。

//假设有一个id为parent的父元素,它包含多个子元素,我们想要在子元素被点击时执行一个操作  
// 绑定事件到父元素  
document.getElementById('parent').addEventListener('click', function(event) {  
  // event.target是实际被点击的元素  
  var target = event.target;  
  // 检查被点击的元素是否是我们感兴趣的子元素  
  if (target.matches('.child-class-name')) {  
    // 执行相应的操作  
    console.log('Child element clicked!');  
  }  
});

取消事件委托:

        使用event.preventDefault()方法可以取消事件的默认行为,从而阻止事件委托的触发。通常用于链接的点击、表单的提交等。

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

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

相关文章

JVM虚拟机初步了解

各位小伙伴们大家好&#xff0c;欢迎来到这个小扎扎的专栏 总结 | 提效 | 拓展&#xff0c;在这个系列专栏中记录了博主在学习期间总结的大块知识点&#xff1b;以及日常工作中遇到的各种技术点 ┗|&#xff40;O′|┛ &#x1f306; 题目速览 1. JVM的位置2. JVM的体系结构3…

python(ch2)

可变长编码和不可变长编码 可变长编码是指不同字符使用不同数量的字节进行编码。例如&#xff0c;UTF-8 编码中&#xff0c;ASCII 字符使用 1 个字节编码&#xff0c;而其他语言的字符使用 2 个或更多字节编码。 不可变长编码是指所有字符都使用相同数量的字节进行编码。例如…

【数据结构与算法】动态规划法解题20240227

动态规划法 一、什么是动态规划二、动态规划的解题步骤三、509. 斐波那契数1、动规五部曲&#xff1a; 四、70. 爬楼梯1、动规五部曲&#xff1a; 五、746. 使用最小花费爬楼梯1、动规五部曲&#xff1a; 一、什么是动态规划 动态规划&#xff0c;英文&#xff1a;Dynamic Pro…

物资管理新篇章:Java+SpringBoot实战

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【Python_Zebra斑马打印机编程学习笔记(四)】ZPL的一些简单指令

ZPL的一些简单指令 ZPL的一些简单指令前言一、ZPL 介绍二、ZPL 语法解析1、标签开始、标签结束2、标签原点位置设置3、标签长度设置4、标签文本打印深度设置5、标签打印宽度设置6、标签方向设置7、标签元素定位8、标签绘制矩形9、标签输入字段10、标签设置字段字体、大小11、标…

【论文阅读-PRIVGUARD】Day3:1-2节

PRIVGUARD: Privacy Regulation Compliance Made Easier&#xff08;PRIVGUARD&#xff1a;更轻松地遵守隐私规定&#xff09; 摘要 持续遵守如GDPR和CCPA等隐私法规已经成为从小型创业公司到商业巨头的公司的一项昂贵负担。罪魁祸首是当今合规过程中对人工审核的严重依赖&…

Pytorch训练RCAN QAT超分模型

Pytorch训练RCAN QAT超分模型 版本信息测试步骤准备数据集创建容器生成文件列表创建文件列表的代码执行脚本,生成文件列表训练RCAN模型准备工作修改开源代码编写训练代码执行训练脚本可视化本文以RCAN超分模型为例,演示了QAT的训练过程,步骤如下: 先训练FP32模型再加载FP32训练…

壹[1],图像源

1&#xff0c;工具名称:图像源 2&#xff0c;参数说明 2.1&#xff0c;图像源 注&#xff1a; 本地图像&#xff0c;使用本地图片以及本地图像文件夹 相机&#xff0c;连接的相机 SDK&#xff0c;使用相机的SDK&#xff0c;而不是海康SDK 2.2&#xff0c;像素格式 注&…

Jeecg项目部署

说明&#xff1a;Jeecg是一款低代码开发平台&#xff0c;简单说是一款现成的项目&#xff0c;该项目集成了许多功能&#xff0c;我们可以在这个项目之上开发自己的业务代码。 本文介绍Jeecg项目的部署&#xff0c;包括后端jeecg-boot项目、前端vue3项目。前端项目在本地Window…

VScode连接远端服务器一直输入密码解决方法

文章目录 1 关闭远程连接2打开命令面板3 输入remote-ssh: kill vs code server on host… 1 关闭远程连接 2打开命令面板 3 输入remote-ssh: kill vs code server on host… remote-ssh: kill vs code server on host… 然后一路回车(选中出问题的主机)&#xff0c;输一遍密码…

真正理解微软Windows程序运行机制——窗口机制(第一部分)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天说说Windows程序的运行机制。经常被问到MFC到底是一个什么技术&#xff0c;为了解释这个我之前还写过帖子&#xff0c;但是很多人还是不理解。其实这没什么&#xff0c;我在学生时代也被这个问题困绕过。…

【日常聊聊】Sora- 探索AI视频模型的无限可能

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 方向一&#xff1a;技术解析 方向二&#xff1a;应用场景 方向三&#xff1a;未来展望 方向四&#xff1a;伦理与创意 方向…

深入理解JS的执行上下文、词法作用域和闭包(下)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【PX4SimulinkGazebo联合仿真】在Simulink中使用ROS2控制无人机沿自定义圆形轨迹正向飞行(带偏航角控制)并在Gazebo中可视化

在Simulink中使用ROS2控制无人机沿自定义圆形轨迹正向飞行&#xff08;带偏航角控制&#xff09;并在Gazebo中可视化 系统架构Matlab官方例程Control a Simulated UAV Using ROS 2 and PX4 Bridge运行所需的环境配置PX4&Simulink&Gazebo联合仿真实现方法建立Simulink模…

vue3自定义实现悬浮固定按钮组件

目录 一、需求描述二、代码解读三、结果展示 一、需求描述 需要5个固定的悬浮圆&#xff0c;居于页面的右侧。鼠标悬浮在圆上面会显示对应的文字提示其中包含返回顶部悬浮圆&#xff0c;当页面滑至底部时出现&#xff0c;点击页面滑到顶部。点击按钮会给出弹窗 二、代码解读…

LCR 172. 统计目标成绩的出现次数

解题思路&#xff1a;二分查找 题解一 class Solution {public int countTarget(int[] scores, int target) {// 搜索右边界 rightint i 0, j scores.length - 1;while(i < j) {int m (i j) / 2;if(scores[m] < target) i m 1;else j m - 1;}int right i;// 若数…

UE5 C++ Gas开发 学习记录(一)

一个新坑,在TPS的空余时间学习 创建了自己,敌人的BaseCharacter和子类,创建了Gamemode,创建了Controller AuraCharacterBase.h // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include &…

H264/H265基本编码参数1

本文主要讲解一些视频编码相关的基本概念 像素 像素是图像的基本单元&#xff0c;一个个像素就组成了图像。你可以认为像素就是图像中的一个点。我们来直观地看看像素是怎么组成图像的。在下面这张图中&#xff0c;你可以看到一个个方块&#xff0c;这些方块就是像素。 分辨…

【GameFramework框架内置模块】4、内置模块之调试器(Debugger)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a;…

开启数字内容创作的新时代

目录 技术解析 未来展望 技术解析 Sora是一款由OpenAI开发的先进AI视频模型&#xff0c;其技术架构基于深度学习和自然语言处理技术。该模型的核心算法原理包括使用深度神经网络进行视频内容的理解、生成和互动。 在技术架构方面&#xff0c;Sora采用了一种混合的神经网络结…