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

3

可以通过以下技术确定鼠标光标下的元素(即最上面的悬停元素):

  • 监听mousemove事件,目标是:
    • event.target或者
    • document.elementFromPoint(event.clientX, event.clientY)

当滚动页面时且不移动鼠标时,此方法不起作用。此时,鼠标在技术上没有移动;因此,不会触发任何鼠标事件。

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

“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个回答

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树中适当的元素。


1
在这个解决方案中,您已经解决了鼠标不移动时滚动的问题。滚动时,“指针下方”的元素确实会接收到hover类。但是,您是否也希望在mousemove时应用此hover类呢? - Freeman Lambda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接