Skip to content
On this page

17. Node.contains()

定义

MDN link: MDN Node.contains

Node.contains(otherNode) 返回一个布尔值,判断 otherNode 是否为 Node 的后代节点。下述三种情况,返回的为 true

  • otherNodeNode 本身
  • otherNodeNode 的直接子节点
  • otherNodeNode 的子节点的所有后续子节点

使用场景

  1. 知道这个方法起源于一个需求:点击下拉菜单以外的地方,则收起该下拉菜单。需要使用 Node.contains() 方法判断是否点击了下拉菜单以外的地方。
  2. 双击表格中的单元格,则可以编辑单元格内容,点击外边(类似于 unblur 事件)则保存编辑的内容,变为普通表格。也需要这个方法来判断是否点击了该单元格外面。

判断是否点击了某一元素外面,使用 Vue3 + TS 封装的 useClickOutside.ts hook 如下:(这里只是一个例子,思想是通用的,参考代码逻辑可以写出其他版本的,例如 React 或者原生 JS。)

ts
import { ref, onMounted, onUnmounted, Ref } from 'vue'

// 传入一个 Node/HTMLElement 不行,在 setup 中无法监控到,也就失去了响应式。
// 所以传入的参数应该是一个 Ref 的类型
export default function useClickOutside(
  elmentRef: Ref<null | HTMLElement>,
): Ref {
  const isClickOutside = ref(false)
  // 点击回调事件
  const handler = (e: MouseEvent) => {
    /* 核心原理
     * e.target 返回的是当前响应事件的元素
     * 1、dropdown 节点包含 e.target,说明是在下拉菜单里面点的
     * 2、dropdown 节点不含 e.target ,说明在外面点击的,就关闭下拉菜单
     */
    if (elmentRef.value) {
      // 类型不吻合,这里需要 as 断言
      if (!elmentRef.value.contains(e.target as HTMLElement)) {
        isClickOutside.value = true
      } else {
        isClickOutside.value = false
      }
    }
  }
  onMounted(() => {
    document.addEventListener('click', handler)
  })
  onUnmounted(() => {
    document.removeEventListener('click', handler)
  })
  // 最后返回这个 ref<boolean>
  return isClickOutside
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33