Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use a Java WebDriverWait with a JavaScript predicate that checks each current-document <img> for complete and naturalWidth > 0. This waits for the images present when the condition is checked to finish loading successfully; it does not, by itself, trigger offscreen lazy-loaded images or cover CSS backgrounds and other frames.
Wait for successful image loads with an explicit wait
Selenium’s page-load wait is not a guarantee that every image your test cares about is ready. A page can continue changing after navigation, and lazy-loaded images can still be pending after the browser fires its load event. An explicit wait lets the test poll for the condition it actually needs.
For successful loads of the current document’s <img> elements, use this Java condition:
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);"
));
The timeout is an example you can tune for your application, not a recommended universal limit. The returned value is true when the predicate succeeds. If it does not succeed before the timeout, Selenium’s wait fails with a timeout exception.
#1 Best Overall
Imports
import java.time.Duration;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait takes the driver and a Duration, and its inherited until method polls the condition until it returns a value that is neither null nor false, or the timeout is reached. Selenium documents explicit waits as a way to specify the precise condition required at each point in a test.
What the predicate checks
document.imagesselects the<img>elements in the current document.img.completemeans that the image has completed loading or has reached a state where no image data is available. It does not prove the image loaded successfully.img.naturalWidth > 0excludes images with no usable intrinsic width, including broken images and images without a usable source.Array.from(...).every(...)requires every image in the collection to satisfy both checks. If there are no images yet, the empty collection passes; it does not prove the application will not add images later.
Choose what “all images” means for your test
The predicate is intentionally scoped: it checks the current document’s image elements at the time the wait is evaluated. Before relying on it, decide which assets and which point in the application’s lifecycle your test needs to cover.
Images inserted by JavaScript
If the application adds image elements after the page initially renders, the predicate could pass before those elements exist. First wait for the component, result list, or other application state that inserts them; then wait for their images. If the relevant state and images can be expressed together, combine both checks in one predicate so the wait cannot finish between them.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →For example, after the application exposes a component with a stable selector, wait for that component before checking images:
Rank #2
wait.until(d -> !d.findElements(By.cssSelector("#results img")).isEmpty());
wait.until(d -> (Boolean) ((JavascriptExecutor) d).executeScript(
"return Array.from(document.querySelectorAll('#results img'))"
+ ".every(img => img.complete && img.naturalWidth > 0);"
));
This example checks only images inside #results. Replace the selector with one that identifies the component in your application. If the component can legitimately contain no images, use a different readiness condition rather than waiting for a non-empty image list.
Images that failed versus images that settled
The recommended predicate treats a failed image as a failure of the wait, because its intrinsic width is not positive. That is appropriate when the test requires usable image content. If the test only needs image requests to stop being pending, use img.complete alone and assert broken images separately:
Boolean imageRequestsSettled = wait.until(d -> (Boolean) ((JavascriptExecutor) d).executeScript(
"return Array.from(document.images).every(img => img.complete);"
));
Boolean noBrokenImages = (Boolean) ((JavascriptExecutor) driver).executeScript(
"return Array.from(document.images).every(img => img.naturalWidth > 0);"
);
These checks answer different questions: completion says the image is no longer pending, while the width check is a success criterion. Decide which outcome the test contract requires rather than treating the two as interchangeable.
CSS images and frames
document.images does not include images used as CSS backgrounds. Images inside an iframe belong to that frame’s document, not the top-level document. If either asset type matters, add a separate check for it: switch to the relevant frame before evaluating its document, and use an application-appropriate method for background assets. The sample predicate does not cover them.
Rank #3
Make lazy-loaded images load before waiting
Native lazy loading postpones fetching images until they approach the viewport. Consequently, an offscreen lazy image may not delay the window load event. A wait over document.images does not make the browser fetch an image that has not yet been requested; depending on the page, a deferred image may keep the condition false until it is requested, or the collection may pass before deferred content is added.
If your test needs images throughout a long page, scroll through the relevant content to bring lazy images near the viewport, then wait for the image condition. A simple approach is to advance down the page in increments and finish at the bottom:
JavascriptExecutor js = (JavascriptExecutor) driver;
long previousHeight = -1;
int maxScrolls = 100; // A safety limit; adjust for the page under test.
for (int i = 0; i < maxScrolls; i++) {
long height = ((Number) js.executeScript(
"return document.documentElement.scrollHeight;"
)).longValue();
long viewport = ((Number) js.executeScript(
"return window.innerHeight;"
)).longValue();
if (height == 0 || viewport == 0) {
break;
}
for (long y = 0; y < height; y += viewport) {
js.executeScript("window.scrollTo(0, arguments[0]);", y);
}
js.executeScript("window.scrollTo(0, document.documentElement.scrollHeight);");
long newHeight = ((Number) js.executeScript(
"return document.documentElement.scrollHeight;"
)).longValue();
if (newHeight == previousHeight) {
break;
}
previousHeight = newHeight;
}
wait.until(d -> (Boolean) ((JavascriptExecutor) d).executeScript(
"return Array.from(document.images).every(img => img.complete && img.naturalWidth > 0);"
));
The loop is a practical trigger, not a universal lazy-loading solution. It uses a safety limit so a page that keeps growing cannot scroll indefinitely. If scrolling inserts more content or the page height changes, adapt the trigger to that application—for example, wait for the specific content region to finish expanding. A page-specific check is more reliable than assuming that one trip down the document covers every deferred image.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Page-load strategy is not an image-ready condition
Selenium offers normal, eager, and none page-load strategies. The default, normal, waits for the document’s ready state to be complete; eager waits for it to be interactive; none does not block for a ready state. None of those settings expresses the test-specific condition that all required image elements loaded successfully. Ready-state completion also does not ensure that a JavaScript-driven application has finished inserting or updating content.
Use a page-load strategy to choose how navigation proceeds, then use an explicit wait for the application condition needed by the test. Avoid mixing implicit and explicit waits: Selenium warns that the interaction can make the resulting wait duration unpredictable.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Common failures and how to diagnose them
The wait times out although the page looks loaded
Inspect the images the predicate is checking. A broken image, an empty or missing source, or a lazy image that has not been requested can prevent the success predicate from passing. If failed requests should count as settled, wait on complete and make a separate assertion for broken images. If the image is lazy, trigger loading by scrolling the relevant content into view.
The wait succeeds but a later image is missing
The collection may have been empty or complete at the instant the predicate passed, while application code inserted another image afterward. Wait for the state that creates the images, or combine that state with the image condition. A short fixed sleep does not establish that the application has reached the required state.
Some visible page artwork is not checked
Confirm whether the artwork is an <img> in the current document. CSS backgrounds and images in child frames are outside this predicate. Handle the relevant frame separately or add a check suited to the asset type.
Best Value
The wait behaves unpredictably
Check whether the test has an implicit wait configured as well as this explicit wait. Selenium cautions against combining the two because nested waiting can produce unpredictable elapsed times. Prefer explicit waits for the conditions that matter to this test.
Performance and reliability considerations
An explicit wait repeatedly evaluates its condition until it passes or times out. Keep the JavaScript predicate focused and avoid doing unrelated work in every poll. A check over every image has work proportional to the number of image elements, so for a page with many images or a test concerned with one component, query only that component rather than scanning the entire document.
Set the timeout to match the expected behavior of the application and the environment in which the test runs. A larger timeout can accommodate slower loads but also makes a genuine failure take longer to report; a smaller one gives quicker feedback but may fail when the application is still legitimately loading. The cited Selenium and browser documentation establishes no universal performance number for this wait.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesOr skip the browser setup
If your goal is to obtain a page screenshot rather than assert Selenium test behavior, ScreenshotNeo offers a website screenshot API and MCP server. It is not a Selenium wait and does not replace assertions in a browser test. Its capture options include full-page screenshots with lazy images loaded, and its cleanup can accept cookie or consent banners and remove known consent platforms, newsletter popups, and chat widgets before the capture; each cleanup step can be turned off.
One GET request returns a screenshot or PDF. This cURL example saves a WebP capture of the specified page:
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 API documentation for request options and setup. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billed status. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to AI agents and MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
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 →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.

