Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a Selenium Java explicit wait that evaluates every <img> in the current document. The reliable success condition is that each image is complete and has a positive intrinsic width:

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
wait.until(d -> (Boolean) ((JavascriptExecutor) d).executeScript(
    "return Array.from(document.images).every(img => img.complete && img.naturalWidth > 0);"
));

The naturalWidth test is important: complete can also be true for an empty source or a broken image. This wait covers the current document’s <img> elements; it does not automatically fetch off-screen lazy images, inspect CSS backgrounds, or enter child frames.

Complete runnable Java example

The following test opens a page, waits for its images to load successfully, and then continues with assertions or screenshots.

import java.time.Duration;

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;

public class WaitForImages {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        try {
            driver.get("https://example.com");

            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
            Boolean imagesLoaded = wait.until(d -> (Boolean)
                ((JavascriptExecutor) d).executeScript(
                    "return Array.from(document.images).every(" +
                    "img => img.complete && img.naturalWidth > 0);"
                )
            );

            System.out.println("Images loaded: " + imagesLoaded);
        } finally {
            driver.quit();
        }
    }
}

Add Selenium’s Java client to your build in the usual way for your project, and use a browser driver compatible with the browser under test. The imports above are sufficient for the wait itself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why an explicit wait is needed

Selenium navigation’s default page-load behavior waits for a document ready state, but a ready document is not the same as a fully updated application. JavaScript may insert images after navigation, replace placeholders, or change image URLs after an API response. An explicit wait polls the condition your test actually needs instead of assuming that navigation completion is enough.

WebDriverWait is a specialization of FluentWait<WebDriver>. Its constructor accepts a WebDriver and a Duration; until keeps polling until the function returns a value that is neither null nor false, or the timeout expires.

What the JavaScript predicate checks

document.images

This collection contains the <img> elements in the current document. Array.from(...).every(...) requires every member to satisfy the predicate. If there are no image elements, every returns true; that is logically correct for “all current images,” but you can add a separate assertion if the page must contain at least one image.

img.complete

complete means the browser has finished attempting to load the image resource. It can be true for a successfully decoded image, a broken request, an empty or missing source, or an image that has not required a fetch. It therefore describes settled loading state, not success.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

img.naturalWidth > 0

A positive intrinsic width indicates that the browser obtained usable image data. Combining it with complete makes the sample fail when an image request finished unsuccessfully. If your contract is only that requests have settled, use img.complete alone and report broken images separately.

Define what “all images” means

The correct predicate depends on the test’s scope. Decide this before choosing a wait.

  • Current document: the supplied predicate checks every current <img>.
  • A component: query a known container and evaluate its images, avoiding unrelated ads or widgets.
  • Images inserted later: first wait for the application state or component that creates them, then run the image predicate, or combine both checks in one script.
  • Lazy-loaded content: scroll through the relevant page so the browser requests images near the viewport, then wait for the resulting elements.
  • CSS backgrounds: these are not in document.images and need a separate asset-loading strategy.
  • Frames: switch into each frame and evaluate that frame’s document independently.

Lazy-loaded images and scrolling

Native lazy loading postpones a fetch until an image approaches the viewport. Such an image may still be pending when the window load event fires. A global image wait cannot force an off-screen lazy image to load; it can only observe the elements and states that currently exist.

For a long page, scroll in increments, allow the application to schedule requests, and then apply the same success check. A simple JavaScript scroll loop is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
((JavascriptExecutor) driver).executeScript(
    "window.scrollTo(0, document.body.scrollHeight);"
);

For robust tests, scroll through several viewport-sized positions rather than jumping only to the bottom, because some applications load content when an intersection occurs. After scrolling, wait again:

wait.until(d -> (Boolean) ((JavascriptExecutor) d).executeScript(
    "return Array.from(document.images).every(" +
    "img => img.complete && img.naturalWidth > 0);"
));

Whether this is sufficient depends on the application’s lazy-loading implementation. Some frameworks replace data-src only after an observer callback, so include the framework’s “content ready” signal when one is available.

Wait for a component and its images together

If images are inserted asynchronously, waiting only on document.images can finish before insertion occurs. Combine a component-presence check with the image check:

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
wait.until(d -> (Boolean) ((JavascriptExecutor) d).executeScript(
    "const panel = document.querySelector('#product-gallery');" +
    "if (!panel) return false;" +
    "return Array.from(panel.querySelectorAll('img')).every(" +
    "img => img.complete && img.naturalWidth > 0);"
));

