JavaScript之事件(Event) | 青训营笔记

22 阅读9分钟

9. 事件Event

9.1 处理事件(事件监听)的三种方案

  1. 方式一:直接在html中编写JavaScript代码

例如通过onclick属性在行内写一些简单的操作。

  1. 方式二:DOM属性,通过元素的on***属性来监听事件

    btn.onclick = function() {
    	console.log("您点击了按钮")
    }
    
  2. 方式三:通过EventTarget中的**addEventListener()**方法来注册一个事件监听器

  • 语法:

    addEventListener(type, listener);
    addEventListener(type, listener[, options]);
    addEventListener(type, listener[, useCapture]);
    
    • 参数(type):表示监听事件类型的大小写敏感的字符串
    • 参数(listener):当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数
    • 参数(options):可选。一个指定有关 listener 属性的可选参数对象。可用的选项如下
      • capture:可选。一个布尔值,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
      • once:可选。一个布尔值,表示 listener 在添加之后最多只调用一次。如果为 truelistener 会在其被调用之后自动移除。
      • passive:可选。一个布尔值,设置为 true 时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
      • signal:可选。AbortSignal,该 AbortSignalabort() 方法被调用时,监听器会被移除。
    • 参数(useCapture):可选。一个布尔值,表示在 DOM 树中注册了 listener 的元素,是否要先于它下面的 EventTarget 调用该 listener
  • 描述:将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

推荐使用 addEventListener() 来注册一个事件监听器,理由如下:

  • 它允许为一个事件添加多个监听器。特别是对库、JavaScript 模块和其他需要兼容第三方库/插件的代码来说,这一功能很有用。
  • 相比于 onXYZ 属性绑定来说,它提供了一种更精细的手段来控制 listener 的触发阶段。(即可以选择捕获或者冒泡)。
  • 它对任何事件都有效,而不仅仅是 HTML 或 SVG 元素。

9.2 事件冒泡和事件捕获

9.2.1 事件流

当我们在浏览器上点击一个元素时,实际点击的并不仅仅是这个元素本身,因为HTML元素是存在父子元素叠加层级的。例如,一个span元素放在元素上,div元素是放在body元素上,body元素是放在html元素上...

9.2.2 对事件冒泡和捕捉的解释

当一个事件发生在具有父元素的元素上时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。

  1. 捕获阶段【由外往内】
  • 浏览器检查元素的最外层祖先<html>,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它。
  • 然后,它移动到<html>中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。
  1. 冒泡阶段【由内往外】
  • 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个onclick事件处理程序,如果是,则运行它
  • 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达<html>元素。

在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册

如果想在捕获阶段注册一个事件,可以通过使用addEventListener()注册处理程序,并将可选的第三个属性设置为 true。

9.3 事件对象(event)

当一个事件发生时,就会有和这个事件相关的很多信息,例如:事件的类型是什么、点击的是哪个元素、点击的位置......这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象。event对象中有许多属性和方法,可以通过该对象进行某些操作。

event对象会在传入的事件处理函数回调时被系统传入,可以在回调函数中拿到这个event对象。

btnEl.onclick = function(event) {
    console.log("按钮发生了点击")
    console.log(event)
}

9.3.1 属性

  • target【常用】

    • 语法:let theTarget = event.target
    • 描述:触发事件的对象 (某个 DOM 元素) 的引用。当事件处理程序在事件的冒泡或捕获阶段被调用时,它与event.currentTarget不同。【当前事件发生的元素】
  • currentTarget【常用】

    • 语法:var currentEventTarget = event.currentTarget;

    • 描述:表示当事件沿着 DOM 触发时事件的当前目标。它总是指向事件绑定的元素,而 Event.target 则是事件触发的元素。【当前处理事件的元素】

    event.currentTarget 的值只能在事件处理过程中被使用。如果你尝试用 console.log() 在控制台打印 event 对象,你会发现 currentTarget 的值是 null。如果你想在控制台打印 currentTarget 的值,你应该使用 console.log(event.currentTarget),或者也可以在代码中使用 debugger 语句来暂停代码的执行从而看到 event.currentTarget 的值。

targetcurrentTarget的区别:

总结:target是你点击哪个就是哪个;currentTartget就是你点击的那个的祖先DOM

结合下面的代码结果借助上面的描述理解。

<div class="box">
     <button class="btn">按钮</button>
</div>

var divEl = document.querySelector(".box")
divEl.onclick = function(event) {
 console.log(event.target)
 console.log(event.currentTarget)
 console.log(event.target === event.currentTarget)
}
  • type
    • 描述:返回一个字符串,表示该事件对象的事件类型。
  • eventPhase
    • 描述:表示事件流当前处于哪一个阶段。
  • offsetX(MouseEvent.offserX)
    • 描述:鼠标指针相对于目标节点内边位置的 X 坐标
  • offsetY(MouseEvent.offserY)
    • 描述:鼠标指针相对于目标节点内边位置的 Y 坐标
  • clientX(MouseEvent.clientX)
    • 描述:鼠标指针在点击元素(DOM)中的 X 坐标。
  • clientY(MouseEvent.clientY)
    • 描述:鼠标指针在点击元素(DOM)中的 Y 坐标。
  • pageX(MouseEvent.pageX)
    • 描述:鼠标指针相对于整个文档的 X 坐标;
  • pageY(MouseEvent.pageY)
    • 描述:鼠标指针相对于整个文档的 Y 坐标;
  • screenX(MouseEvent.screenX)
    • 描述:鼠标指针相对于全局(屏幕)的 X 坐标;
  • screenY(MouseEvent.screenY)
    • 描述:鼠标指针相对于全局(屏幕)的 Y 坐标;

