Short answer: a browser’s fullPage screenshot captures the document’s scrollable height, not the hidden contents of every nested overflow:auto element. For a normal scrollable container, temporarily expand it to its scrollHeight, capture that element, and restore its styles. For virtualized or lazy-loaded lists, scroll in increments, wait for rows to render, capture tiles, and stitch them. Then capture the page and the element separately—or expand the element before the page capture—if both views are needed.
Why a full-page screenshot can miss a scrollable element
Playwright defines a full-page image as a screenshot of the full scrollable document, “as if you had a very tall screen and the page could fit it entirely.” Its fullPage option therefore follows the document’s layout height. A nested element such as <div class="results"> can have its own scrollable height while contributing only a fixed box to the document. The browser captures that box, including only the rows currently visible inside it.
This distinction applies to automated browsers, Firefox Developer Tools, Selenium and Puppeteer. Decide first whether the missing content belongs to a regular container whose DOM already contains every row, or to a virtualized/lazy-loaded list that creates rows only while you scroll.
Choose the right capture strategy
| Situation | Recommended method | Nested content handled automatically? | Needs DOM mutation or stitching? |
|---|---|---|---|
| Entire document, no nested overflow | Playwright page.screenshot({fullPage:true}), Firefox “entire page,” Selenium Firefox full-page API, or Puppeteer full-page screenshot |
Yes, at document level | No |
| Static element with all rows in the DOM | Set height/max-height to scrollHeight (or auto), set overflow visible, capture the element, restore styles |
No | Temporary style change |
| Virtualized or lazy-loaded list | Scroll the element by viewport-sized increments, wait for rows/network activity, capture overlapping tiles, stitch | No | Scrolling, waiting and image stitching |
| Need document and nested region | Capture the document and expanded or stitched element as separate assets | No | Usually |
Static scrollable elements with Playwright
Capture the element at its full scrollHeight
The following Node.js script saves inline styles, waits for fonts and images, expands .results, captures it, and restores the original state even if the screenshot fails. It assumes all rows are already present in the DOM.
Recommended Free Tools
#1 Best Overall
- CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
- WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
- A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents
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/results', { waitUntil: 'networkidle' });
await page.evaluate(() => document.fonts?.ready);
await page.waitForLoadState('networkidle');
const results = page.locator('.results');
await results.waitFor();
await results.evaluate((el) => {
const node = el;
node.dataset.captureOriginalStyle = node.getAttribute('style') ?? '';
node.style.setProperty('height', `${node.scrollHeight}px`, 'important');
node.style.setProperty('max-height', 'none', 'important');
node.style.setProperty('overflow', 'visible', 'important');
});
await page.waitForTimeout(100); // allow layout and image decoding to settle
await results.screenshot({ path: 'results-full.png' });
await results.evaluate((el) => {
const original = el.dataset.captureOriginalStyle;
if (original) el.setAttribute('style', original);
else el.removeAttribute('style');
delete el.dataset.captureOriginalStyle;
});
await browser.close();
Use auto instead of an explicit pixel height when the component’s CSS can expand naturally. Measuring scrollHeight is safer when nested rules impose a fixed height. If the element contains a sticky header, its position may change after expansion; capture it separately or temporarily neutralize that rule.
Capture the whole document after expansion
If the expanded region must appear inside a page screenshot, perform the same mutation and then call:
await page.screenshot({ path: 'page-with-results.png', fullPage: true });
Restore styles after the page image is written. Fixed-position cookie notices, chat buttons and other overlays can be repeated in a tall capture; hide or mask them for deterministic output, then restore their styles too.
Virtualized and lazy-loaded lists: scroll, wait and stitch
Virtualized components render only the rows near the viewport. Expanding the container does not reveal rows that do not exist yet. Capture overlapping viewport tiles while moving the container’s own scroll position. Overlap helps prevent one-pixel gaps, but remove the repeated band when stitching.
Reliable tile loop
- Wait for fonts, initial data and images. Record the element’s
clientHeightand currentscrollTop. - Scroll the element by roughly one viewport height minus an overlap (for example, 80–120 CSS pixels).
- After each scroll, wait for a row selector, a network-idle condition, or a short delay appropriate to the application.
- Capture the element viewport at each position. Keep the scrollbar consistent or hide it for all tiles.
- Stop when
scrollTop + clientHeight >= scrollHeight, allowing for rounding. - Stitch tiles in CSS-pixel coordinates, trimming the known overlap. Validate that the first and last expected row appear exactly once.
const box = page.locator('.results');
const overlap = 100;
const tiles = [];
let top = 0;
while (true) {
await box.evaluate((el, y) => { el.scrollTop = y; }, top);
await page.waitForTimeout(150); // replace with a row/network wait when available
const file = `tile-${tiles.length}.png`;
await box.screenshot({ path: file });
tiles.push({ file, top });
const state = await box.evaluate((el) => ({
top: el.scrollTop,
client: el.clientHeight,
scroll: el.scrollHeight
}));
if (state.top + state.client >= state.scroll - 1) break;
top = Math.min(top + state.client - overlap, state.scroll - state.client);
}
For production stitching, use an image library that works in your language and crop each tile’s overlap before compositing. A sticky row header may be painted on every tile; crop it from subsequent tiles or temporarily disable its sticky positioning. A scrollbar can also move the content width between captures, so keep its visibility and width constant.
Playwright, Firefox, Selenium and Puppeteer options
Playwright
Use await page.screenshot({ path: 'screenshot.png', fullPage: true }); for the document and await page.locator('.results').screenshot({ path: 'results.png' }); for one element. Playwright automatically scrolls an element into view for actions that require it, but that behavior does not expand arbitrary overflow containers. The Playwright CLI provides screenshot --full-page; its screenshot tooling also supports a target selector/reference, filename, image type and CSS/device scale.
Rank #2
- CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
- SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
Firefox Developer Tools
Open the toolbox screenshot control and choose “Take a screenshot of the entire page” for document overflow. In Inspector, right-click a node and choose “Screenshot Node” for one element. Firefox does not automatically turn a nested overflow box into a full-height element; use the expansion or scroll-and-stitch method first.
Selenium with Firefox
driver.get_full_page_screenshot_as_file("full.png")
This captures the full document. A nested region still requires an element-specific strategy: temporarily expand it with JavaScript, or repeatedly scroll and capture it.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Puppeteer
Puppeteer’s page screenshot supports full-page Chrome captures and element screenshots through an element handle. As with Playwright, full-page means the document’s scrollable page, not every nested scrolling context; virtualized content requires incremental rendering and stitching.
Make captures repeatable in CI
- Wait for real readiness: await
document.fonts.ready, image decoding and the application’s final data state before readingscrollHeight. - Freeze motion: inject CSS that disables transitions, animations and blinking carets, or mask those regions.
- Control scale: record viewport size and device pixel ratio. Playwright’s CSS-pixel layout and device-pixel output differ when
deviceScaleFactoror screenshot scale changes. - Control overlays: hide cookie banners, chat widgets and fixed toolbars when they obscure tiles; restore the DOM after capture.
- Check completeness: assert the expected row count or last-item marker, not merely that a PNG was produced.
- Preserve state: save and restore inline styles, scroll positions and any classes changed for capture.
Troubleshooting common failures
The screenshot shows only the visible rows
The list is a nested scrolling context. Confirm that scrollHeight > clientHeight, then expand a static container or use tile capture. A document-level fullPage flag alone is insufficient.
Rank #3
- Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
- Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
- Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
- In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
- Ultra-thin bezels: Maximize your viewing experience with thin bezels.
Expansion produces a blank or clipped image
A parent may have overflow:hidden, a fixed grid track or a transform. Inspect ancestor styles, temporarily remove the clipping rule, and wait for a layout frame before capture. Restore every changed rule afterward.
Rows disappear while scrolling
This indicates virtualization. Do not rely on one expanded screenshot. Wait for the row-render promise or a sentinel element after each scroll and verify that the list’s data count has been reached.
Images are missing below the fold
Lazy images may load only after intersection. Scroll each segment into view, wait for its image’s complete state (and decoded pixels where supported), then capture. Network-idle alone may occur before an intersection-triggered request starts.
Tiles have duplicated headers or gaps
Use a fixed overlap, remove the overlap exactly once during stitching, and account for sticky headers. Capture at a constant device scale; mixing CSS and device pixels causes alignment errors.
The page capture repeats a banner or chat button
Fixed-position elements are painted in each viewport slice. Hide or mask them during capture, or capture the nested element separately and compose it with the page image.
Rank #4
- CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
- SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
- MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
- KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
- INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient
Or skip the browser setup
ScreenshotNeo is a screenshot API and MCP server for developers. It can target one element by CSS selector, load lazy images for full-page captures, wait for a selector, delay or network idle, execute custom JavaScript/CSS, click before capture, hide selectors, set device and retina options, and return PNG, JPEG, WebP or PDF. For a nested region, use its element selector with the relevant height/overflow script; for a document capture, use the full-page option.
Free tools Windows power users keep installed
One-click scans. No signup required.
One-call example (the API documentation lists all parameters):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed as clean shots, and response headers report the page verdict and billing status. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Cost, performance and reliability decisions
- Expansion is fastest when every row already exists, because it needs one layout pass and one image.
- Stitching costs more time and memory but is the dependable option for virtualization and lazy rendering.
- Separate captures are safer when sticky or fixed UI would be duplicated in a combined page image.
- API capture reduces browser maintenance when you need repeatable URL jobs, bulk capture (up to 100 URLs per call), signed webhooks, caching with a chosen TTL or a usage API.
FAQ
Does CSS height:auto always reveal a nested list?
No. It works only when ancestors and the component’s layout permit expansion and all required rows are present in the DOM. Virtualized lists still need scrolling.
Should I include the scrollbar in the final image?
Choose deliberately. Keeping it can document the UI state; hiding it produces a cleaner asset. Use the same choice for every tile so widths align during stitching.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsBest Value
- 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
- 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
- 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.
Can a PDF capture solve nested scrolling?
Not by itself. A PDF or full-page image follows the document layout. Expand the element or render and stitch its content before generating the final document.
Frequently Asked Questions
How do I know whether a list is virtualized?
Inspect the DOM while scrolling. If the number of row nodes stays small while different records replace them, the list is virtualized and requires incremental capture.
What is the safest way to avoid changing the live page?
Run the capture in an isolated browser context or a duplicate page, save every modified style and scroll position, and restore them in a finally-style cleanup block.
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.




