Rewriting the jQuery hoverIntent plugin in vanilla JavaScript
Published: 5 February 2021 • Tags: javascript
One of the great jQuery plugins that’s been around for ages (and still in development it seems) is jquery-hoverIntent. It’s a script that tracks mouse movement and delays any hover actions until the user’s mouse has slowed down - in other words when we think they intended to hover over an element.
In my previous article I looked at a simple way of replicating the effect using JavaScript’s setTimeout
function. In this tutorial I’ll run through my creation of a full replacement for hoverIntent without the need for jQuery. I think it’s an interesting look into the internals of a popular jQuery plugin.
There’s nothing particularly wrong with jQuery if you are using it for many parts of your site, but it doesn’t make sense to download and execute many kilobytes when in modern JS we can program the same behaviour without it.
Why hover intent? #
The original use case for hoverIntent was drop-down menus. The standard CSS hover menu has poor usability by default - I gave several reasons why in the previous article. I explained a simple idea to delay the dropdown using a fixed timeout.
I went this way initially as I thought the hoverIntent system would be very complicated to set up, but as we’ll see later it’s actually a simpler algorithm than I imagined. That previous method works fine, but there are two minor problems:
- The timeout is fixed. Although I set it very short, 300ms is noticable by most people. The jQuery plugin only delays while the mouse is still moving; once it stops the event triggers only a few milliseconds later.
- The dropdowns will still appear if the user’s mouse is moving over the menu (for example moving horizontally across the menu). With hoverIntent nothing appears until the mouse stops.
The HTML/CSS setup #
We’ll use the same HTML & CSS as last time:
<ul id="nav" class="menu">
<li class="menu-item">Articles
<ul class="submenu">
<li>Design</li>
<li>Front-end coding</li>
<li>Back-end coding</li>
</ul>
</li>
<li class="menu-item">Tutorials
<ul class="submenu">
<li>HTML</li>
<li>CSS</li>
<li>Javascript</li>
</ul>
</li>
<li class="menu-item">...</li>
<li class="menu-item">...</li>
<li class="menu-item">...</li>
</ul>
.menu {
display: flex;
max-width: 800px;
list-style: none;
margin: 1rem auto;
padding: 0;
height: 36px;
background: #ccc;
}
.menu-item {
flex-grow: 1;
position: relative;
padding: 10px;
text-align: center;
}
.submenu {
display: none;
list-style: none;
position: absolute;
top: 36px;
left: 0;
margin: 0;
padding: 0;
width: 100%;
background: #ddd;
}
.submenu > li {
padding: 10px;
}
.menu-item.active > .submenu {
display: block;
}