This condition does not assert that a particular number of images exists. If the gallery must contain, for example, four images, assert that count separately so a completely empty gallery cannot pass.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Page-load strategy versus an image wait

Approach What it tells you What it does not guarantee
normal (default) Navigation waits for the document ready state complete. JavaScript-driven updates, late-inserted images, or successful image decoding.
eager Navigation waits for the document to become interactive. Most remaining resources and application work.
none Navigation does not block on a ready state. Any readiness condition; your test must provide explicit waits.
Custom image wait The exact image condition you define, including success via naturalWidth. Images outside the queried document, CSS assets, or lazy resources never triggered.

Choose the narrowest condition that matches the test. Do not mix implicit and explicit waits; Selenium warns that their interactions can make total wait time unpredictable.

Common failures and fixes

TimeoutException

Cause: at least one image never reaches the predicate, often because the URL is broken, blocked, requires authentication, or belongs to an off-screen lazy element.

Fix: inspect the failing elements in the browser, log each image’s src, complete, and naturalWidth, verify network access and credentials, and trigger lazy loading by scrolling. Increase the timeout only when the page legitimately needs more time; a larger timeout does not repair a failed request.

The wait passes despite a broken image

Cause: the predicate checks only complete.

Fix: require naturalWidth > 0, as in the main example, and report the URL of any element that fails.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The wait passes before images appear

Cause: the page initially has no matching images and asynchronous code inserts them later.

Fix: wait for the container, a known image count, or the application’s loaded state before evaluating image success.

Lazy images remain unloaded

Cause: they are intentionally outside the viewport, so no request has started.

Fix: scroll through the page, wait for each batch, and then run the predicate. If the test is only about visible content, limit the query to that region instead of forcing every page image.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Images in an iframe are missing

Cause: each frame has its own document.

Fix: locate the frame, call driver.switchTo().frame(...), evaluate the predicate there, then return with driver.switchTo().defaultContent().

CSS background images are not detected

Cause: background images are stylesheets resources, not <img> elements.

Fix: test the component’s computed styles or application-specific readiness signal, and treat that as a separate condition.

Diagnostics for a failing page

Run this script to identify which current-document images are not successful:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Object[] failures = (Object[]) ((JavascriptExecutor) driver).executeScript(
    "return Array.from(document.images)" +
    ".filter(img => !(img.complete && img.naturalWidth > 0))" +
    ".map(img => ({src: img.currentSrc || img.src," +
    "complete: img.complete, naturalWidth: img.naturalWidth}));"
);

Use the returned records in test logs. They distinguish a not-yet-settled request from a settled request with zero intrinsic width and show the URL the browser actually selected through currentSrc.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a dependable page image rather than a browser test, ScreenshotNeo provides a website screenshot API and MCP server. One request can return PNG, JPEG, WebP, or PDF, with options for full-page capture, lazy-image loading, selectors, waits, custom JavaScript and CSS, device emulation, headers, cookies, blocking rules, and more. Cookie 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 status. Its MCP tools let AI agents call take_screenshot, get_page_info, and capture_pdf.

Use the API documentation at https://screenshotneo.com/docs/ for the complete option list. A minimal cURL request is:

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}`);

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account to try it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Performance, reliability, and cost decisions

  • Use the shortest timeout that accommodates the application, and keep polling focused on the component under test when possible.
  • Do not wait for third-party advertising or analytics images unless they are part of the requirement; they can make a test slow and nondeterministic.
  • For visual assertions, wait for successful intrinsic dimensions before capturing. For request-settlement tests, allow failed images and assert failures explicitly.
  • When pages change continuously, define a stable boundary such as a gallery count, a network-idle signal supplied by the application, or a component-ready marker.
  • Keep the browser session and test data consistent so authentication, geolocation, and responsive layout do not change which images are requested.

Frequently Asked Questions

Does Selenium have a built-in “wait for all images” condition in Java?

The documented approach is a custom explicit wait. Evaluate the page’s image collection with JavaScript and return a boolean that represents your test’s definition of loaded.

Should I use window.onload instead?

Not when JavaScript inserts content or lazy loading is involved. The load event can fire while deferred images remain unfetched, so wait for the specific state your test needs.

What does a 20-second timeout mean?

It is the maximum time for the explicit wait in the example, not a guarantee that the page will load within that period. A timeout raises an exception when the predicate never becomes true.

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.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.