滚动时将元素悬停在鼠标光标下方

时间:2018-06-05 12:43:18

标签: javascript events

可以使用以下技术确定鼠标光标下方的元素(即最顶端的元素):

  • 收听mousemove事件。目标是
    • event.target
    • document.elementFromPoint(event.clientX, event.clientY)

在不移动鼠标的情况下滚动时,这不起作用。然后,鼠标在技术上不动;因此,不会发生任何鼠标事件。

不幸的是,在收听scroll事件时,上述两种技术都不再适用。 event.target将是滚动的任何元素(或document)。此外,鼠标光标位置未在event对象上公开。

this answer to “Determine which element the mouse pointer is on top of in Javascript”中所述,一种可能的解决方案是通过CSS :hover伪类查询悬停元素。

document.addEventListener('scroll', () => {
  const hoverTarget = document.querySelector('.element:hover');
  if (hoverTarget) {
    hover(hoverTarget);
  }
});

然而,这是不可用的,因为它非常低效且不准确。 scroll事件是快速触发的事件之一,在执行任何代价高昂的事情时(例如查询DOM)需要放慢速度。

此外,滚动时,悬停元素落后于。您可以在任何具有大量链接的网站上观察到这一点:将鼠标悬停在其中一个上并滚动到另一个链接而不移动鼠标。它只在几毫秒后更新。

有什么办法,这可以很好地实现吗?基本上,我想要mouseenter的倒数:我不想知道鼠标何时进入和元素,我想知道一个元素何时与鼠标相交(例如当鼠标没有移动但是元素[即滚动时] )。

1 个答案:

答案 0 :(得分:2)

解决此问题的一种方法是将鼠标光标位置与mousemove事件一起存储,并在scroll事件中使用document.elementFromPoint(x, y)来确定应该悬停的元素。

请记住,由于scroll事件以如此高的频率被触发,因此效率仍然非常低。事件处理程序应该去抖以将函数的执行限制为每个延迟一次。 David Walsh在JavaScript Debounce Function中解释了如何执行此操作。

let hoveredElement;
let mouseX = 0, mouseY = 0;

document.addEventListener('DOMContentLoaded', () => {
  document.addEventListener('mousemove', event => {
    mouseX = event.clientX;
    mouseY = event.clientY;

    hover(event.target);
  });

  document.addEventListener('scroll', () => {
    const hoverTarget = document.elementFromPoint(mouseX, mouseY);
    if (hoverTarget) {
      hover(hoverTarget);
    }
  });
});

function hover(targetElement) {
  // If the target and stored element are the same, return early
  // because setting it again is unnecessary.
  if (hoveredElement === targetElement) {
    return;
  }

  // On first run, `hoveredElement` is undefined.
  if (hoveredElement) {
    hoveredElement.classList.remove('hover');
  }

  hoveredElement = targetElement;
  hoveredElement.classList.add('hover');
}
.element {
  height: 200px;
  border: 2px solid tomato;
}

.element.hover {
  background-color: lavender;
}
<div class="container">
  <div class="element element-1">1</div>
  <div class="element element-2">2</div>
  <div class="element element-3">3</div>
  <div class="element element-4">4</div>
  <div class="element element-5">5</div>
</div>

目前,解决方案将在移动鼠标和滚动时将鼠标悬停在鼠标下方。您可能更适合将mousemove侦听器附加到一组特定元素,然后始终悬停event.currentTarget(即事件侦听器附加到的元素)。对于scroll部分,您可以使用hoverTarget.closest在DOM树中查找合适的元素。