9.3.2 方法

  • preventDefault()
    • 语法:event.preventDefault();
    • 描述:取消事件的默认行为
    • 应用(详见MDN官网)
      • 阻止默认的点击事件执行
      • 在编辑域中阻止按键
  • stopPropagation()
    • 语法:event.stopPropagation();
    • 描述:阻止捕获和冒泡阶段中当前事件的进一步传播。但是,它不能防止任何默认行为的发生;例如,对链接的点击仍会被处理。

9.4 事件处理函数中的this

此时this与event.currentTarget指向的元素相同

<div class="box">
    <button class="btn2">按钮2</button>
</div>

var divEl = document.querySelector(".box")

// 当点击按钮2时
divEl.onclick = function(event) {
    console.log(this)       // <div class="box">...</div>
    console.log(event.target)   // <button class="btn2">按钮2</button>
    console.log(event.currentTarget)    // <div class="box">...</div>
    console.log(this === event.target)  // false
    console.log(this === event.currentTarget)   // true
}
// 当点击div时
divEl.onclick = function(event) {
    console.log(this)       // <div class="box">...</div>
    console.log(event.target)   // <div class="box">...</div>
    console.log(event.currentTarget)    // <div class="box">...</div>
    console.log(this === event.target)  // true
    console.log(this === event.currentTarget)   // true
}

9.5 EventTarget接口

EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件。

Node、Window、Element等都继承自EventTarget。

9.5.1 方法

  • addEventListener()

  • 语法:

    addEventListener(type, listener);
    addEventListener(type, listener[, options]);
    addEventListener(type, listener[, useCapture]);
    
    • 参数(type):表示监听事件类型的大小写敏感的字符串
    • 参数(listener):当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数
    • 参数(options):可选。一个指定有关 listener 属性的可选参数对象。可用的选项如下
      • capture:可选。一个布尔值,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
      • once:可选。一个布尔值,表示 listener 在添加之后最多只调用一次。如果为 truelistener 会在其被调用之后自动移除。
      • passive:可选。一个布尔值,设置为 true 时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
      • signal:可选。AbortSignal,该 AbortSignalabort() 方法被调用时,监听器会被移除。
    • 参数(useCapture):可选。一个布尔值,表示在 DOM 树中注册了 listener 的元素,是否要先于它下面的 EventTarget 调用该 listener
  • 描述:将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

    推荐使用 addEventListener() 来注册一个事件监听器,理由如下:

    • 它允许为一个事件添加多个监听器。特别是对库、JavaScript 模块和其他需要兼容第三方库/插件的代码来说,这一功能很有用。
    • 相比于 onXYZ 属性绑定来说,它提供了一种更精细的手段来控制 listener 的触发阶段。(即可以选择捕获或者冒泡)。
    • 它对任何事件都有效,而不仅仅是 HTML 或 SVG 元素。
  • removeEventListener()

    • 语法:

      removeEventListener(type, listener);
      removeEventListener(type, listener[, options]);
      removeEventListener(type, listener[, useCapture]);
      
    • 参数(type):一个字符串,表示需要移除的事件类型。

    • 参数(listener):需要从目标事件移除的事件监听器函数。

    • 参数(options):可选。一个指定事件侦听器特征的可选对象。可选项有:

      • capture:一个布尔值,指定需要移除的事件监听器函数是否为捕获监听器。如果未指定此参数,默认值为 false
    • 参数(useCapture):可选。一个布尔值,指定需要移除的事件监听器函数是否为捕获监听器。如果未指定此参数,默认值为 false

    • 描述:可以删除使用 EventTarget.addEventListener() 方法添加的事件。可以使用事件类型,事件侦听器函数本身,以及可能影响匹配过程的各种可选择的选项的组合来标识要删除的事件侦听器。

    如果一个 EventTarget 上的事件监听器在另一监听器处理该事件时被移除,那么它将不能被事件触发。不过,它可以被重新绑定。

    还有一种移除事件监听器的方法:可以向 addEventListener() 传入一个 AbortSignal,稍后再调用拥有该事件的控制器上的 abort() 方法即可。

    <button>按钮</button>
    
    var btnEl = document.querySelector("button")
    
    var foo = function() {
        console.log("监听到按钮的点击")
    }
    
    btnEl.addEventListener("click", foo)
    
    // 需求:过5秒钟后将这个事件监听移除
    setTimeout(function() {
        btnEl.removeEventListener("click", foo)
    }, 5000)
    
  • dispatchEvent()【了解】

    • 语法:dispatchEvent(event)

      • 参数(event):被派发的 Event。其 Event.target 属性为当前的EventTarget
    • 描述:向一个指定的事件目标派发一个 Event,并以合适的顺序(同步地)调用所有受影响的 EventListener

    在调用此方法时,Event.target 属性默认为当前的 EventTarget

9.6 事件委托(event delegation)