Modern web applications heavily rely on JavaScript for dynamic content loading. Elements might not be immediately available when the page loads, causing automation tests to fail if they interact with elements before they appear.
To handle this, waiting strategies are essential. In this blog, we will cover:
✅ Why waiting for elements is necessary
✅ Different types of waits in Selenium & Playwright
✅ Best practices for handling dynamic elements
✅ Code examples (Selenium & Playwright)
✅ Tools & GitHub repositories for advanced handling
🚀 Why Do We Need to Wait for Elements?
A test script may fail due to:
🔹 Asynchronous JavaScript execution – Elements load after the page
🔹 API response delays – Backend calls affect UI rendering
🔹 Animations & transitions – Buttons or modals may take time to appear
🔹 Lazy loading – Some elements load when scrolled into view
If you try to interact with an element before it’s available, Selenium or Playwright throws errors like:
selenium.common.exceptions.NoSuchElementException
playwright._impl._errors.TimeoutError
The solution? Use waits to pause execution until elements are ready.
⏳ Types of Waits in Automation Testing
There are three main types of waits used in automation frameworks:
Wait Type | Description | Use Case |
---|---|---|
Implicit Wait | Waits for a defined time before throwing an error | Applied globally, but may cause performance issues |
Explicit Wait | Waits until a specific condition is met before proceeding | More efficient than implicit waits |
Fluent Wait | Similar to explicit wait but allows polling intervals | Useful for handling dynamically appearing elements |
🛠 1️⃣ Using Implicit Waits (Selenium & Playwright)
🔹 Selenium Example
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # Waits up to 10 seconds for elements
driver.get("https://example.com")
element = driver.find_element("id", "dynamicElement") # No explicit wait needed
driver.quit()
✅ Pros: Simple to implement
❌ Cons: Not efficient for waiting on specific elements
🔹 Playwright Example
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_default_timeout(10000) # Set implicit timeout
page.goto("https://example.com")
page.locator("#dynamicElement").click()
browser.close()
🎯 2️⃣ Using Explicit Waits (Better for Performance & Reliability)
🔹 Selenium Example
# Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com")
# Wait for element to be visible
wait = WebDriverWait(driver, 10)
element = wait.until(EC.visibility_of_element_located((By.ID, "dynamicElement")))
element.click()
driver.quit()
✅ Pros: Optimized, waits for specific conditions
❌ Cons: Requires writing additional conditions
🔹 Playwright Example (Recommended)
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
# Wait for element to be visible before clicking
page.wait_for_selector("#dynamicElement", state="visible")
page.locator("#dynamicElement").click()
browser.close()
⏳ 3️⃣ Fluent Waits (Polling Intervals for Unstable Elements)
🔹 Selenium Example
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
wait = WebDriverWait(driver, 15, poll_frequency=0.5) # Poll every 0.5s
element = wait.until(EC.element_to_be_clickable((By.ID, "dynamicElement")))
element.click()
✅ Best For: Elements appearing at unpredictable intervals
📌 Best Practices for Waiting Strategies
✅ Use explicit waits instead of implicit waits for efficiency
✅ Avoid sleep()
functions, as they waste execution time
✅ Use polling intervals for more responsive tests
✅ Use Playwright’s wait_for_selector
instead of timeouts
✅ Use Selenium’s expected_conditions
for better stability
🚀 Advanced Handling: Waiting for AJAX & JavaScript Execution
Sometimes elements appear after an API request. You can wait for the network request to complete before interacting.
🔹 Playwright: Wait for API Response
with page.expect_response("**/api/getData") as response_info:
page.locator("#submit").click()
response = response_info.value
print(response.status)
🔹 Selenium: Wait for JavaScript Execution
WebDriverWait(driver, 10).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)
✅ Ensures all dynamic content has loaded before proceeding
📚 Further Reading & Resources
🔗 Popular Resources
🎯 Underrated Resources
📌 GitHub Repositories for Handling Dynamic Elements
🤖 AI-Powered Tools for Improving Web Testing
- Testim – AI-based smart locators for handling dynamic elements
- Applitools – AI-powered visual testing for web apps
- LambdaTest – Cloud-based Selenium & Playwright testing
🎯 Conclusion
Dynamic elements can cause flaky tests if not handled properly. Using explicit waits, fluent waits, and JavaScript execution waits, we can ensure our automation scripts interact only with fully loaded elements.
Would you like to explore parallel execution strategies for handling dynamic tests next? 🚀