简体   繁体   中英

Hover the element below the mouse cursor when scrolling

One can determine the element below the mouse cursor (ie the top-most hovered element) with the following techniques:

  • Listen for the mousemove event. The target is
    • event.target or
    • document.elementFromPoint(event.clientX, event.clientY) .

This does not work when scrolling while not moving the mouse . Then, the mouse technically doesn't move; thus, no mouse event will fire.

Unfortunately, both techniques from above are no longer applicable when listening for the scroll event. event.target will be whichever element is scrolled (or document ). Also, the mouse cursor position is not exposed on the event object.

As described in this answer to “Determine which element the mouse pointer is on top of in Javascript” , one possible solution is querying the hovered element via the CSS :hover pseudo-class.

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

However, this is not usable because it is very inefficient and inaccurate. The scroll event is one of the rapidly firing events and needs to be slowed down when performing anything mildly costly (eg querying the DOM).

Also, the hovered element lags behind when scrolling. You can observe this on any kind of website with a lot of links: Hover over one of them and scroll to another link without moving the mouse. It updates only after a few milliseconds.

Is there any way, this can be implemented nicely and efficient? Basically, I want the inverse of mouseenter : Instead of knowing when the mouse enters and element, I want to know when an element intersects with the mouse (eg when the mouse is not moved but the element [ie when scrolling]).

One approach of tackling this is storing the mouse cursor location with the mousemove event and in the scroll event use document.elementFromPoint(x, y) to figure out the element that should be hovered.

Keep in mind that this is still pretty inefficient due to the scroll event being fired with such a high frequency. The event handler should be debounced to limit execution of the function to once per delay. David Walsh explains how to do this in 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> 

Currently, the solution will hover the top-most element under the mouse both when moving the mouse and when scrolling. It might be more suitable for your needs to attach the mousemove listener to a set of specific elements and then always hover event.currentTarget (ie the element the event listener was attached to). As for the scroll part, you can use hoverTarget.closest to find the suitable element up in the DOM tree.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM