Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsSome links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To capture a hover state, put the pointer over the target, wait until the visual change is complete, and take the screenshot without moving the pointer away. For a one-off image, Chrome DevTools can pin :hover and capture the node. For repeatable work or CI, use Playwright or Puppeteer to hover a stable locator and then capture the element, viewport, or full page.
Choose the capture method
| Method | Best for | Repeatability | JavaScript-driven hover | CI use |
|---|---|---|---|---|
| Chrome DevTools | Manual inspection and a quick image | Low to medium | Limited: forces CSS state only | No |
| Playwright | Regression tests and scripted captures | High | Yes, with a real pointer action | Yes |
| Puppeteer | Node.js browser automation | High | Yes, with page.hover() |
Yes |
| ScreenshotNeo | API or AI-agent captures without browser setup | High when the page interaction is supported by your capture workflow | Use a click or custom script where needed | Yes |
A forced pseudo-class is ideal when the effect is pure CSS. If hovering runs JavaScript, opens a component, fetches data, or changes application state, use a real pointer action in Playwright or Puppeteer.
Capture a hover state manually in Chrome DevTools
- Open the page in Chrome, then open DevTools with
Ctrl+Shift+I(Windows/Linux) orCmd+Option+I(macOS). - In the Elements panel, select the element whose hover appearance you need.
- Right-click the node and choose Force State > :hover. Chrome keeps the pseudo-class active even after the physical pointer leaves the element. The same menu can force states such as
:active,:focus,:visited, and:focus-within. - Right-click the node again and select Capture node screenshot. Chrome saves an image of that node.
If the effect is a dropdown, tooltip, mega-menu, or other overlay that extends outside the selected node, capture the viewport instead. Keep :hover forced while you use the DevTools command for a viewport screenshot. A node image clips content outside the node’s bounds, so it is the wrong target for an escaping overlay.
Recommended Free Tools
When DevTools appears to lose the style
- Make sure you forced the state on the element that owns the CSS rule; forcing it on a child may not match a selector such as
.card:hover .actions. - Check whether a more specific rule, an overlay, or an animation is hiding the change.
- For a JavaScript menu, forcing
:hoverdoes not dispatch the pointer events that the application expects. Use automation with a real hover instead.
Automate the capture with Playwright
Playwright’s hover action scrolls the locator into view, performs actionability checks, and moves the mouse to the element’s center. Use a semantic role or unique CSS selector rather than an nth-child path.
#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Element screenshot
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1
});
await page.goto('https://example.com', { waitUntil: 'networkidle' });
const moreDetails = page.getByRole('button', { name: 'More details' });
await moreDetails.waitFor({ state: 'visible' });
await moreDetails.hover();
await page.screenshot({ path: 'button-hover.png', animations: 'disabled' });
await browser.close();
The example captures the viewport. To capture only the hovered control, replace the final call with:
await moreDetails.screenshot({
path: 'button-hover.png',
animations: 'disabled'
});
Tooltip or menu outside the element
Use a viewport or full-page image after the hover action. A full-page capture is useful for a page-level state, but it can include content below the fold and may not represent a single viewport exactly.
await moreDetails.hover();
await page.waitForTimeout(300); // use a known UI delay, not an arbitrary long sleep
await page.screenshot({ path: 'menu-hover-full.png', fullPage: true, animations: 'disabled' });
Making Playwright runs deterministic
- Set viewport dimensions and device scale explicitly. CSS-pixel and device-pixel output are different choices; keep the same choice between baseline and comparison runs.
- Wait for fonts, images, and asynchronous menu content to settle. Prefer a selector-based wait or network-idle condition over a large fixed delay.
- Disable or freeze animations where your framework supports it. Otherwise, capture after a deterministic transition point.
- Move the mouse to a neutral location before a screenshot when you do not want hover styling. Playwright screenshots include whatever hover effect exists at capture time.
- Keep browser version, operating system, headless mode, hardware conditions, and power settings stable for visual regression. Rendering can vary across those factors.
Use Puppeteer for Node.js automation
Puppeteer’s page.hover(selector) finds the selector, scrolls it into view, and moves the pointer to its center. Then use page.screenshot() for the page or viewport, or an element handle for a local image.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.hover('.menu-trigger');
await page.screenshot({ path: 'menu-hover.png' });
await browser.close();
})();
Capture only the hovered element
const target = await page.$('.menu-trigger');
if (!target) throw new Error('Hover target not found');
await target.screenshot({ path: 'trigger-hover.png' });
Use a selector that identifies one intended element. If several elements match, Puppeteer may hover the first match, producing a plausible but incorrect image.
Rank #2
CSS hover versus JavaScript hover
Pure CSS
A rule such as .nav-link:hover { color: red; } can be reproduced with DevTools’ forced state or a real pointer. Forced state is convenient for inspecting computed styles and taking a node screenshot.
JavaScript behavior
Menus, tooltips, analytics-triggered components, and framework widgets may listen for pointerenter, mouseover, or related events. A real hover() call is the safer option because it performs the same pointer movement a visitor makes. If the component opens only after a delay, wait for a visible menu or tooltip selector rather than assuming the delay has elapsed.
Nested and delegated handlers
Event delegation can attach the handler to a parent while the visual target is a child. Hover the user-facing control, then wait for the stateful descendant. Inspect the DOM after hovering to confirm that the expected class, attribute, or menu node appeared.
Free tools Windows power users keep installed
One-click scans. No signup required.
Decide what to capture
- Element: best for a button, card, icon, or local style. It will not include a tooltip or menu positioned outside the element’s box.
- Viewport: best for an overlay whose position depends on the visible window.
- Full page: best when the hovered state must be documented alongside the entire page, though overlays and fixed-position elements require careful review.
For comparisons, fix the viewport, device scale, browser, fonts, and color scheme. Capture the same target with the pointer deliberately positioned each time. A stray pointer can either create a hover state or remove it before the image is written.
Rank #3
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Troubleshoot missing or incorrect hover effects
The screenshot looks like the normal state
Verify that the hover call completed before capture and that the selector identifies the visible element. In DevTools, force :hover on the node owning the rule. In automation, wait for a state-specific selector, class, or attribute.
The menu flashes and disappears
The pointer may be moving across a gap between trigger and menu, or an animation may still be running. Hover the trigger, wait for the menu to become visible, and disable animations where possible. For a CSS menu, use a parent selector whose hover region includes both trigger and panel.
The tooltip is cut off
You captured the element rather than the viewport. Take the viewport image after hovering, or adjust the screenshot region to include the overlay.
Nothing changes when forcing :hover
The component may be JavaScript-driven, use a different state such as focus, be covered by another element, or require a pointer event. Switch to Playwright or Puppeteer and inspect console errors and network requests.
Rank #4
Images differ between machines
Use the same browser and OS where possible, install identical fonts, set viewport and device scale, and avoid comparing a headed run with a headless run. Freeze animations and wait for web fonts and lazy images.
The target cannot be found
Wait for navigation and rendering, then use a stable role, label, test ID, or unique class. Avoid selectors tied to generated framework hashes or DOM position.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF; its capture options include custom JavaScript and CSS, clicking before capture, waits, device and viewport settings, full-page and element captures, and more. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. For an interaction that requires a real hover, use a custom script or click/wait workflow supported by your page; pure CSS hover can also be represented by injected CSS.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →One-call cURL example
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for the full option set, including output format, viewport, waits, scripts, and signed links. Python and Node.js clients use the same endpoint:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots each month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Best Value
Cost, performance, and reliability considerations
- Browser automation consumes startup time and memory. Reuse a browser process for a batch, but isolate pages when state or credentials must not leak.
- Network-idle waits improve completeness but can stall on sites with persistent analytics connections. Combine a reasonable timeout with a selector that proves the hovered UI is ready.
- Full-page images are larger and slower than element captures. Capture the smallest region that answers your question.
- For regression suites, store the exact viewport, scale, browser version, and wait conditions with each baseline.
- With an API, inspect the response status and the
X-Page-VerdictandX-Billedheaders so failed or clean captures are distinguishable.
FAQ
Can I capture a hover state without moving my physical mouse?
Yes. DevTools’ Force State > :hover pins the CSS pseudo-class, while Playwright and Puppeteer move a virtual pointer in the browser.
Why does a full-page screenshot not show the hover menu on every scroll position?
Hover is tied to a pointer position and a particular layout state. A full-page capture stitches content beyond the visible viewport; it is not the same as hovering separately at every scroll position.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallShould I use hover or focus for keyboard-accessible menus?
Capture the state your interface promises. If the menu opens on keyboard focus, reproduce focus and capture that state separately; a hover image does not prove keyboard behavior.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

