手写VueUse的onClickOutside函数

以下是手写 VueUse 的 onClickOutside 函数的实现,采用 Vue 3 组合式 API 风格,包含详细注释和代码:

typescript 复制代码
import { Ref, onMounted, onUnmounted } from 'vue';

/**
 * 检测点击目标元素外部的操作
 * @param targetRef 要监听的目标元素引用
 * @param callback 点击外部时触发的回调函数
 */
export function onClickOutside(
  targetRef: Ref<HTMLElement | null>,
  callback: (event: MouseEvent) => void
) {
  // 事件处理函数
  const listener = (event: MouseEvent) => {
    const target = targetRef.value;
    
    // 确保目标元素存在且事件目标存在
    if (!target || !event.target) return;

    // 判断点击是否发生在目标元素外部
    if (!target.contains(event.target as Node)) {
      callback(event);
    }
  };

  // 组件挂载时添加监听
  onMounted(() => {
    // 使用捕获阶段确保在阻止冒泡的情况下仍能触发
    document.addEventListener('click', listener, { capture: true });
  });

  // 组件卸载时移除监听
  onUnmounted(() => {
    document.removeEventListener('click', listener, { capture: true });
  });
}

使用示例

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue';
import { onClickOutside } from './onClickOutside';

const target = ref<HTMLElement | null>(null);
const isOpen = ref(true);

onClickOutside(target, () => {
  isOpen.value = false;
});
</script>

<template>
  <div v-if="isOpen" ref="target" class="modal">
    点击外部区域关闭我
  </div>
</template>

实现要点解析

  1. 事件阶段处理

    • 使用 { capture: true } 在捕获阶段监听事件,确保即使其他事件调用了 stopPropagation() 也能触发
  2. 类型安全

    • 使用 TypeScript 类型标注,明确处理 HTMLElement 和事件对象类型
  3. 内存管理

    • 通过 onUnmounted 自动清理事件监听,避免内存泄漏
  4. 边界情况处理

    • 检查目标元素是否存在(targetRef.value
    • 检查事件目标是否存在(event.target
  5. DOM 判断逻辑

    • 使用 contains() 方法判断点击目标是否在监听元素内部

扩展功能建议

如需实现更复杂的功能,可以考虑添加以下特性:

  1. 排除元素:允许配置不触发回调的排除元素
  2. 事件类型 :支持监听其他事件类型(如 mousedown
  3. 响应式配置:通过响应式参数控制监听行为
  4. 立即触发:添加 immediate 选项支持初始状态检测
  5. 条件判断:添加执行回调的条件判断函数
相关推荐
LuckyLay16 分钟前
Vue百日学习计划Day9-15天详细计划-Gemini版
前端·vue.js·学习
水银嘻嘻6 小时前
12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建
运维·前端·自动化
小嘟嚷ovo7 小时前
h5,原生html,echarts关系网实现
前端·html·echarts
十一吖i7 小时前
Vue3项目使用ElDrawer后select方法不生效
前端
只可远观7 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰7 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
敲代码的小吉米7 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊8 小时前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js
九月TTS8 小时前
TTS-Web-Vue系列:组件逻辑分离与模块化重构
前端·vue.js·重构
我是大头鸟9 小时前
SpringMVC 内容协商处理
前端