Browser automation can finish navigation before a JavaScript-driven interface exposes the next usable control. Selenium explicit waits synchronize each test action with the application state that action requires, avoiding fixed sleeps that are either too short or longer than necessary.
Python's WebDriverWait polls a condition until it returns a value that is not false or raises TimeoutException at the deadline. Locator-based expected conditions such as element_to_be_clickable() return the matching WebElement, so the next action can use the element found by the successful poll.
The local page begins with a disabled button and changes its status only after JavaScript enables the control. A second status change after the click separates button readiness from application success; keep implicit waits at their default zero unless a project requires them because Selenium warns that mixing implicit and explicit waits can produce unpredictable timeout lengths.
Related: How to click a button with Selenium
Related: How to run a headless browser in Selenium
<!doctype html> <html lang="en"> <meta charset="utf-8"> <title>Selenium explicit wait demo</title> <button id="submit-order" disabled>Submit order</button> <p id="status">Loading order</p> <script> const button = document.querySelector("#submit-order"); const status = document.querySelector("#status"); setTimeout(() => { button.disabled = false; status.textContent = "Ready to submit"; }, 700); button.addEventListener("click", () => { status.textContent = "Submitted"; }); </script> </html>
from pathlib import Path import shutil from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait page_url = (Path(__file__).parent / "explicit-wait-demo.html").resolve().as_uri()
options = Options() options.add_argument("--headless=new") options.add_argument("--window-size=1280,720") browser_path = shutil.which("google-chrome") or shutil.which("chromium") if browser_path: options.binary_location = browser_path driver_path = shutil.which("chromedriver") service = Service(driver_path) if driver_path else Service() driver = webdriver.Chrome(service=service, options=options)
An installed ChromeDriver is used when it is on PATH. Otherwise, the empty Service() lets Selenium Manager resolve a compatible driver on supported systems.
Related: How to configure ChromeDriver for Selenium
try: driver.get(page_url) wait = WebDriverWait(driver, 5) wait.until( EC.text_to_be_present_in_element((By.ID, "status"), "Ready to submit"), "order status did not become ready", ) button = wait.until( EC.element_to_be_clickable((By.ID, "submit-order")), "submit button did not become clickable", ) ready_status = driver.find_element(By.ID, "status").text button_text = button.text button.click()
Custom messages make a timeout identify the state that never appeared instead of reporting an empty wait failure.
Related: How to troubleshoot Selenium timeout errors
from pathlib import Path import shutil from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait page_url = (Path(__file__).parent / "explicit-wait-demo.html").resolve().as_uri() options = Options() options.add_argument("--headless=new") options.add_argument("--window-size=1280,720") browser_path = shutil.which("google-chrome") or shutil.which("chromium") if browser_path: options.binary_location = browser_path driver_path = shutil.which("chromedriver") service = Service(driver_path) if driver_path else Service() driver = webdriver.Chrome(service=service, options=options) try: driver.get(page_url) wait = WebDriverWait(driver, 5) wait.until( EC.text_to_be_present_in_element((By.ID, "status"), "Ready to submit"), "order status did not become ready", ) button = wait.until( EC.element_to_be_clickable((By.ID, "submit-order")), "submit button did not become clickable", ) ready_status = driver.find_element(By.ID, "status").text button_text = button.text button.click() wait.until( EC.text_to_be_present_in_element((By.ID, "status"), "Submitted"), "order status did not change to Submitted", ) result_status = driver.find_element(By.ID, "status").text print(f"ready: {ready_status}") print(f"button: {button_text}") print(f"result: {result_status}") finally: driver.quit()
The final wait observes the application response rather than treating a returned click() call as proof that submission finished.
$ python3 selenium-explicit-waits-use.py ready: Ready to submit button: Submit order result: Submitted