组件弹出层隐藏问题(点击弹出层外可隐藏弹出层)

724 阅读2分钟

引言

本文主要使用element Plus 以及 TDesign来说明弹出层的三种打开方式,并且给出各个方式的一些不足点,以及一种较完美的解决方案。

方法一 使用组件自带的触发方式,悬浮 和 点击

element plus 弹出层的悬浮展示弹出层,这种这做法的弊端是想要在弹出层做完操作时不能立马隐藏弹窗。可以看到我选择了11月7日,我想要的效果是我点击之后,这个弹出层能够隐藏。触发方式为点击时也有相同的问题。

image.png

相应的代码部分

<template>
  <div>
    <el-popover style="width: 360px">
      <div class="date-picker-panel-border">
        <t-date-picker-panel v-model="date" :on-change="handleChange" />
      </div>
      <template #reference>
        <div style="width: 32px; margin: 40px auto">
          <CalendarIcon size="32" />
        </div>
      </template>
    </el-popover>
  </div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import dayjs from "dayjs";
//
import { CalendarIcon } from "tdesign-icons-vue-next";
const date = dayjs().format("YYYY-MM-DD");
const handleChange = () => {};
</script>
:global(.el-popper) {
  width: 300px !important;
}

方法二 使用组件的visible属性,通过这个绑定的true或者false的值来进行控制显隐。

这种做法相较于上述做法无法进行点击弹窗之外进行隐藏弹出层。但可以在改变日期之后隐藏弹出层,具体效果如下:

20241121231154_rec_.gif

相应的代码部分

<template>
  <div>
     <el-popover :visible="visible">
      <div class="date-picker-panel-border">
        <t-date-picker-panel v-model="date" :on-change="handleChange" />
      </div>
      <template #reference>
        <div style="width: 32px; margin: 40px auto" @click="visible = true">
          <CalendarIcon size="32" />
        </div>
      </template>
    </el-popover>
  </div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import dayjs from "dayjs";
//
import { CalendarIcon } from "tdesign-icons-vue-next";
const visible = ref(false);
const date = dayjs().format("YYYY-MM-DD");
const handleChange = (value) => {
  console.log(value);
  visible.value = false;
};
</script>
:global(.el-popper) {
  width: 300px !important;
}

较完美的解决方法

  1. 在onMounted 方法当中注册一个点击事件,在上方js当中加入以下代码,如下所示:
onMounted(() => {
  document.addEventListener("click", handleClose);
});
const handleClose = () => {
  visible.value = false;
};
  1. 这样可以实现在点击弹出层其他位置时隐藏弹出层。但是因为事件冒泡原理,我们在点击打开弹出层时会出现先打开弹出,然后冒泡到handleClose中弹出层又立马关闭。效果图如下:

20241121232247_rec_.gif 3. 添加阻止冒泡,两个地方,第一打开弹窗的点击方法。

  <template #reference>
        <div style="width: 32px; margin: 40px auto" @click.stop="visible = true">
          <CalendarIcon size="32" />
        </div>
  </template>

第二个地方,更改日期,注意由于更改日期是change方法,他没有.stop修饰词所以我们要另辟蹊径。在日期选择器的外层div中添加@click.stop,这样change方法往上冒泡就到了外层div,而外层div又禁止冒泡所以就被阻断了。

  <div class="date-picker-panel-border" @click.stop>
    <t-date-picker-panel v-model="date" :on-change="handleChange" />
  </div>

最后大家看看效果:

20241121232956_rec_.gif

结论

以后遇到点击弹窗之外能够关闭弹窗都可以参考这种方法。关键就是这两步,然后注意事件冒泡就好了

onMounted(() => {
  document.addEventListener("click", handleClose);
});
const handleClose = () => {
  visible.value = false;
};

最后附上源码

<template>
  <div>
    <el-popover :visible="visible">
      <div class="date-picker-panel-border" @click.stop>
        <t-date-picker-panel v-model="date" :on-change="handleChange" />
      </div>
      <template #reference>
        <div
          style="width: 32px; margin: 40px auto"
          @click.stop="visible = true"
        >
          <CalendarIcon size="32" />
        </div>
      </template>
    </el-popover>
  </div>
</template>


<script lang="ts" setup>
import { onMounted, ref } from "vue";
import dayjs from "dayjs";
//
import { CalendarIcon } from "tdesign-icons-vue-next";
const visible = ref(false);
const date = dayjs().format("YYYY-MM-DD");
const handleChange = (value) => {
  console.log(value);
  visible.value = false;
};
onMounted(() => {
  document.addEventListener("click", handleClose);
});
const handleClose = () => {
  visible.value = false;
};
</script>
:global(.el-popper) {
  width: 300px !important;
}