JS Web API 事件

写写有关事件流 事件绑定 冒泡与捕获

事件流

  • 在浏览器发展的过程中,开发团队遇到了一个问题。那就是页面中的哪一部分拥有特定的事件?
  • 可以想象画在一张纸上的一组同心圆,如果你把手指放在圆心上,那么你的手指指向的其实不是一个圆,而是纸上所有的圆。放到实际页面中就是,你点击一个按钮,事实上你还同时点击了按钮所有的父元素。
  • 开发团队的问题就在于,当点击按钮时,是按钮最外层的父元素先收到事件并执行,还是具体元素先收到事件并执行?所以这儿引入了事件流的概念。

事件流所描述的就是从页面中接受事件顺序

  • 因为有两种观点,所以事件流也分为两种,分别是事件冒泡和事件捕获。现行的主流是事件冒泡

DOM事件流的三个阶段

  • DOM结构是一个树型结构,当一个DOM元素触发一个事件时,该事件会在元素结点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为 DOM 事件流(DOM event flow )。
  • 传播(Propagation)按顺序分三个阶段
    1. 捕获阶段(capture phrase,从根节点window到目标节点,即最近的、最精确的元素节点)
    2. 目标阶段(target phrase,到达目标事件位置(事发地),触发事件)
    3. 冒泡阶段(bubbling phrase,从目标节点到根节点 )
  • 三个阶段图示
  • 当一个元素同时绑定冒泡和捕获事件如果没有子节点,那么就会按照JavaScript顺序执行(即先绑定的btn冒泡事件,后绑定的btn捕获事件,则先冒泡后捕获,反之亦然),当一个元素同时绑定冒泡和捕获事件,如果有子节点那么会先捕获

