Appearance
clickOut 点击外部指令
当用户点击当前元素之外的区域时触发回调,常用于关闭弹层、下拉菜单等交互。
安装与引入
按需导入(推荐)
ts
import { VClickOutside } from '@cuixingjian/cui-utils'全局注册
ts
import { createApp } from 'vue'
import { VClickOutside } from '@cuixingjian/cui-utils'
const app = createApp(App)
app.directive('click-outside', VClickOutside)
app.mount('#app')局部注册
vue
<script setup lang="ts">
import { VClickOutside as vClickOutside } from '@cuixingjian/cui-utils'
</script>
<template>
<div v-click-outside="handler">内容</div>
</template>效果演示
基础用法
点击浮层外部区域自动关闭。
使用代码
直接传入处理函数:
vue
<script setup lang="ts">
const handleOutside = (e: Event) => {
// 关闭下拉、弹层等
}
</script>
<template>
<div v-click-outside="handleOutside">下拉菜单内容</div>
</template>传入选项对象(排除元素):
vue
<script setup lang="ts">
import { ref } from 'vue'
const triggerRef = ref<HTMLElement>()
const onOutside = (e: Event) => {
// 收起气泡等处理
}
</script>
<template>
<button ref="triggerRef">触发器</button>
<div
v-click-outside="{
onOutside,
exclude: [triggerRef, '.safe-area']
}"
>
气泡内容
<div class="safe-area">点击这里不触发</div>
</div>
</template>API 说明
指令值类型
| 类型 | 说明 | 示例 |
|---|---|---|
Function | 直接传入处理函数 | v-click-outside="handler" |
Object | 传入配置对象 | v-click-outside="{ onOutside: handler, exclude: [...] }" |
配置选项 (ClickOutsideOptions)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| onOutside | (event: Event) => void | — | 点击外部时触发的回调函数 |
| exclude | Array<Element | string> | [] | 排除区域(元素引用或选择器字符串) |
| events | string[] | ['click', 'touchstart', 'pointerdown'] | 监听的事件类型列表 |
| capture | boolean | true | 是否在捕获阶段监听 |
| enabled | boolean | true | 是否启用指令 |
回调参数
| 参数 | 类型 | 说明 |
|---|---|---|
| event | Event | 原生事件对象 |
注意事项
基本规则
- 元素内部点击不会触发回调(通过
el.contains(event.target)判断) - 默认使用捕获阶段监听,更稳定地检测外部点击
- 指令不涉及样式,需要自行添加
排除区域使用
为什么需要排除区域?
- 防止误关闭:Teleport 到 body 的弹层内容不在目标元素内,但属于同一交互
- 保障操作连续性:用户在弹层内进行复杂操作时,点击关联控件不应关闭弹层
- 保留触发器行为:触发器与内容分离时,点击触发器不应判定为外部点击
典型场景:
- 下拉菜单与触发按钮分离
- Popover 内嵌日期选择器等组件
- 多级菜单/嵌套弹层
- 富文本编辑器悬浮工具栏
- 新手引导遮罩安全区
源码
展开查看
ts
import type { Directive } from 'vue'
import { withInstallDirective } from '../install'
export interface ClickOutsideOptions {
onOutside?: (event: Event) => void
exclude?: Array<Element | string>
events?: string[]
capture?: boolean
enabled?: boolean
}
type BindingValue = ((event: Event) => void) | ClickOutsideOptions
const CLICK_OUTSIDE_SYMBOL = Symbol('cui-v-click-outside')
interface ClickOutsideState {
options: Required<
Pick<ClickOutsideOptions, 'events' | 'capture' | 'enabled'>
> &
ClickOutsideOptions
handler: (event: Event) => void
listener: (event: Event) => void
}
function resolveOptions(value: BindingValue): ClickOutsideOptions {
/* ...略 */
}
function isExcluded(
target: Node | null,
exclude: Array<Element | string> | undefined
): boolean {
/* ...略 */
}
const clickOutside: Directive<HTMLElement, BindingValue> = {
/* ...略 */
}
export const VClickOutside = withInstallDirective(clickOutside, 'click-outside')
export default VClickOutside