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

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 Selenium explicit wait with a custom predicate. Record the element’s current height, then poll until its height differs from that baseline—or until it reaches a target value within a small tolerance. This synchronizes with the page’s actual state instead of guessing with time.sleep().

Wait for a height change with a custom explicit wait

Height is not one of Selenium’s named Expected Conditions. Selenium provides conditions for presence, visibility, text, title and staleness, but a change from one rendered height to another is application-specific. Define that state yourself and pass it to an explicit wait.

Python: wait until the height differs from its baseline

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

locator = (By.CSS_SELECTOR, "#panel")
panel = driver.find_element(*locator)
initial_height = driver.execute_script(
    "return arguments[0].getBoundingClientRect().height;", panel
)

def height_changed(d):
    # Re-find the node in case the framework replaced it.
    element = d.find_element(*locator)
    current_height = d.execute_script(
        "return arguments[0].getBoundingClientRect().height;", element
    )
    return abs(current_height - initial_height) > 1

WebDriverWait(driver, 10, poll_frequency=0.2).until(height_changed)

until() calls the predicate repeatedly. A truthy return ends the wait; if 10 seconds pass without a change greater than one CSS pixel, Selenium raises TimeoutException.

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

Python: wait for a specific height

target_height = 320

def height_is_target(d):
    element = d.find_element(*locator)
    current_height = d.execute_script(
        "return arguments[0].getBoundingClientRect().height;", element
    )
    return abs(current_height - target_height) <= 1

WebDriverWait(driver, 10).until(height_is_target)

Use a target predicate when the UI contract is known—for example, an accordion that should open to about 320 pixels. The one-pixel tolerance accommodates fractional layout values and rounding.

Choose the right measurement

getBoundingClientRect().height

This JavaScript expression returns the rendered border-box height and can be fractional, such as 319.65625. It reflects the value Selenium sees after CSS layout, transforms excluded from the element’s layout dimensions. A tolerance prevents a wait from failing because the browser never produces an exact integer.

Selenium’s size or rect accessors

element = driver.find_element(*locator)
height = element.size["height"]
# or, where supported:
height = element.rect["height"]

These accessors are convenient when integer dimensions are sufficient. Use JavaScript when you need the browser’s fractional rendered value or want one measurement method shared across bindings.

Capture the baseline at the correct moment

Take the baseline only after the element exists and after the action that establishes the initial state. If the panel starts collapsed, locate it, record its collapsed height, click the expand control, and then wait. Recording before a navigation, click, or state reset can make the predicate detect the wrong transition.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

panel_locator = (By.CSS_SELECTOR, "#panel")
open_locator = (By.CSS_SELECTOR, "#open-panel")
wait = WebDriverWait(driver, 10)

panel = wait.until(EC.presence_of_element_located(panel_locator))
before = driver.execute_script(
    "return arguments[0].getBoundingClientRect().height;", panel
)
driver.find_element(*open_locator).click()

def expanded(d):
    element = d.find_element(*panel_locator)
    now = d.execute_script(
        "return arguments[0].getBoundingClientRect().height;", element
    )
    return now > before + 1

wait.until(expanded)

Presence is enough for measuring dimensions; visibility is useful when the element must also be displayed. Selenium’s visibility condition checks that an element is present and has positive width and height, but it does not prove that the height changed.

Handle animation and dynamic DOM updates

Re-find nodes that may be replaced

React, Vue, Angular and other front-end code can replace an element during an update. Holding the original WebElement and repeatedly reading it can produce StaleElementReferenceException. The predicates above call find_element on every poll, so they use the current node.

Locate inside the predicate when the element is initially absent

def height_after_render(d):
    element = d.find_element(By.CSS_SELECTOR, "#results")
    value = d.execute_script(
        "return arguments[0].getBoundingClientRect().height;", element
    )
    return value > 0

WebDriverWait(driver, 15, poll_frequency=0.2).until(height_after_render)

If a missing element is expected during rendering, configure the wait to ignore or handle the relevant lookup exception, or first wait for presence and then measure. Do not hide unrelated errors: a selector typo should fail quickly rather than look like a slow animation.

Wait for the final state, not merely the first movement

A height-change predicate becomes true as soon as an animation moves one pixel. That is correct for “has changed,” but not for “finished expanding.” Prefer a known target, a minimum expected height, or a stability predicate that observes the same value for several polls.

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.
target = 320

def finished(d):
    element = d.find_element(*locator)
    value = d.execute_script(
        "return arguments[0].getBoundingClientRect().height;", element
    )
    return abs(value - target) <= 1

