浏览器事件机制

一、事件是什么?

        事件是用户操作页面时发生的交互动作,像是click/move,除了这个还可以是文档加载,窗口滚动和大小调整。事件被封装成一个event对象,包含了属性和方法。

        现代浏览器有三种事件模型:

  • DOM0级事件模型:这种模型不会传播,没有事件流的概念,可以在网页中直接定义监听函数,也可以通过js属性来指定监听函数,直接在dom对象上注册事件名字。
  • IE事件模型:一次事件有两个过程,事件处理阶段和事件冒泡阶段。事件处理会首先执行目标元素绑定的监听事件,然后从目标元素冒泡到document,依次检查经过的节点是否绑定事件监听函数,如果有则执行。通过attachEvent来添加监听函数,可以添加多个,会依次执行。
  • DOM2级事件模型:事件有三个过程,事件捕获、事件触发和事件冒泡,下面详说。

二、事件触发的过程

DOM2级事件模型,事件触发有三个阶段:

  • window往事件触发处传播,遇到注册的捕获事件会触发(捕获阶段)
  • 触发注册事件
  • 触发处往window处传播,遇到注册的冒泡事件会触发(冒泡阶段)

         捕获过程即是从window开始,window->document->html->body->div->ul->li,一层层进去,找到触发事件。事件冒泡也是同样的原理,从触发源处一层层往父级元素走,回到window。

        事件触发一般来说是按照上面的顺序,但是如果给一个body中的子节点同时注册冒泡事件和捕获事件,事件触发会按照注册的顺序执行。

addEventListener(事件,回调函数,true:捕获阶段/false:冒泡阶段)

//会先打印冒泡,后打印捕获
node.addEventlistener("click",event=>{
    console.log("冒泡")
},false)

node addEventListener("click",event=>{
    console.log("捕获")
},true)

        一般来说,如果只希望事件只触发在目标上,这时候可以使用stopPropagation来阻止事件的进一步传播,多用于阻止事件冒泡,也可以阻止事件捕获。

        同样能实现阻止事件的还有stopImmediatePropagation,且能阻止该事件目标执行别的注册事件。

node.addEventListener('click',event=>{
    event.stopImmediatePropagation()
    console.log('冒泡')    
},false)

//点击node只会执行上面的函数,下面这个函数不会执行
node.addEventListener('click',event=>{
    console.log('捕获')
},true)

 

三、事件委托

        本质上是利用了浏览器事件冒泡的机制,因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。

场景:给页面的所有a标签添加click事件,可能会想到:

document.addEventListener('click',functrion(e){
    if(e.target.nodeName=="A")  //返回的是大写
        console.log("a")
},false)

        但是这些a标签可能包含一些span、img等元素,如果点击到了a标签里的这些元素,就不会触发click事件,毕竟事件是绑定在a上的。

        这时使用事件委托,当点击目标时,会逐级向上查找,直到找到a标签为止。

document.addEventListener("click",function(e){
    var node=e.target;
    while(node.parentNode.nodeName!="BODY"){
        if(node.nodeName=="A"){
            console.log("a");
            break;
        }
        node=node.parentNode;
    }
},false)

 

四、事件循环 EventLoop

        学习JavaScript时大家都会有一个疑问,这个语言明明是个单线程语言,为什么还会有异步?怎么处理异步的?

首先了解一些基本的概念:

  1. JavaScript有一个main thread主线程和call-stack调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。调用栈是一种后进先出的数据结构,执行代码时,通过不同函数的执行上下文压入栈中来确保代码的有序执行
  2. 浏览器内核中有多种线程在工作,其中有个叫JS引擎线程,负责解析运行JavaScript脚本。
  3. JavaScript中的任务分为同步任务和异步任务,同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务会在异步有结果后将注册的回调函数添加到任务队列中,等待主线程空闲时被调到栈中执行。任务队列先进先出。
  4. 任务队列分为宏任务和微任务。
  5. 当调用栈中的事件执行完毕,JS引擎会判断微任务队列中是否有任务可以执行,如果有就压入栈,微任务队列执行完毕后再去执行宏任务队列中的任务。

宏任务和微任务

  • 微任务:promise的回调、node中的process.next Tick、对DOM变化监听的MutationObserver
  • 宏任务:script脚本的执行、setTimeout、setInterval、setImmediate一类的定时事件,I/O操作,UI渲染等

先举个例子:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
  1. 整体script作为第一个宏任务进入主进程,遇到console.log,输出script start
  2. 遇到setTimeout,其回调函数被分发到宏任务中
  3. 遇到promise,then函数被分发到微任务中
  4. 遇到console.log,输出script end
  5. 执行微任务,输出promise1,promise2
  6. 执行宏任务,输出setTimeout

输出顺序是:script start - script end - promise1 - promise2 - setTimeout

再来一个:

console.log('script start');

setTimeout(function() {
  console.log('timeout1');
}, 10);

new Promise(resolve => {
    console.log('promise1');
    resolve();
    setTimeout(() => console.log('timeout2'), 10);
}).then(function() {
    console.log('then1')
})

console.log('script end');
  1. 先执行script整体代码,输出script start
  2. 遇到setTimeout,分发到宏任务
  3. new Promise中代码立刻执行,输出promise1,然后执行resolve()
  4. 遇到setTimeout2,分发到宏任务
  5. 遇到then,分发到微任务
  6. 输出script end,整体script运行完
  7. 执行微任务,输出then
  8. 执行宏任务,输出timeout1,timeout2

输出顺序是:script start - promise1 - script end - then1 - timeout1 - timeout2


总结

学废了学废了

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
浏览器事件循环机制是一种执行模型,用于处理浏览器中的异步任务。它的基本原理是通过一个事件队列来管理异步任务的执行顺序,确保每个任务都能按照规定的顺序得到执行。 事件循环机制的核心是事件循环线程,它负责处理所有的异步任务。当浏览器遇到一个异步任务时,它会将任务添加到事件队列中,然后继续执行同步任务。当同步任务执行完毕后,事件循环线程会开始从事件队列中取出任务,按照顺序执行它们。 事件循环机制的另一个重要的概念是回调函数,它是异步任务完成后需要执行的函数。当浏览器取出一个任务时,它会检查该任务是否有回调函数,如果有,就执行该函数。如果没有,就直接进入下一个任务。 事件循环机制的实现还涉及到一些微任务和宏任务的概念。微任务是指在当前任务执行完毕后立即执行的任务,而宏任务则是指需要等到下一个事件循环周期才执行的任务。常见的微任务包括Promise的then()和catch()方法、MutationObserver的回调函数等,而常见的宏任务包括setTimeout、setInterval、requestAnimationFrame等。 总之,浏览器事件循环机制的原理是通过一个事件队列来管理异步任务的执行顺序,确保每个任务都能按照规定的顺序得到执行。它是浏览器中实现异步任务的核心机制,理解它对于开发高效的异步代码非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值