JavaScript事件驱动模型
事件是什么
Event - Web API | MDN (mozilla.org)
事件流

如图,事件首先经历捕获过程,得到具体事件发生的元素或位置,接着经历冒泡过程向上传递。
一个完整的JS事件流是从window开始,最后回到window的一个过程。
事件流被分为三个阶段(1~ 5)捕获过程、(5~ 6)事件触发过程、(6~ 10)冒泡过程。
事件驱动模型
为什么要有事件,因为JavaScript的事件驱动模型的思想是按事件驱动,而非按代码顺序驱动。
在JavaScript事件驱动模型中,主要存在以下几个步骤:
\1. 事件监听:首先,我们需要对某个元素进行事件监听,例如点击事件、滚动事件等。这个步骤通常是通过addEventListener方法来实现的。
\2. 事件触发:当用户进行某些操作(如点击、滚动等)时,如果这些操作对应的事件被监听,那么这个事件就会被触发。
\3. 事件处理:当事件被触发后,会生成一个事件对象,这个事件对象会被传递给事件处理函数。事件处理函数会根据事件对象来进行相应的处理。
\4. 事件循环:JavaScript采用单线程模型,但是可以通过事件循环来处理多个事件。当一个事件被触发并处理完成后,JavaScript会查看是否还有其他的事件需要处理,如果有,就会继续处理下一个事件,这就是所谓的事件循环。
\5. 异步处理:由于JavaScript的单线程模型,对于一些需要长时间处理的任务,如果直接在主线程上进行处理,就会阻塞后面的代码执行。为了解决这个问题,JavaScript提供了异步处理机制。在异步处理中,长时间的任务会被放在一个单独的线程上处理,主线程在处理其他事件时,会不断检查这个线程的状态,当它处理完成后,主线程会接收到一个事件,然后执行相应的回调函数。
通过上述步骤,JavaScript事件驱动模型可以有效地处理用户的交互请求,使得Web页面具有良好的交互性。同时,通过异步处理机制,JavaScript还可以处理一些复杂和耗时的任务,提高了程序的执行效率。
作者:37手游前端组 链接:https://juejin.cn/post/7366532224567001098 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
事件流的处理
阻止冒泡
我们用 e.stopPropagation() 这个方法添加到某事件函数里的末尾,就可以做到阻止冒泡事件。但是IE8及以下不支持e.stopPropagation() 方法,所以就封装一个方法。
javascript<script> function stopPropagation(e) { e = e || window.event; if(e.stopPropagation) { e.stopPropagation(); //W3C阻止冒泡方法 } else { e.cancelBubble = true; //IE阻止冒泡方法 } } </script>在child(区域①)的点击事件函数最后添加 stopPropagation(e) 方法
javascript<script> child.onclick = function (e) { alert("你点击了child区域"); stopPropagation(e); }; </script>
- 再次点击区域①的时候就只弹出
你点击了child区域这一个弹框。
事件捕获
讲事件捕获之前先了解下**addEventListener()**方法:
addEventListener()定义与用法
- document.addEventListener() 方法用于向文档添加事件句柄。
- 提示: 可以使用 document.removeEventListener() 方法来移除 addEventListener() 方法添加的 事件句柄。
- 提示:使用 element.addEventListener() 方法为指定元素添加事件句柄。
语法:
javascriptdocument.addEventListener(event, function, useCapture);参数值:
参数 描述 event 必需。描述事件名称的字符串。 注意: 不要使用 “on” 前缀。例如,使用 “click” 来取代 “onclick”。 提示: 所有 HTML DOM 事件,可以查看我们完整的 HTML DOM Event 对象参考手册。 function 必需。描述了事件触发后执行的函数。 当事件触发时,事件对象会作为第一个参数传入函数。事件对象的类型取决于特定的事件。例如, “click” 事件属于 MouseEvent(鼠标事件) 对象。 useCapture 可选。布尔值,指定事件是否在捕获或冒泡阶段执行。 可能值: true - 事件句柄在捕获阶段执行 false - 默认。事件句柄在冒泡阶段执行 从上面的表格中,可以看到参数值
useCapture,为true的时候,事件在捕获过程中就会执行。 用代码感受下:javascript<div class="div1" id="div1"> <div class="div2" id="div2"></div> </div> <script> var div1 = document.getElementById("div1"); var div2 = document.getElementById("div2"); div1.addEventListener("click",function () { alert("你点击了div1") },false); //false 在冒泡阶段执行 div2.addEventListener("click",function () { alert("你点击了div2") },false); </script>当点击子元素 div2 的时候,会先弹出
你点击了div2的弹框后,再弹出你点击了div1的弹框。如果将div1的addEventListener方法中的最后的布尔参数值改成true,来看看什么效果:
javascript<div class="div1" id="div1"> <div class="div2" id="div2"></div> </div> <script> var div1 = document.getElementById("div1"); var div2 = document.getElementById("div2"); div1.addEventListener("click",function () { alert("你点击了div1") },true); //false 在捕获阶段执行 div2.addEventListener("click",function () { alert("你点击了div2") },false); </script>当点击子元素 div2 的时候,会先弹出
你点击了div1的弹框后,再弹出你点击了div2的弹框。,和之前的顺序就不一样了。
事件委托
不对某个元素进行事件绑定,而是将事件绑定到父元素或祖先元素上,通过事件冒泡触发这个事件。如不对小li绑定事件,对ol/ul绑定事件,提高了性能,且在元素动态增删时无需再绑定事件,尤其是通过请求获取的li项
浏览器默认行为
浏览器默认行为是浏览器自动为事件触发后添加的行为,目的是为了使用户体验更好。
浏览器默认行为有哪些
1.点击链接的默认行为是跳转到链接指定的 URL。
2.表单提交的默认行为是将表单数据发送给服务器,并刷新当前页面或加载新页面。
3.按下回车键时,如果焦点在文本输入框中,浏览器会默认提交最近的父表单。
4.鼠标右键弹出菜单的默认行为是显示浏览器自带的上下文菜单。
5.拖拽元素的默认行为是移动元素或复制元素,具体取决于拖拽操作的设置。
6.在滚动条上点击并拖动的默认行为是滚动页面。
7.在输入框中按下退格键的默认行为是删除光标前的一个字符。
有时候,这些默认行为和开发人员的想法不一致,因此需要阻止默认行为。
如何阻止默认行为
性能优化:告知浏览器我没有阻止默认事件
在上面的分析中,会出现浏览器需要知道开发人员有没有添加preventDefault方法来阻止默认事件,但是事件冒泡的寻找preventDefault方法需要事件,可能造成抖动和延迟。
因此开发人员可以告知浏览器我没有阻止默认事件。
addEventListener的可选项passive: true向浏览器发出信号,表明处理程序将不会调用preventDefault()。为什么需要这样做?
移动设备上会发生一些事件,例如
touchmove(当用户在屏幕上移动手指时),默认情况下会导致滚动,但是可以使用处理程序的preventDefault()来阻止滚动。因此,当浏览器检测到此类事件时,它必须首先处理所有处理程序,然后如果没有任何地方调用
preventDefault,则页面可以继续滚动。但这可能会导致 UI 中不必要的延迟和“抖动”。
passive: true选项告诉浏览器,处理程序不会取消滚动。然后浏览器立即滚动页面以提供最大程度的流畅体验,并通过某种方式处理事件。对于某些浏览器(Firefox,Chrome),默认情况下,
touchstart和touchmove事件的passive为true。
拓展
addEventListener的几个参数:
addEventListener(type, listener[, useCapture || options])
addEventListener("click", fun[, true || {capture: true, passive: true, once: true}])由于 touchstart 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault() 方法阻止,那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。视频里也说了,即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。
视频里还说了,有 80% 的滚动事件监听器是不会阻止默认行为的,也就是说大部分情况下,浏览器是白等了。所以,passive 监听器诞生了,passive 的意思是“顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了。
所以passive:true的意思就是告诉浏览器,不会阻止默认事件,你放心的滚动吧,不用等待了。 ————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.csdn.net/qq_40409143/article/details/116259308
addEventListener() 的参数之passive 的用法_addeventlistener passive-CSDN博客
addEventListener的移除:
不必理会passive项,直接移除即可。
自定义事件与触发
引导:事件的编程触发
事件可以是用户行为事件,也可以是浏览器行为事件。用户行为事件中的点击事件,如果想要不点击,可以触发吗?
const myButton = document.getElementById('myButton')
myButton.click()常用的场景是隐藏Input框(其上传文件),使用代码触发文件的上传。
自定义事件
定义一个自定义事件
let myEvent = new Event('myEvent')触发一个自定义事件
document.dispatchEvent(myEvent)监听一个自定义事件
document.addEventListener('myEvent', function(e) {
console.log('myEvent 发生了!');
});异步事件处理
请求也是一个事件,此时不希望请求的时候代码被阻塞,所以使用Promise,详见Promise章节。
参考资料
JavaScript事件驱动模型:一窥究竟! - 掘金 (juejin.cn)
让页面滑动流畅得飞起的新特性:Passive Event Listeners-CSDN博客
passive 的事件监听器 - 紫云飞 - 博客园 (cnblogs.com)
