Selenium - wait until element is present, visible and interactable
Asked Answered
L

7

130

I have a Selenium script (Python) that clicks a reply button to make the class anonemail appear. The time it takes for the class anonemail to appear varies. Because of that I have to use sleep until the element has appeared.

I want to wait until the class has appeared instead of using sleep. I have heard about wait commands, but I don't know how to use them.

This is what I have thus far:

browser.find_element_by_css_selector(".reply-button").click()
sleep(5)
email=browser.find_element_by_css_selector(".anonemail").get_attribute("value")
Lacilacie answered 1/12, 2019 at 21:36 Comment(3)
Do any of the answers address waiting for the element to be interactable? It can be present and clickable and still not ready for you to send keys to it.Fulk
Try: i.stack.imgur.com/XaPtT.pngCorelli
anonemail = anonymous + email. Aren't there naming conventions for class names?Hygeia
I
176

As per the best practices:

  • If your use case is to validate the presence of any element, you need to induce WebDriverWait setting the expected_conditions as presence_of_element_located() which is the expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible. So the effective line of code will be:

    WebDriverWait(browser, 20).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".reply-button"))).click()
    
  • If your use case is to extract any attribute of any element you need to induce WebDriverWait setting the expected_conditions as visibility_of_element_located(locator) which is an expectation for checking that an element is present on the DOM of a page and visible. Visibility means that the element is not only displayed, but it also has a height and width that is greater than 0. So in your use case, effectively the line of code will be:

    email = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "element_css"))).get_attribute("value")
    
  • If your use case is to invoke click() on any element you need to induce WebDriverWait setting the expected_conditions as element_to_be_clickable() which is an expectation for checking an element is visible and enabled such that you can click it. So in your use case, effectively the line of code will be:

    WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".reply-button"))).click()
    

References

You can find a couple of detailed discussion in:

Imputable answered 1/12, 2019 at 21:51 Comment(1)
Thanks a lot! This doesn't show element not found error. However, it immediately closes the browser window even if an explicit wait exists. Do you know why?Jaenicke
P
57

After clicking the Reply button, use .visibility_of_element_located like below:

browser.find_element_by_css_selector(".reply-button").click()

# Wait for initialize, in seconds
wait = WebDriverWait(browser, 10)

email = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.anonemail'))).get_attribute("value")
print(email)

Following import:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

Waits documentation

Pentatomic answered 2/12, 2019 at 3:33 Comment(0)
R
17

You can use waits. Check for more information in Selenium waits.

In the example below we are waiting 10 seconds for the element to be visible, using the function visibility_of_element_located.

driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.ID, "myDynamicElement"))
    )
finally:
    driver.quit()
Rife answered 1/12, 2019 at 21:44 Comment(0)
F
12
  • WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, ".reply-button"))):

    • Hey driver, wait (0-5 seconds), when you see .reply-button is presence, return that button to me!
      • presence is present on the DOM. Hidden element is good enough.
  • WebDriverWait(driver, 5).until( EC.visibility_of_element_located((By.CSS_SELECTOR, ".reply-button”)))):

    • Hey driver, wait (0-5 seconds), when you see .reply-button is visibility, return that button to me!
      • visibility is present and visible on the DOM. Hidden element is not good enough!
  • WebDriverWait(driver, 5).until( EC.element_to_be_clickable((By.CSS_SELECTOR, ".reply-button”)))):

    • Hey driver, wait (0-5 seconds), when you see .reply-button is clickable, return that button to me!
      • clickable is present and visible and clickable on the DOM. Hidden element or non-clickable is not good enough!

Other available conditions

Fiona answered 23/7, 2022 at 1:38 Comment(1)
I really like finding this kind of answers in SO: I think they help you learn fishing instead of only giving you a fish. Kudos!Taunton
C
8

You can use implicitly_wait:

from selenium import webdriver

driver = webdriver.Firefox()
driver.implicitly_wait(15)
driver.get("http://url")
driver.find_element_by_id("id_of_element").click()

It waits until element is loaded.

In your case the implementation would be,

browser.implicitly_wait(10)
browser.find_element_by_css_selector(".reply-button").click()
email = browser.find_element_by_css_selector(".anonemail").get_attribute("value")
Carlin answered 1/12, 2019 at 21:47 Comment(0)
M
2

I also had a similar problem to yours. I tried using implicit_wait() and WebDriverWait, but they did not work.

So I solved setting the implicit_wait(10) in the web driver instance and using this snippet to click on the button:

element = driver.find_elements_by_xpath("//button[contains(string(), 'Log In')]")[0]
driver.execute_script("arguments[0].click();", element)
Mulberry answered 5/5, 2022 at 12:38 Comment(0)
P
0

Modern Python Selenium frameworks have smart-waiting already included before performing actions such as clicking. For example, here's a SeleniumBase script that tests a demo e-commerce website:

from seleniumbase import SB

with SB() as sb:
    sb.open("https://www.saucedemo.com/")
    sb.type("input#user-name", "standard_user")
    sb.type("input#password", "secret_sauce")
    sb.click("input#login-button")
    sb.click('button[id="add-to-cart-test.allthethings()-t-shirt-(red)"]')
    sb.click("div#shopping_cart_container a")
    sb.click("button#checkout")
    sb.type("input#first-name", "SeleniumBase")
    sb.type("input#last-name", "Rocks")
    sb.type("input#postal-code", "01720")
    sb.click("input#continue")
    sb.click("button#finish")
    sb.assert_exact_text("Thank you for your order!", "h2")

In addition, there are simple methods for waiting for certain things if you still want to use those. Eg:

sb.wait_for_element_present(selector)
sb.wait_for_element_visible(selector)
sb.wait_for_element_clickable(selector)

(The selector is auto-detected, which means you no longer have to specify CSS vs XPath.)


The Selenium "best practices" now recommend using frameworks that include smart-waiting for you:

That means NOT USING a long line like this anymore:

WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button"))).click()

and instead, DO USE a framework where you can do something like this:

sb.click("button")

It's much cleaner, just as reliable, and displays better error messages if the element you're looking for never shows up within the default timeout.

Frameworks, such as SeleniumBase, are listed on the Selenium Ecosystem page: https://www.selenium.dev/ecosystem/#frameworks

Philis answered 23/2 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.