封装弹窗组件时,可以有两种方式来关闭,一种全局监听click方法,判断当前点击元素是否是弹框元素,另一种通过focus和blur事件
全局监听click方法
- 弹窗组件
HTML
代码
vue
<template>
<div class="z-50 text-[red] select-none">
<div v-if="showMenu" ref="menu" @click="closeMenu">
<slot />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, useTemplateRef } from "vue";
defineOptions({ name: "Pop" });
const showMenu = ref<boolean>(false);
const contextMenuRef = useTemplateRef("menu");
const closeMenu = () => {
showMenu.value = false;
};
const openMenu = () => {
showMenu.value = true;
};
</script>
- 全局click监听
vue
<script setup lang="ts">
import { onMounted, onUnmounted, ref, useTemplateRef } from "vue";
defineOptions({ name: "Pop" });
const showMenu = ref<boolean>(false);
const contextMenuRef = useTemplateRef("menu");
const openMenu = () => {
showMenu.value = true;
};
const closeMenu = () => {
showMenu.value = false;
};
const handleOtherClickClose = (e: MouseEvent) => {
if (contextMenuRef.value) {
const isSelfMenu = contextMenuRef.value.contains(e.target as Element);
if (!isSelfMenu) {
closeMenu();
}
}
};
onMounted(() => {
document.addEventListener("click", handleOtherClickClose);
});
onUnmounted(() => {
document.removeEventListener("click", handleOtherClickClose);
});
</script>
- 使用
监听click事件时,加上 .stop
事件修饰符[1],防止事件冒泡,触发到全局事件
vue
<template>
<Pop ref="pop">
<h1>Hello</h1>
</Pop>
<div @click.stop="handlePopShow">show</div>
</template>
<script setup lang="ts">
import { useTemplateRef } from "@vue/runtime-core";
const popRef = useTemplateRef("pop")
const handlePopShow = () => {
popRef.value.openMenu()
}
</script>
focus和blur方法
正常情况下onfocus和onblur事件只在input等元素中才有,而div却没有,要使div拥有这两个事件,需要加上tabindex属性,但是元素默认会加上焦点虚线, IE浏览器中添加属性 hidefocus="true"
去除,其他浏览器添加样式 outline: none
去除
- 弹窗组件
HTML
代码
vue
<template>
<div class="z-50 text-[red] select-none">
<div
v-if="showMenu"
ref="menu"
tabindex="-1"
hidefocus="true"
@blur="closeMenu"
@click="closeMenu"
>
<slot />
</div>
</div>
</template>
- focus和blur事件处理
关键的是在控制组件显示时,调用focus事件
vue
<script setup lang="ts">
import { nextTick, ref, useTemplateRef } from "vue";
const showMenu = ref<boolean>(false);
const contextMenuRef = useTemplateRef("menu");
const openMenu = () => {
showMenu.value = true;
nextTick(() => {
contextMenuRef.value?.focus();
});
};
const closeMenu = () => {
showMenu.value = false;
};
</script>
References
Event Handling: Event Modifiers. cn.vuejs.org. ↩︎