summaryrefslogtreecommitdiff
path: root/assets/js/tooltip.js
blob: eea5b350e134b69930616c2be3c26e57b956d775 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
(() => {
  const allTooltipped = document.querySelectorAll('[data-tooltip]');

  const createOrUpdateTooltip = (element) => {
    const tooltipText = element.getAttribute('data-tooltip') || element.getAttribute('title');

    if (!tooltipText) return;

    let tooltipElement = element.querySelector('.__generated-tooltip');
    if (!tooltipElement) {
      element.classList.add('group', 'relative', 'tooltipped');

      tooltipElement = document.createElement('div');
      tooltipElement.classList.add(
        '__generated-tooltip',
        ...'absolute bottom-full left-1/2 transform -translate-x-1/2 translate-y-1 bg-black/80 text-white px-2.5 py-1.5 rounded text-xs whitespace-nowrap opacity-0 invisible transition-all duration-300 pointer-events-none mb-2 z-1000 group-hover:opacity-100 group-hover:visible group-hover:translate-y-0 dark:bg-white/90 dark:text-black'.split(' ')
      );
      element.appendChild(tooltipElement);
    }

    tooltipElement.textContent = tooltipText;
  };

  const observer = new IntersectionObserver((entries, obs) => {
    entries.forEach(entry => {
      if (!entry.isIntersecting) return;

      const element = entry.target;
      createOrUpdateTooltip(element);

      // listen to tooltip text changes
      const mutationObserver = new MutationObserver(mutations => {
        for (const mutation of mutations) {
          if (mutation.type === 'attributes' &&
              (mutation.attributeName === 'title' || mutation.attributeName === 'data-tooltip')) {
            createOrUpdateTooltip(element);
          }
        }
      });

      mutationObserver.observe(element, {
        attributes: true,
        attributeFilter: ['title', 'data-tooltip'],
      });

      obs.unobserve(element); // 不再需要 Intersection 监听
    });
  }, {
    rootMargin: '100px',
    threshold: 0.1,
  });

  allTooltipped.forEach(el => observer.observe(el));
})();