聊聊你对浏览器中事件模型的理解

127 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

核心描述

  • 浏览器的事件模型,主要基于 DOM Events 规范(DOM0 Events、DOM2 Events、DOM3 Events)
  • 浏览器的事件模型,主要是 DOM2 Events 规范中规定的事件流模型,分为3个阶段:
    • 事件捕获,最先发生,触发顺序 document -> <html> -> <body> -> target element
    • 到达目标,实际的目标元素 target element 接收到事件
    • 事件冒泡,到达目标元素后,触发顺序target element -> body -> html -> document
  • 所以可以理解为事件模型即浏览器对规范中上述的3个阶段的实现
  • 主流的浏览器基本都实现了事件捕获和事件冒泡,我们实际项目中用的更多的其实也是事件冒泡,如果要在事件捕获阶段触发事件响应逻辑,需要在 targetElement.addEventListener('click',handler,true ) 第三个参数中设置为 true,则表示在捕获阶段调用事件处理程序

知识拓展

  • 在 HTML 代码中触发事件的方式有三种:
    • 在元素中直接调用
    // btnOnClickHandle 方法为当前文档中可以直接访问的方法,需要注意其作用域
    <div id='myBtn' onclick="btnOnClickHandle">点击按钮</div>
    
    • 通过 onXXX 的方式调用
    const btnEle = document.getElementById('myBtn')
    myBtn.onclick = btnOnClickHandle () {
        // 处理按钮点击事件的相关逻辑
    }
    
    • 通过 addEventListener 的方式调用(推荐)
    const btnEle = document.getElementById('myBtn')
    myBtn.addEventListener('click', btnOnClickHandle, false)
    function btnOnClickHandle () {
        // 处理按钮点击事件的相关逻辑
    } 
    
  • 需要注意一些比较老的浏览器版本,对事件的处理方式有差异,以及兼容性的问题,所以在项目中需要做一些兼容处理,或是直接调用第三方库
    • 比如 IE8 或者更早版本的浏览器,并不支持 addEventListener 的调用,而是其特有的 attachEvent
    • 不过随着最新微软的公告,今后将不再继续支持 IE,而是推荐用户使用基于 Chrome 内核的 Edge 浏览器。所以作为开发者而言,我们应该将更多的精力放在实际业务中,而非无休止的兼容代码上,但这并不意味着我们应该放弃对这方面的思考和警惕,因为用户软件的更迭,总是有时间成本的,作为开发者我们还是应该了解到相关的技术原因,以便于更好的服务用户。
  • 除了理解事件模型,我们也应该关注不断更新的事件规范以及浏览器支持的事件类型,比如:
    • 用户界面事件(UIEvent):涉及与 BOM 交互的通用浏览器事件
    • 焦点事件(FocusEvent):在元素获得和失去焦点时触发
    • 鼠标事件(MouseEvent):使用鼠标在页面上执行某些操作时触发
    • 滚轮事件(WheelEvent):使用鼠标滚轮(或类似设备)时触发
    • 输入事件(InputEvent):向文档中输入文本时触发
    • 键盘事件(KeyboardEvent):使用键盘在页面上执行某些操作时触发
    • 合成事件(CompositionEvent):在使用某种 IME(Input Method Editor,输入法编辑器)输入字符时触发
    • ……
  • 需要注意,在 DOM2 Event 中的变化事件(Mutation Events)已经被废弃,并用 MutationObserver API取代,用于在 DOM 被修改时异步执行回调,可以观察整个文档、DOM 树的一部分,或某个元素。此外还能观察元素属性、子节点、文本,或者前三者任意组合的变化。
  • 事件性能优化,在 JavaScript 中,页面中事件处理程序的数量和页面整体性能直接相关。
    • 原因分析:
      • 每个函数都是对象,都占用内存空间,对象越多,性能越差
      • 为指定事件处理程序所需访问 DOM 的次数会先期造成整个页面交互的延迟
    • 解决方案:
      • 事件委托,利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件,通过回调方法的 target 对象的属性,来判断最终生效的元素,再做对应的处理,可以减少事件的绑定数量
      • 删除事件处理程序:在业务的合适时机,如模块隐藏或已完成逻辑处理,则对后续不再使用的事件监听进行移除
  • 事件拓展
    • 自定义右键菜单,oncontextmenu 事件,可以屏蔽或自定义右键菜单
    • 页面卸载事件,beforeunload 事件,可以在浏览器离开当前页面之前触发,需要注意的事,如果想要弹出对应的弹窗提示,则需要用户有过对应的交互操作才会触发,否则会直接离开页面,而不会弹出弹窗。

参考资料

浏览知识共享许可协议

本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。