Selenium - stale element reference: element is not attached to the page
Asked Answered
X

6

15

I am trying to understand why it gives an error while I am trying to display items that took from websites. I am also using google chrome for a browser.

chromeDriver.Navigate().GoToUrl("somewebsites");

chromeDriver.FindElement(By.Id("something.link.text")).Click();
chromeDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5));

And this other parts of my code

var item = chromeDriver.FindElementsByXPath("//*[starts-with(@id,\"g_1_\")]/td[3]/span");
foreach (var value in item)
{
    Console.WriteLine(value.Text); 
}

Whenever I use "chromeDriver.FindElement(By.Id("something.link.text")).Click();", it gives an error. I can't display the data that I pulled.

In the error message, It says "OpenQA.Selenium.StaleElementReferenceException: 'stale element reference: element is not attached to the page document". I checked the other posts, but I did not figure it out. How can I solve it?

As I read, selenium gives much faster response than website opening time, so it gives the error. Also, rarely, it gives that selenium cannot be located the element. Any suggestion?

EDIT: If I remove ".text" from display function, It displays "OpenQA.Selenium.Remote.RemoteWebElement". Does the problems with strings ?

EDIT2: I can hold data that I took from websites as like this:

[i]="System.Collections.ObjectModel.ReadOnlyCollection`1[OpenQA.Selenium.IWebElement]" For this, I wrote something like this:

string[] test= new string[100];

for (int i = 0; i < 100; i++)
{
     kaan[i] = driver.FindElements(By.XPath("//*[starts-with(@id,\"g_1_\")]/td[3]/span")).ToString();
}

How can I convert to the text

Xylograph answered 9/7, 2017 at 23:31 Comment(2)
Possible duplicate of StaleElementReference Exception in PageFactoryDarn
Please add the html.Lattonia
A
18

I haven't worked in c# but have worked on java/selenium. But,I can give you the idea to overcome staleness.

Generally we will be getting the Stale Exception if the element attributes or something is changed after initiating the webelement. For example, in some cases if user tries to click on the same element on the same page but after page refresh, gets staleelement exception.

To overcome this, we can create the fresh webelement in case if the page is changed or refreshed. Below code can give you some idea.(It's in java but the concept will be same)

Example:

 webElement element = driver.findElement(by.xpath("//*[@id='StackOverflow']"));
 element.click();
 //page is refreshed
 element.click();//This will obviously throw stale exception

To overcome this, we can store the xpath in some string and use it create a fresh webelement as we go.

String xpath = "//*[@id='StackOverflow']";
driver.findElement(by.xpath(xpath)).click();
//page has been refreshed. Now create a new element and work on it
driver.findElement(by.xpath(xpath)).click();   //This works

In this case, we are collecting a group of webelements and iterating to get the text. But it seems there is some changes in the webelement after collecting the webelements and gettext throws staleness. We can use a loop and create the element on the go and get text.

for(int i = 0; i<5; i++)
{
   String value = driver.findElement(by.xpath("//.....["+i+"]")).getText);
   System.out.println(value);
}

Hope this helps you. Thanks.

Arrowy answered 10/7, 2017 at 3:3 Comment(4)
it gives an idea, i will try it. Thank youPinette
This will not work since Selenium caches the result of a selector. Using the same selector will give the same result, therefore returning the initial stale object.Guideline
basic selenium never catches. In the case if we use page factory then might be.Arrowy
@tocqueville Is there any way to handle caches of a selector result? Selenium return element which already changed at html( I insert time.sleep(10) which is enough to complete the element change). And I don't allocate this element into some variable, but use driver.find_element_by_xpath() at pythonClergyman
C
3

This type of error is usually caused by an updated DOM. I would suggest that you on all pages that contain javascript/ajax calls actually wait for those calls to finish before interacting with the site. I generally perform this wait just after the page has loaded, and when performing some kind of action that triggers javascript/ajax calls and/or updates to the DOM. In practice, this means running the below function after the page has loaded (or has been interacted with), and after that find the element you want to interact with.

public void WaitForJqueryAjax() {
        int delay = MaxdelaySeconds;
        while(delay > 0) {
            Thread.Sleep(1000);
            var jquery = (bool)(this.driver as IJavaScriptExecutor)
                .ExecuteScript("return window.jQuery == undefined");
            if(jquery) {
                break;
            }
            var ajaxIsComplete = (bool)(this.driver as IJavaScriptExecutor)
                .ExecuteScript("return window.jQuery.active == 0");
            if(ajaxIsComplete) {
                break;
            }
            delay--;
        }
    }
Cereal answered 11/7, 2017 at 11:34 Comment(1)
I use this trick too. No idea how to do it if the Ajax calls aren't going through jQuery, though.Cushing
P
1

I have been to a similar situation.Here the main problem is page needs time so I added time.sleep(sec) and it worked fine.

Pleasantry answered 27/2, 2020 at 2:55 Comment(1)
Generally, relying on sleep() in this way provides, at best, an inefficient workaround to the core problem. Best case, it potentially waits too long, thus unnecessarily slowing down performance. Worst case, it fires too quickly, resulting in the same problem. Ideally, we’d tie into a specific event to fire this upon availability. Do you have any insight into what events might be appropriate here, instead of simply using sleep() to delay processing?Mizell
V
0

I had initially this problem but I got it resolved by few tricks

  • Wait for some parent element to be visible in the page (may be a parent container div)
  • Wait of any ajax (xhr) request to complete around the element you are trying to use
  • (this is the trick) do not store any element as instance property value, always fetch on request (this will help you from staleelement). Try expression in c# 6, example private IWebElement _parentContainer => _driver.FindElement(...)

Also, you can create abstract basecontrol and some extension methods to for IWebElement and do some basic check operations while finding elements

Vespasian answered 10/7, 2017 at 10:6 Comment(0)
N
0

A solution that worked for me was using recursion to create a fresh webelement after the page has been refreshed. This idea was mentioned by santhosh kumar in this thread. This is the code snippet.

def preventStaleElement(Bool):
    while(Bool):
        # During the recursion, a new element will be created since the try 
        # block is run once more
        try:
            element = driver.find_element(By.CLASS_NAME, "mouseover_load")
            action = ActionChains(driver).move_to_element(element)
            action.perform()
        except StalElementReferenceException:
            preventStaleElement(True)



   
Neeley answered 22/2, 2022 at 3:56 Comment(0)
A
0

General advise: This type of error occurs when the browser has already navigated to the next page before any type of action is performed on the element, hence the element can no longer be referenced.

To know which element is causing the issue, you can print the stack trace of the error to identify the exact element which is causing the issue.

Possible fix is to check if the element being referenced is present/visible before a user event or introduce a time delay.

Adriene answered 25/1, 2023 at 12:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.