One can determine the element below the mouse cursor (ie the top-most hovered element) with the following techniques:
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.