Image hover zoom — a lightbox without a library
Galleries, shops, stock-photo sites — thumbnails everywhere. To see an image in full, you have to click, wait, go back. This rule adds a hover preview: you hover a thumbnail, a larger version pops up by the cursor. A lightweight lightbox with no library at all.
The rule
The rule's JavaScript — URL pattern for the gallery you use, or * globally:
const box = document.createElement('img');
box.style.cssText =
'position:fixed;pointer-events:none;z-index:99999;max-width:40vw;' +
'max-height:80vh;box-shadow:0 8px 32px rgba(0,0,0,.4);' +
'border-radius:8px;display:none';
document.body.appendChild(box);
addEventListener('mouseover', (e) => {
const img = e.target.closest('img');
if (!img) return;
box.src = img.currentSrc || img.src;
box.style.display = 'block';
});
addEventListener('mousemove', (e) => {
box.style.left = (e.clientX + 24) + 'px';
box.style.top = (e.clientY + 24) + 'px';
});
addEventListener('mouseout', () => { box.style.display = 'none'; });
How it works
One preview element
We create exactly one <img> and reuse it, only swapping its src. No DOM cleanup, no leaks.
pointer-events: none
The preview sits on top of the page. Without pointer-events: none it would catch mouse events itself — the cursor would "enter" the preview, mouseout would misfire and the preview would flicker. With this rule the preview is transparent to the mouse.
currentSrc versus src
img.currentSrc is the version the browser actually picked from srcset — usually sharper than src. We reach for it first, with src as a fallback.
position: fixed plus client coordinates
position: fixed places the preview relative to the window, and e.clientX/clientY is the cursor position relative to the window — they match, so the preview tracks the cursor regardless of scroll.
A variant — full size instead of the thumbnail
In many galleries the thumbnail has a URL like .../thumb/photo.jpg while the full size is .../large/photo.jpg. Swap box.src for the rewritten address:
box.src = (img.currentSrc || img.src).replace('/thumb/', '/large/');
The pattern differs site to site — look at the src of a thumbnail and of a full photo, find the difference.
Pitfalls
- CSS backgrounds are not images. Photos placed as a
background-imageare not<img>elements — this rule will not catch them. - Lazy loading. A thumbnail with
loading="lazy"that you have not scrolled to yet may have nosrc. Once it appears in view it works normally. - Tiny icons. The rule catches every
<img>, UI icons included. To target only the gallery, narrow the selector, e.g.e.target.closest('.gallery img').
See also
- Examples — the zoom snippet and other ready-made code
- Auto-pager — another "add a feature the page lacks" rule
- Mini-IDE in a tab — where to test rules like this
Install JustZix — and view photos without clicking each one.
Rate this post
No ratings yet — be the first.