JavaScript事件机制

        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.hqwc.cn/news/492388.html

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

相关文章

蓝桥杯-最小砝码

知识点&#xff1a;本题主要考察任何一个物体都可以用 3进制表示。 #include <iostream> #include<cmath> using namespace std; //知识点:任何一个物体都可以用 3进制表示 int main() { int n; cin >> n; int sum 0; for (int i 0;; i)…

2024.02.23作业

1. 尝试处理普通信号 #include "test.h"#define MAXSIZE 128void handler(int signo) {if (SIGINT signo){printf("用户按下了 ctrl c 键\n");} }int main(int argc, char const *argv[]) {if (signal(SIGINT, SIG_IGN) SIG_ERR){perror("signal …

【AIGC】基于深度学习的图像生成与增强技术

摘要&#xff1a; 本论文探讨基于深度学习的图像生成与增强技术在图像处理和计算机视觉领域的应用。我们综合分析了主流的深度学习模型&#xff0c;特别是生成对抗网络&#xff08;GAN&#xff09;和变分自编码器&#xff08;VAE&#xff09;等&#xff0c;并就它们在实际应用中…

数据采集设备:安装过程的要点与注意事项

在当今的数据驱动世界&#xff0c;数据采集设备在各行各业中发挥着至关重要的作用。从工业生产到环境监测&#xff0c;再到医疗诊断&#xff0c;数据采集设备为我们提供了大量有价值的信息。然而&#xff0c;要想充分发挥这些设备的潜力&#xff0c;首先需要确保它们被正确地安…

网络层的DDoS攻击与应用层的DDoS攻击之间的区别

DDoS攻击&#xff08;即“分布是拒绝服务攻击”&#xff09;&#xff0c;是基于DoS的特殊形式的拒绝服务攻击&#xff0c;是一种分布式、协作的大规模攻击方式&#xff0c;主要瞄准一些企业或政府部门的网站发起攻击。根据攻击原理和方式的区别&#xff0c;可以把DDoS攻击分为两…

物联网常见通信协议

从应用的角度出发&#xff0c;物联网系统可分解为物联设备、网关、云端、用户终端。 物联设备可分为两类&#xff1a; 一种因其支持 TCP/IP&#xff0c;可以直接接入物联网&#xff0c;如 Wi-Fi、GPRS/3G/4G 等&#xff1b; 一种则需要网关&#xff08;实现协议转换&#xff…

【小尘送书-第十四期】《高效使用Redis:一书学透数据存储与高可用集群》

大家好&#xff0c;我是小尘&#xff0c;欢迎你的关注&#xff01;大家可以一起交流学习&#xff01;欢迎大家在CSDN后台私信我&#xff01;一起讨论学习&#xff0c;讨论如何找到满意的工作&#xff01; &#x1f468;‍&#x1f4bb;博主主页&#xff1a;小尘要自信 &#x1…

如何使用群晖NAS中FTP服务开启与使用固定地址远程上传下载本地文件?

文章目录 1. 群晖安装Cpolar2. 创建FTP公网地址3. 开启群晖FTP服务4. 群晖FTP远程连接5. 固定FTP公网地址6. 固定FTP地址连接 本文主要介绍如何在群晖NAS中开启FTP服务并结合cpolar内网穿透工具&#xff0c;实现使用固定公网地址远程访问群晖FTP服务实现文件上传下载。 Cpolar内…

函数防抖?一个vue指令搞定

说在前面 &#x1f388;防抖&#xff08;Debounce&#xff09;在前端开发中是一种常用的技术&#xff0c;它的作用是限制某个操作在短时间内的频繁触发&#xff0c;只有在一定的间隔时间内才执行相应的操作。 什么是防抖 函数防抖 方法是一个函数&#xff0c;它的执行被延迟了…

115/200V 航空交流静变电源 115/200V机场直线加电设备

一、 115/200V 航空交流静变电源简介&#xff1a; 随着全球科技的快速发展和航空产业的不断进步&#xff0c;飞机的性能和功能要求日益提升&#xff0c;对电源设备的需求也更加严格。其中&#xff0c;“115/200V 航空交流静变电源”作为飞机的115/200V机场直线加电设备&#x…

每日OJ题_牛客WY33 计算糖果(IO型OJ)

目录 牛客WY33 计算糖果 解析代码 牛客WY33 计算糖果 计算糖果_牛客题霸_牛客网 解析代码 #include <iostream> using namespace std; bool isTrue(int a) {if (a < 30 && a > -30){return true;}return false; } int main() {int x1, x2, x3, x4;cin …

数学建模【插值与拟合】

一、插值与拟合简介 在数学建模过程中&#xff0c;通常要处理由试验、测量得到的大量数据或一些过于复杂而不便于计算的函数表达式&#xff0c;针对此情况&#xff0c;很自然的想法就是&#xff0c;构造一个简单的函数作为要考察数据或复杂函数的近似。插值和拟合就可以解决这…