To capture a lazy-loaded page with Selenium in Java, scroll the page in steps, wait after each step for the specific content you expect, and take the screenshot only when your completion condition is met. A completed navigation or document.readyState value of complete does not guarantee that JavaScript-driven content has appeared.
Use a content signal, not a fixed delay
Lazy-loaded pages often add content as a visitor approaches it. Selenium can execute JavaScript to scroll, while WebDriverWait can poll for a meaningful change, such as a result count increasing or a target element becoming visible. The selector and completion rule must match the page you are capturing; there is no universal selector or fixed wait time that proves every page is complete.
As an Amazon Associate I earn from qualifying purchases.
The example below uses a result-count increase as its signal. Replace .result-item with a selector from the target page. It scrolls incrementally, stops after two consecutive rounds without an increase, and has a maximum-round safety bound. Treat the quiet-round rule as a heuristic, not proof that all content has loaded.
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 problemsRunnable Java example
This example uses Selenium’s Java APIs, ChromeDriver, and a Java version that supports text blocks only if you choose to add them; the code itself uses standard Java syntax. Add Selenium and a compatible ChromeDriver setup to your project before running it.
#1 Best Overall
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
public class LazyPageScreenshot {
public static void main(String[] args) throws Exception {
String url = "https://example.com"; // Replace with the page to capture.
By itemSelector = By.cssSelector(".result-item"); // Replace with the page's real selector.
int maximumRounds = 20; // Safety bound; tune for the target page.
WebDriver driver = new ChromeDriver();
try {
driver.get(url);
JavascriptExecutor js = (JavascriptExecutor) driver;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
AtomicInteger previousCount = new AtomicInteger(
driver.findElements(itemSelector).size()
);
int stableRounds = 0;
for (int round = 0;
round < maximumRounds && stableRounds < 2;
round++) {
js.executeScript(
"window.scrollBy(0, Math.max(300, window.innerHeight * 0.8));"
);
int countBeforeScroll = previousCount.get();
try {
wait.until(d ->
d.findElements(itemSelector).size() > countBeforeScroll
);
previousCount.set(driver.findElements(itemSelector).size());
stableRounds = 0;
} catch (TimeoutException noNewItemsWithinTimeout) {
// May mean no more items, or a wrong selector/condition.
stableRounds++;
}
}
File screenshot = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
Files.copy(
screenshot.toPath(),
Path.of("lazy-page.png"),
StandardCopyOption.REPLACE_EXISTING
);
} finally {
driver.quit();
}
}
}
The AtomicInteger keeps the count usable inside the wait lambda while allowing it to be updated between rounds. The example captures after the loop, even if the maximum-round bound is reached; in production, decide whether reaching that bound should instead fail the job or produce an explicitly partial capture.
Choose a completion condition that fits the page
- Known element: wait for the target element to become visible when a specific section or item is the goal.
- Known total: wait until the number of matching results reaches the expected count when the page exposes one.
- Loading indicator: wait for a loading marker to disappear, ideally alongside a check that the desired content is present.
- Explicit end state: use a page-provided end marker or completion signal when available.
A count increase is not suitable when the page replaces existing elements rather than appending them. In that case, wait for the target element or another signal that reflects the content you need.
Rank #2
Adapt the scrolling to the page
The sample scrolls the document by a fraction of the viewport. Incremental scrolling is useful when a page responds as successive regions approach the viewport; one jump to the bottom may not trigger the same sequence. This is site-dependent behavior, not a Selenium guarantee.
Document scroll or nested container
If the page’s results sit inside a scrollable panel, scrolling window will not move that panel. Identify the scroll container and move that element instead, for example by executing JavaScript that changes the container’s scrollTop. Verify that the intended region actually moves and that the site’s content appears afterward.
Rank #3
Scroll toward a known element
If the target is already discoverable in the DOM, use Selenium element interaction or scroll it into view. This can be a better fit when the page listens to element visibility or when user-like interaction matters. Selenium JavaScript execution runs in the currently selected frame or window; switch into the correct frame before looking for or scrolling content inside it. Selenium also documents that cross-domain restrictions can affect scripts, particularly custom requests or access involving another frame.
Save the right kind of capture
For a screenshot, Selenium’s Java API supports TakesScreenshot and OutputType.FILE, as used above. The driver and browsing context can affect what the screenshot contains. Check the saved image in the actual target environment rather than assuming the result is a full-page image.
Rank #4
If you need only a component, Selenium also supports taking a screenshot of a web element. If you need the whole page, confirm that your chosen browser and driver support the full-page mechanism you intend to use; a normal screenshot may capture only the current viewport.
If by “capture” you mean page text or DOM data, locate and extract the loaded elements after the wait condition. Do not rely on getPageSource() as proof that post-load JavaScript changes are represented: Selenium’s Java API does not guarantee that returned source reflects modifications made after the page loaded.
Best Value
Waits, timing, and reliability
Explicit waits poll until a particular condition becomes true, which suits asynchronously arriving content. Selenium warns: “Do not mix implicit and explicit waits.” Combining them can make timing unpredictable. Prefer an explicit wait tied to the page’s content signal rather than using a fixed sleep as the main completion test.
driver.get() returning, or the browser reaching document.readyState == "complete", is not enough to establish that a single-page app has stopped adding JavaScript-driven content. Page-load strategy checks document readiness; it does not provide a universal application-level completion signal.
For a transition that itself requires asynchronous JavaScript, Selenium Java provides executeAsyncScript. The script must invoke Selenium’s injected completion callback, and the configured script timeout must allow the operation to finish. For most scroll-triggered loading, a synchronous scroll followed by an explicit DOM condition is simpler to inspect and debug.
Windows 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 reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteTroubleshooting incomplete captures
| Symptom | Likely cause | What to check or change |
|---|---|---|
| The first screenshot is missing lower-page content | Navigation completed before the page’s JavaScript added it. | Scroll through the page and wait for the target content condition before capturing. |
| Loading stops at a fixed point | The page may load only as successive regions approach view, or the content may be in a nested scroll container. | Use incremental scrolling and verify which element actually scrolls. |
| The wait times out although content seems visible | The selector or condition may be wrong, or the content may be inside a frame Selenium has not selected. | Check the selector against the rendered DOM and switch to the correct frame before querying it. |
| The image is not a full-page screenshot | Screenshot scope can differ across driver and browsing context. | Inspect the output and use a full-page mechanism supported by the target browser and driver if necessary. |
getPageSource() appears stale |
Returned source is not guaranteed to reflect JavaScript changes made after loading. | Verify the relevant elements directly or inspect the rendered screenshot. |
Or skip the browser setup
ScreenshotNeo can capture a URL with one GET request. Before capture, it accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of these steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, with the response identifying the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and other MCP clients.
For example, with ScreenshotNeo API access configured:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
The service returns PNG, JPEG, WebP, or PDF captures. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month with no card.
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.
Recommended Free Tools




