Skip to content

Vue3 弹框封装

November 14, 2024
3 min read
491 words

封装弹窗组件时,可以有两种方式来关闭,一种全局监听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

  1. Event Handling: Event Modifiers. cn.vuejs.org. ↩︎