Python Selenium: wait until an element is no longer stale?

10,804

Solution 1

A stale element is an element reference that you have stored that is no longer valid because the page, part of the page, or maybe just the element was refreshed. A simple example

element = driver.find_element_by_id("elementID")
# do something that refreshes the page
element.click()

Here element.click() will throw a stale element exception because the reference was stored before the refresh but used (clicked) after the refresh. In this case, that reference is no longer valid. Once a reference is stale, it never becomes "unstale"... that reference can never be used again. The only way to fix that is to store the reference again.

NOTE: Your code sample is not correct for .staleness_of(). It takes a web element reference, not a locator. You need an existing reference to wait for it to go stale. See the docs.

Now to solve the problem... you need to wait for the refresh to complete and then get a new reference.

element = driver.find_element_by_id("elementID")
# do something that refreshes the element
self.wait.until(EC.staleness_of(element))
element = self.wait.until(EC.visibility_of_element_located((By.ID, "elementID")))
# do something with element

Waiting for the element to become stale waits for the element reference to be lost, which means the element has changed/refreshed. Once we know the element has changed, we can now get a new reference to it. In this case, waiting for the element to become visible. We now have a new reference stored in our variable that we can use without stale element exceptions.

Solution 2

From the documentation:

Staleness of:

class staleness_of(object):
    """ Wait until an element is no longer attached to the DOM.
    element is the element to wait for.
    returns False if the element is still attached to the DOM, true otherwise.
    """
    def __init__(self, element):
        self.element = element

    def __call__(self, ignored):
        try:
            # Calling any method forces a staleness check
            self.element.is_enabled()
            return False
        except StaleElementReferenceException:
            return True

Element to be clickable:

class element_to_be_clickable(object):
    """ An Expectation for checking an element is visible and enabled such that
    you can click it."""
    def __init__(self, locator):
        self.locator = locator

    def __call__(self, driver):
        element = visibility_of_element_located(self.locator)(driver)
        if element and element.is_enabled():
            return element
        else:
            return False

As you can see, both use the same method is_enabled() in order to perform the check.

Share:
10,804

Related videos on Youtube

Haziq
Author by

Haziq

.

Updated on September 16, 2022

Comments

  • Haziq
    Haziq about 1 year

    I have a situation in which I want to wait until an element is no longer STALE i.e. until an elements gets connected to the DOM. Following wait options do not work somehow:

    self.wait.until(EC.visibility_of_element_located((By.ID, "elementID")))
    self.wait.until(EC.presence_of_element_located((By.ID, "elementID")))
    

    Its opposite wait function is present which waits until an element becomes stale, which is:

    self.wait.until(EC.staleness_of((By.ID, "elementID")))
    

    But I want it to wait until the element is NO LONGER STALE i.e. until it gets connected to the DOM. How can I achieve this functionality?

    EDIT: there is a solution here : here but I am looking for any other better approach if any.

    • nostradamus
      nostradamus about 6 years
      I'm confused. "Stale" means to me that the element was present before but now is gone. So, "no longer stale" does not make any sense to me?! I think it should work following the proposed answers (using presence_of_element_located). If it doesn't, something else is going wrong. Is the site using java or anything else that could dynamically change it? For testing purposes: Does it work if you build in a static extremely long wait (let's say, 120 seconds)?
  • Haziq
    Haziq about 6 years
    As mentioned in the question, this function does not help and still it raises StaleElementException.
  • Haziq
    Haziq about 6 years
    I have tried this function but it raises StaleElementException.
  • JeffC
    JeffC about 5 years
    That doesn't check for stale and OP didn't mention anything about waiting for expected text.
  • sangharsh
    sangharsh over 3 years
    you answer might not be correct for the raised issue, but it may give me a solution in my case!
  • undetected Selenium
    undetected Selenium over 3 years
    @sangharsh Ahh, that was a 3 year old answer and I was a new at Selenium then :)