David
David

Reputation: 68

Selenium c# - OpenQA.Selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document

Hopefully someone can help on this issue...

I keep receiving the above error message (see title) when interacting with cascading drop-downs. The only rudimentary fix I have successfully employed is "Thread.Sleep"... See code extract below:

Note I am passing the following parameters:

attribute: ID

attrval: e.g. ID123456 (ID of the drop-down)

parameter: Car (drop-down value we are wanting to select)

IWebElement element = findMyElement(attribute, attrval);
SelectElement selectElement = new SelectElement(element);
selectElement.SelectByText(parameter);
// dirty code - needs to be re-written
Thread.Sleep(500);
if (new SelectElement(findMyElement(attribute, attrval)).SelectedOption.Text.Equals(parameter))
{
    return "pass";
}

Note2: findMyElement is a custom method (here is an extract):

public static IWebElement findMyElement(string attribute, string attrval)
        {
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
            switch (attribute.ToLower())
            {
                case "id":
                    wait.Until(ExpectedConditions.ElementExists(By.Id(attrval)));
                    wait.Until(ExpectedConditions.ElementIsVisible(By.Id(attrval)));
                    return driver.FindElement(By.Id(attrval));

As I have stated in my code comments is there anyway I can avoid using Thread.Sleep as I am aware that this is not a recommended approach.

Thanks in advance :)

Upvotes: 3

Views: 4980

Answers (1)

John O.
John O.

Reputation: 708

It looks as if the first drop-down element gets unloaded from the DOM when the postback starts and reloads to the DOM once the postback finishes.

The StaleElementReferenceException is thrown when your code is trying to touch this element during the postback. The Sleep() call works by halting your code from touching this element for a period that coincidentally is long enough for the postback to complete.

The ideal solution is to identify when the postback has completed.

What predicate will only return true once the the postback is complete? Replace the Sleep() with a wait for that predicate to return true.

e.g. If the second drop-down only appears on the postback, replace Thread.Sleep(500); with:

Func<IWebDriver, bool> predicate = (x) =>
{
    try
    {
        IWebElement elementThatOnlyAppearsOnPostback = findMyElement(attribute, attrval);
        return true;
    }
    catch (NoSuchElementException)
    {
        return false;
    }
};
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); // or whatever timeout you want to set
wait.Until(predicate);

If the second drop-down is already there, but only becomes populated with options on the postback, switch the predicate to:

Func<IWebDriver, bool> predicate = (x) =>
{
    SelectElement secondDropDown = new SelectElement(findMyElement(attribute, attrval));
    return (secondDropDown.Options.Count > 0);
}

Upvotes: 1

Related Questions