JavaScript基础回顾(三):DOM 事件

52 阅读4分钟

事件基础

  • 事件是用户或浏览器触发的动作,如点击按钮、移动鼠标、按下键盘等。
  • 事件是交互式网页的核心,允许网页响应用户的操作。

事件类型

我们常见的事件类型包括:

  • MouseEvent:与鼠标相关的事件,如 click、mouseover 等。它提供了鼠标事件特有的属性,如 clientX、clientY、screenX、screenY 等。
  • KeyboardEvent:与键盘相关的事件,如 keydown、keyup。它提供了键盘事件特有的属性,如 key、shiftKey、ctrlKey 等。
  • UIEvent:与用户界面相关的事件:如 load、unload、resize、scroll 等。它提供了一些通用的属性,如 view(指向与事件相关的试图(window 对象))。
  • DragEvent:与拖放操作相关的事件,如 dragstart、dragend 等。它提供了拖放事件特有的特性,如 dataTransfer 等。
  • FocusEvent:与焦点相关的事件,如 focus、blur 等。它提供了焦点事件特有的属性,如 relatedTarget(指向失去焦点的元素)。
  • ProgressEvent:与进度相关的事件,如 loadstart、progress、abort 等。它提供了如 lengthComputable、loaded、total 等特有属性。
  • WheelEvent:与鼠标滚轮相关的事件,如 wheel。它提供了如 deltaX、deltaY 等特有属性。

另外,我们还可以通过 CustomEvent 自定义事件。所有的事件都继承自 Event。

事件处理

  • 我们可以 HTML 元素的事件属性(如 onclick 等)绑定事件,也可以通过 addEventListener 方法添加时间监听。
  • addEventListener:element.addEventListener(event, handleFunction, useCapture)
  • 在使用 addEventListener 时,需要主动 removeEventListener,避免内存泄漏和重复触发。
  • 事件传播包括:
    • 捕获阶段:事件从根节点向下传播到目标节点的过程中。
    • 目标阶段:事件到达目标节点时。
    • 冒泡阶段:事件从根节点向上传播到根节点的过程。

在前端框架中不推荐使用 addEventListener

  1. 生命周期管理:React 和 Vue 都会在组件卸载时清理它们添加的事件监听器。如果你使用了 addEventListener,需要确保在组件卸载时手动移除这些监听器,否则可能导致内存泄漏。
  2. 性能优化:框架的事件处理通常经过了优化,以适应其自身的更新和渲染机制。
  3. 代码简洁性:使用框架提供的时间处理方式可以是代码更简洁,更符合框架的使用习惯。

处理 resize 事件

React

import { useState, useEffect } from 'react';
import { debounce } from 'lodash';

const ListenResize = () => {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  })

  const handelResize = debounce(() => {
    setWindowSize({
      width: window.innerWidth,
      height: window.innerHeight,
    })
  }, 300)

  useEffect(() => {
    window.addEventListener('resize', handelResize);

    return () => {
      window.removeEventListener('resize', handelResize);
    }
  }, [])

  return (
    <div>
      <div>宽:{windowSize.width}</div>
      <div>高:{windowSize.height}</div>
    </div>
  )
}

export default ListenResize

Vue

<template>
  <div>
    <div>宽:{{ windowSize.width }}</div>
    <div>高:{{ windowSize.height }}</div>
  </div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { debounce } from 'lodash'

const windowSize = ref({
  width: window.innerWidth,
  height: window.innerHeight
})

const handleResize = debounce(() => {
  windowSize.value = {
    width: window.innerWidth,
    height: window.innerHeight
  }
}, 300)

onMounted(() => {
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
})
</script>

事件对象

事件对象的属性:

  • event.type:事件的类型,如 click、load 等。
  • event.target:触发事件的元素。
  • event.currentTarget:事件处理函数绑定的元素。

事件对象的方法:

  • preventDefault():取消事件的默认行为,如 a 标签的链接跳转、submit button 的表单提交等。如果事件是可以取消的(cancelable 是 true),则此方法可以调用。
  • stopPropagation():阻止单一事件继续冒泡或捕获。
  • stopImmediatePropagation():阻止同类事件继续冒泡或捕获。

创建与触发事件:

  • 创建事件:使用 new Event(type, options) 构造函数(或其他事件构造函数)可以创建一个事件对象。type 是事件的类型,options 是一个配置对象,可以设置 bubbles 和 cancelable 等属性。
  • 触发事件:使用元素的 dispatchEvent() 方法可以触发事件。

事件委托

事件委托是一种技术,它利用了事件冒泡的原理,将一个事件监听器添加到父元素上,而不是子元素上。当子元素触发事件时,这个时间会冒泡到父元素,然后触发父元素上的事件监听器。

优点

  • 减少内存消耗:因为只需要一个事件监听器,而不是为每个子元素都添加一个。
  • 动态元素处理:对于动态添加到 DOM 中的元素,不需要单独绑定事件监听器。
  • 简化代码:简化了时间处理的代码,使得维护更加容易。

使用场景

  • 列表项交互:如表格、列表等,只需要在 table 或 ul 等父元素绑定一个事件监听器。
  • 动态内容:动态添加子元素时。
  • 表单元素:表单内的元素可以使用表单统一处理。

实现

在父元素添加事件监听器,通过 event.target 判断具体的子元素。