WebDriverWait(driver, 10, poll_frequency=0.1).until(finished)

Equivalent patterns in Java and JavaScript

Java

By locator = By.cssSelector("#panel");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement panel = driver.findElement(locator);
double initial = ((Number)((JavascriptExecutor) driver).executeScript(
    "return arguments[0].getBoundingClientRect().height;", panel)).doubleValue();

wait.until(d -> {
    WebElement current = d.findElement(locator);
    double height = ((Number)((JavascriptExecutor) d).executeScript(
        "return arguments[0].getBoundingClientRect().height;", current)).doubleValue();
    return Math.abs(height - initial) > 1;
});

Use the Selenium Java binding’s WebDriverWait and a lambda (an ExpectedCondition is also suitable). Re-locate inside the lambda for replacement-prone DOMs.

JavaScript (selenium-webdriver)

const {Builder, By} = require('selenium-webdriver');

const driver = await new Builder().forBrowser('chrome').build();
const locator = By.css('#panel');
const panel = await driver.findElement(locator);
const initial = await driver.executeScript(
  'return arguments[0].getBoundingClientRect().height;', panel
);

await driver.wait(async () => {
  const current = await driver.findElement(locator);
  const height = await driver.executeScript(
    'return arguments[0].getBoundingClientRect().height;', current
  );
  return Math.abs(height - initial) > 1;
}, 10000, 'Panel height did not change');

The JavaScript binding’s driver.wait accepts an asynchronous function and a timeout in milliseconds. A false result keeps polling; timeout failure includes the supplied diagnostic message.

Timeouts, polling, and wait strategy

Set the timeout to the longest legitimate delay: network rendering plus the longest CSS or JavaScript animation, with modest headroom. A 10-second timeout is an example, not a universal setting. A shorter poll interval (such as 0.1–0.2 seconds) observes animations sooner but executes more browser commands; the default interval is adequate for slower state changes.

A fixed time.sleep can be too short on a slow run and waste time on a fast one. Selenium describes explicit waits as a way to specify the exact condition at each point where an application may not yet be in the desired state. Use implicit waits cautiously: mixing a large implicit wait with explicit waits can make failure timing unpredictable.

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

Common failures and fixes

TimeoutException

  • Cause: the height never reaches the condition, the selector is wrong, or the timeout is shorter than the real animation or network delay.
  • Fix: log the measured value, verify the locator, inspect the element’s initial and final states, and set a timeout based on the documented UI behavior.

StaleElementReferenceException

  • Cause: a framework replaced the node after you captured it.
  • Fix: find the element inside the predicate, as in the examples, rather than retaining one WebElement.

Height is always zero

  • Cause: the element is not rendered, is collapsed with CSS, or you measured a wrapper whose content has not arrived.
  • Fix: wait for the relevant render condition, check the intended node in browser developer tools, and distinguish “present” from “visible.”

The wait passes during the animation

  • Cause: any difference satisfies a baseline comparison.
  • Fix: wait for a target with tolerance, a minimum threshold, or repeated equal readings when completion matters.

Intermittent results across browsers or viewport sizes

  • Cause: responsive CSS, font loading, fractional pixels, or different animation timing changes the final value.
  • Fix: define a semantic threshold or per-viewport target, wait for fonts/content when they affect layout, and retain a tolerance.
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 or PDF rather than an interactive Selenium test, ScreenshotNeo provides a single screenshot request and handles the browser session for you. It accepts consent banners before capture 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, and response headers identify the page verdict and whether it was billed.

Use the API examples in the ScreenshotNeo documentation:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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 exposes 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 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Practical checklist

  • Locate the element at the state where measurement begins.
  • Capture a baseline after the initiating action or state setup.
  • Measure rendered height consistently.
  • Use a one-pixel tolerance for fractional values.
  • Re-find nodes that front-end code can replace.
  • Choose a target or stability rule when animation completion matters.
  • Set timeout and polling to the actual UI behavior.
  • Let timeout failures reveal real synchronization or application defects.

Frequently Asked Questions

Can I wait for height to become exactly zero?

Yes. Use a target predicate with a tolerance, such as abs(height - 0) <= 1; exact floating-point equality is unnecessarily strict.

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

Does visibility_of_element_located wait for an expansion?

No. It verifies presence plus positive width and height. It does not compare the current height with an earlier or expected value.

Should I use CSS height or scrollHeight?

Use the property that matches the requirement. The examples measure rendered border-box height with getBoundingClientRect().height; scrollHeight instead represents the content height, including overflow.

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.