一个 hook,用于实现 ant-design-vue 中父级滚动 popup 自动收起

76 阅读1分钟

useClosePopupOnScroll

/**
 * 当 popup 指定的最近一个父级元素滚动时,关闭 popup
 */

import { onUnmounted, ref } from 'vue';

// 寻找某个 el 最近的父元素
function findClosestParent(el, selector) {
  if (typeof el.closest === 'function') {
    return el.closest(selector);
  } else {
    // 对于不支持 closest 方法的浏览器, 我们可以使用 while 循环来向上遍历 DOM 树
    while (el) {
      if (el.matches(selector)) {
        return el;
      }
      el = el.parentElement;
    }
  }
  // 如果在 DOM 树中无法找到匹配的元素, 则返回 null
  return null;
}

/**
 * @parentSelector 指定的最近一个父级元素
 * @popupContainer 指定的 popup 容器, 默认是 #app
 */
export function useClosePopupOnScroll(
  parentSelector: string,
  popupContainer = document.querySelector('#app'),
) {
  const popupVisible = ref(false);

  return {
    popupVisible,
    getPopupContainer(trigger) {
      const parentElement = findClosestParent(trigger, parentSelector);

      // 找不到直接返回
      if (!parentElement) return popupContainer;

      function closePopup() {
        popupVisible.value = false;
      }

      parentElement.addEventListener('scroll', closePopup);

      onUnmounted(() => {
        parentElement.removeEventListener('scroll', closePopup);
      });

      return popupContainer;
    },
  };
}

使用

<template>
  <a-popover
    v-model:visible="popupVisible"
    trigger="click"
    placement="topLeft"
    :getPopupContainer="getPopupContainer"
  >
  ....
  </a-popover>
</template>

<script setup lang='ts'>
import { useClosePopupOnScroll } from '@/hooks/web/useClosePopupOnScroll';

const { popupVisible, getPopupContainer } = useClosePopupOnScroll('.ant-table-body');
</script>