Skip to content

Tooltip 组件特性

  • 最基础的当鼠标悬浮在目标元素上时,弹出 tooltip 提示信息
  • 可以指定 tooltip 弹出位置:placement
  • 支持 disable
  • 支持手动控制 manual,当开启时,hover 等触发事件失效
  • 支持动画
  • 支持虚拟触发

实现的基本思路

  • popperjs 负责 tooltip 元素的位置计算;
  • 通过在 tooltip 元素上绑定 mouseentermouseenter等事件来控制 tooltip 元素的显示隐藏

学习到的一些写法:

  • 针对不同的触发方式,绑定事件:
vue
<template>
  <div  v-on="events" />
</template>

<script setup>
  const events: Ref<Record<string, EventListener>> = ref({});
  const outerEvents: Ref<Record<string, EventListener>> = ref({});
  const dropdownEvents: Ref<Record<string, EventListener>> = ref({});

  function attachEvents() {
    if (props.disabled || props.manual) return;
    if (props.trigger === "hover") {
      events.value["mouseenter"] = openFinal;
      outerEvents.value["mouseleave"] = closeFinal;
      dropdownEvents.value["mouseenter"] = openFinal;
      return;
    }
    if (props.trigger === "click") {
      events.value["click"] = togglePopper;
      return;
    }
    if (props.trigger === "contextmenu") {
      events.value["contextmenu"] = (e) => {
        e.preventDefault();
        openFinal();
      };
      return;
    }
  }
</script>
  • 对于打开和关闭 tooltip 的函数,添加防抖功能
typescript
let openDebounce: DebouncedFunc<() => void> | void;
let closeDebounce: DebouncedFunc<() => void> | void;

function openFinal() {
  // 在打开 Tooltip 时,先取消可能正在进行的关闭操作。
  // 这里这样写是因为打开和关闭方法都是 debounce 的,所以需要先取消再执行
  closeDebounce?.cancel();
  openDebounce?.();
}

function closeFinal() {
  // 在关闭 Tooltip 时,先取消可能正在进行的打开操作
  // 这里这样写是因为打开和关闭方法都是 debounce 的,所以需要先取消再执行
  openDebounce?.cancel();
  closeDebounce?.();
}

popperjs

tooltip 的实现用到了 popverjs 这个库,这个库是用于辅助构建提示类工具组件库的,它的基本用法如下:

javascript
const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');

// Pass the button, the tooltip, and some options, and Popper will do the
// magic positioning for you:
Popper.createPopper(button, tooltip, {
  placement: 'right',
});

css

实现箭头效果,用了一个正方形,旋转了 45 度方式实现;

css
#arrow,
#arrow::before {
  position: absolute;
  width: 8px;
  height: 8px;
  box-sizing: border-box;
  background: var(--er-popover-bg-color);
}
#arrow::before {
  visibility: visible;
  content: '';
  transform: rotate(45deg); // 旋转45度
}