事件绑定(addEventListener()

1
2
3
4
const btn = document.getElementById('btn1')
btn.addEventListener('click', event => {
console.log('clicked')
})
  • 注意:事件绑定函数可接受的参数1为事件对象,事件对象.target即触发事件的DOM元素
  • addEventListener的参数3默认为false,即表示使用事件冒泡模式,设置为true则代表使用事件捕获模式

事件对象.preventDefault()取消默认动作

  • 事件对象.preventDefault()取消事件的默认动作
  • 例如:如果addEventListener()的参数1是 “submit”,通过调用preventDefault()可以阻止提交表单。
    • 注意,如果 Event 对象的 cancelable 属性是 fasle,那么就没有默认动作,或者不能阻止默认动作。无论哪种情况,调用该方法都没有作用。
  • 例子:下面“事件代理”的例子中有所体现。

事件冒泡

  • 事件发生后,这个事件就要开始冒泡(从里到外),先在最内层的元素上被触发,然后逐级向上传递到包含该元素的更外层元素,直到传递到最外层的文档对象。为什么要传播呢?因为事件源本身(可能)并没有处理事件的能力,即处理事件的函数(方法)并未绑定在该事件源上。
    • 例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身可能没有绑定js函数去处理这个事件,所以这个点击事件必须从这个按钮传播出去,从而到达能够处理这个事件的代码中(例如我们给按钮的绑定一个onclick事件函数),或者按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则冒泡到父级去处理。
    • 补充:捕获事件则是从外往里 ,先由文档对象捕获到事件,然后再逐级向下传递到触发该事件的最内层元素(只有特殊需要再使用事件捕获即可)
  • 冒泡例子冒泡例子
    • 点击事件的传播顺序:p、div2、body、document、window
  • 阻止冒泡例子:给“取消”的文字上绑定click事件,由于取消按钮太多,所以直接绑定在body上,给“激活”的文字上也绑定click事件:阻止冒泡例子

事件对象.stopPropagation()阻止冒泡

  • 事件对象.stopPropagation()可让事件传播到目标阶段后停止传播
  • 即:终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。
  • 注意:使用该方法的节点事件还是会被触发,只是不冒泡去让别的元素触发事件了

事件代理

  • 注意:不要滥用(一般用在瀑布流或者元素数量太多/结构复杂的情况下)
  • 事件代理是基于事件冒泡机制的。
  • 事件代理,即需要绑定事件的元素太多时将事件绑定在他们的父元素上
  • 例子:希望给所有a标签绑定点击事件(假设点击 加载更多 时会继续加载a标签,a标签的数量不定)事件代理例子

好处

  • 代码简洁
  • 减少浏览器内存占用

键盘事件

  • 重点:
    • 优先使用keydown
    • 优先使用event.key
  • 参考文章

2个主要事件

  • keyup/keydown+addEventListener或者onKeyuponKeydown
  • 注意:推荐使用keydown 更胜于 keyup
    • 虽然 keydownkeyup 事件都涵盖了所有的键,并且被大多数浏览器支持,但它们之间仍有一些不同之处,使得 keydown 更胜于 keyup
    • keydown 事件在浏览器处理键之前触发,而 keyup 事件在浏览器处理键之后触发。如果你取消了 keydown 事件(例如使用 event.preventDefault()),浏览器的操作将被取消。在 keyup 事件的情况下,即使你取消了该事件,浏览器的操作也不会被取消

支持绑定键盘事件的html元素

  • 在 HTML 中,并非所有的元素都能绑定键盘事件 ,只有那些可以获取焦点(可获得用户输入)的元素才能绑定键盘事件
    • 这些元素通常包括输入框(<input> 元素)、文本区域(<textarea> 元素)以及可编辑的元素(例如,<div contenteditable="true">
  • 支持键盘事件的常见 HTML 元素:
    • <input> 元素(包括不同类型的输入,如文本、密码、数字等)
    • <textarea> 元素
    • <select> 元素
    • <button> 元素
    • <a> 元素(当具有 href 属性时)
    • <div> 元素(当设置 contenteditable="true" 属性时)
    • <span> 元素(当设置 contenteditable="true" 属性时)
  • 这些元素可以通过添加事件监听器来捕获和处理键盘事件,如 keyupkeydown 等。其他元素,如 <p><h1><div>(默认不可编辑)、<img> 等,通常不支持直接绑定键盘事件,因为它们无法接收用户的键盘输入。
  • 注意:
    • 即使某个元素可以绑定键盘事件,也可能会受到其他因素的影响,如元素的焦点状态、浏览器的默认行为等
    • 在绑定键盘事件时,需要确保元素具有焦点或者通过其他方式使其处于焦点状态,以便能够正常触发键盘事件
    • 如果想要绑定键盘事件的元素不属于上述列出的支持键盘事件的元素类型,可以考虑使用 JavaScript 的全局事件监听器(如 document+addEventListener)来捕获和处理键盘事件

交互式键盘事件游乐场

  • 在这里输入任意键即可查看有关它的上下文信息

KeyboardEvent 属性/方法

  • 常用的event属性:
    • altKey ctrlKey shiftKey:按下几个特殊键会返回布尔值
      • 当需要判断按键组合时他们会十分有用,比如判断是否按下ctrl+z
        1
        2
        3
        4
        5
        document.getElementById("to_focus").addEventListener("keydown", function(event) {
        if (event.ctrlKey && event.key === "z") {
        // 做些事,可以是一个“撤销”操作
        }
        });
    • key:按下的键的实际值
      • 小写字母(a)和大写字母(A)的 event.code 值相同,但 event.key 值表示的是实际输入的字母
    • 【已弃用】keyCode:返回按下键的数字代码
      • 如不需要兼容老旧的浏览器,接下来都用key
      • 如果你必须支持较旧的浏览器,较好的替代属性是 event.which
        1
        2
        3
        4
        5
        6
        7
        8
        window.addEventListener("keydown", function (event) {

        if (event.key !== undefined) {
        //使用 KeyboardEvent.key 处理事件
        } else if (event.which !== undefined) {
        // 使用 KeyboardEvent.which 处理事件
        }
        });
  • 键盘事件值的完整列表

相关题目

通用的事件监听函数

  • 【简单版】可监听 普通绑定通用事件监听函数通用的事件监听函数【简单版】
  • 【正式版】可以同时监听 普通绑定 和 代理绑定 的通用事件监听函数通用的事件监听函数【正式版】普通绑定时this就指向触发事件的DOM元素事件代理时就需要事件对象.target来获取触发事件的DOM元素

event.target

  • 事件对象的target属性event.target代表触发当前事件的DOM元素
  • 比如 <button id="myButton">Click me</button>

Element.matches()

  • 如果元素匹配指定的选择器字符串,则Element.matches()返回true,否则返回false。
  • 语法let result = element.matches(selectorString);
    • result 的值为 true 或 false.
    • selectorString 是个css选择器字符串.
  • MDN文档

描述事件冒泡的流程

  • 基于DOM树形结构
  • 事件会顺着触发元素往上冒泡
  • 应用场景:事件代理

无限下拉的图片列表,如何监听每个图片的点击?

  • 事件代理
  • 用e.target获取触发元素
  • 用matches来判断是否是触发元素