Selenium wait until one of the two elements is present
Asked Answered
T

7

27

A lot of times I want the webdriver to wait for presence of one of the two elements. Normally this happens when I am expecting the page to be showing either element1 in some cases or element 2. Currently I am doing this sequentially using two waits, but it's inefficient since I need to wait 2 times. Is there any way to combine the two waits into one? In other words I want to wait until element1 or element2 is present.

try: 
  element = WebDriverWait(self.browser, 15).until(EC.presence_of_element_located((By.ID, "elem1")))
  element.click()
  return "elem1"
except: 
  print "failed to find elem1"

try: 
  element = WebDriverWait(self.browser, 5).until(EC.presence_of_element_located((By.ID, "elem2")))  
  return "elem2"    
except:
  print "sth wrong!"
  raise  Exception("Sth Wrong!") 

return "Should not get here"      
Turbinate answered 5/3, 2016 at 10:33 Comment(0)
W
27

You can do an OR

driverWait.until(ExpectedConditions.or(
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.something")),
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.anything"))));
Wadesworth answered 16/4, 2018 at 19:48 Comment(4)
tried it, gave me a syntax error at or, is it possible that there's no or methodGovernment
I had the same issue as @Government and this helped me #16462677Hydrargyrum
Please note that this syntax has been released in selenium 4Government
Use ExpectedConditions.any_of(...) or ExpectedConditions.all_of(...) instead .orSukhum
S
13

Not tested, but you can try something like

element = WebDriverWait(self.browser, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#elem1, #elem2")))

The comma in the selector is OR.

Salchunas answered 5/3, 2016 at 10:48 Comment(1)
This is the right answer! It's so simple and it works.Plunk
N
12

You could use a lambda

WebDriverWait(driver,15).until(
    lambda driver: driver.find_elements(By.ID,"Id1") or driver.find_elements(By.ID,"Id2"))
Noenoel answered 8/5, 2017 at 1:37 Comment(1)
Be careful if you ALSO use implicitly_wait(n). Each of the lambdas will take n secs before they return. So, if you had called implicitly_wait(15) and then tried WebDriverWait(driver,15).until(), then only the first lambda would be checked.Odele
R
3

You can use the any_of method which is implemented inside the ExpectedConditionssection to check if any of the elements are available and this is relevant to the logical expression - "OR".

Example:

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

WebDriverWait(driver, 40).until(EC.any_of(
        EC.visibility_of_element_located((By.ID, 'elem1')),
        EC.visibility_of_element_located((By.ID, 'elem2'))
    )
)
Remorseful answered 11/10, 2022 at 2:29 Comment(0)
S
0

From a suggested edit:

Selenium 4

You can do an OR

driverWait.until(ExpectedConditions.any_of(
  ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.something")),
  ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.anything"))));

or and AND

driverWait.until(ExpectedConditions.all_of(
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.something")), 
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.anything"))));
Schreibman answered 5/3, 2016 at 10:33 Comment(0)
A
0

This is an alternative solution while I was having problems with other solutions.

For example, if we only have 2 conditions, and 1st is never satisfied while the 2nd is already satisfied. Then the other solutions block until the end of wait_delay before return the result; while the following solution skip it:

WebDriverWait(driver, wait_delay).until(
  wait_for_any([
    EC.presence_of_element_located(locator)
    for locator in locators
]))

where

class wait_for_any:
    def __init__(self, methods):
        self.methods = methods

    def __call__(self, driver):
        for method in self.methods:
            try:
                if method(driver):
                    return True
            except Exception:
                pass
            
        return False
Airlee answered 28/11, 2021 at 22:53 Comment(0)
S
0

For those looking to await the appearance of any one of several elements without the inefficiency of waiting for each in sequence this method works. It waits for any of the elements defined by their IDs and promptly returns the first that appears. (you can use other selectors like XPath as well)

def get_any_elem(driver, timeout, *element_ids):
    def condition(d):
        for elem_id in element_ids:
            try:
                element = d.find_element(By.ID, elem_id)
                if element:
                    return element
            except NoSuchElementException:
                continue
        return False

    return WebDriverWait(driver, timeout).until(condition)
Supinator answered 30/10, 2023 at 23:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.