I have list which has multiple links under each section. Each section has same links I need to click a particular link under each section. I have written the below code but when it executes it gives me stale element reference: element is not attached to the page document
error.
This is my code:
public static void main(String[] args) throws InterruptedException
{
WebDriver driver = new ChromeDriver();
driver.navigate().to("url......");
driver.findElement(By.id("Login1_txtEmailID")).sendKeys("[email protected]");
driver.findElement(By.id("Login1_txtPassword")).sendKeys("Testing1*");
driver.findElement(By.id("Login1_btnLogin")).click();
List<WebElement> LeftNavLinks=driver.findElements(By.xpath("//*[@id='sliding-navigation']//a"));
Thread.sleep(1000);
String ben="Benefit Status";
String[] linkTexts = new String[LeftNavLinks.size()];
int i = 0;
for (WebElement e : LeftNavLinks)
{
linkTexts[i] = e.getText();
System.out.print(i+" " + linkTexts[i]+"\n");
if(linkTexts[i].equals(ben))
{
String BenefitStatLi="//*[@id='sliding-navigation']/li[%s]/a";
System.out.print(i+" " + linkTexts[i]+"\n");
driver.findElement(By.xpath(String.format(BenefitStatLi,i))).click();
driver.findElement(By.xpath("//* [@id='divContentHolder']/div[1]/a[1]")).click();
}
i++;
}
}
}
This is the HTML structure is as below
<div id="ucAdminMenu_divMenu">
<ul id="sliding-navigation">
<li class="sliding-element">
<a href=" ">Claims Status</a>
</li>
<li class="sliding-element">
<a href=" ">Eligibility Status</a>
</li>
<li class="sliding-element">
<h3>Section-1</h3>
</li>
<li class="sliding-element">
<a href=" ">Forms and Documents</a>
</li>
<li class="sliding-element">
<a href=" HourBank.aspx?id=002">Hour Bank</a>
</li>
<li class="sliding-element">
<h3>Section-2</h3>
</li>
<li class="sliding-element">
<a href=" ">Benefit Status</a>
</li>
<li class="sliding-element">
<a href=" ">Forms and Documents</a>
</li>
<li class="sliding-element">
<h3>Section-3</h3>
</li>
<li class="sliding-element">
<a href=" ">Forms and Documents</a>
</li>
<li class="sliding-element">
<h3>Testing Fund</h3>
</li>
<li class="sliding-element">
<a href=" ">Benefit Status</a>
</li>
<li class="sliding-element">
<a href=" ">Order ID Card</a>
</li>
</ul>
</div>
The Error Trace is:
Exception in thread "main"
org.openqa.selenium.StaleElementReferenceException: stale element
reference: element is not attached to the page document
This question is related to
java
list
selenium-webdriver
use this code to wait till the element is attached:
boolean breakIt = true;
while (true) {
breakIt = true;
try {
// write your code here
} catch (Exception e) {
if (e.getMessage().contains("element is not attached")) {
breakIt = false;
}
}
if (breakIt) {
break;
}
}
Use this code:
public class LinkTest
{
public static void main(String[] args)
{
WebDriver driver = new FirefoxDriver();
driver.navigate().to("file:///C:/Users/vkiran/Desktop/xyz.html");
List<WebElement> alllinks =driver.findElements(By.xpath("//*[@id='sliding-navigation']//a"));
String a[]=new String[alllinks.size()];
for(int i=0;i<alllinks.size();i++)
{
a[i]=alllinks.get(i).getText();
if(a[i].startsWith("B"))
{
System.out.println("clicking on this link::"+driver.findElement(By.linkText(a[i])).getText());
driver.findElement(By.linkText(a[i])).click();
}
else
{
System.out.println("does not starts with B so not clicking");
}
}
}
}
According to @Abhishek Singh's you need to understand the problem:
What is the line which gives exception ?? The reason for this is because the element to which you have referred is removed from the DOM structure
and you can not refer to it anymore (imagine what element's ID has changed).
Follow the code:
class TogglingPage {
@FindBy(...)
private WebElement btnTurnOff;
@FindBy(...)
private WebElement btnTurnOn;
TogglingPage turnOff() {
this.btnTurnOff.isDisplayed();
this.btnTurnOff.click(); // when clicked, button should swap into btnTurnOn
this.btnTurnOn.isDisplayed();
this.btnTurnOn.click(); // when clicked, button should swap into btnTurnOff
this.btnTurnOff.isDisplayed(); // throws an exception
return new TogglingPage();
}
}
Now, let us wonder why?
btnTurnOff
was found by a driver - okbtnTurnOff
was replaced by btnTurnOn
- okbtnTurnOn
was found by a driver. - okbtnTurnOn
was replaced by btnTurnOff
- okthis.btnTurnOff.isDisplayed();
on the element which does not exist anymore in Selenium sense - you can see it, it works perfectly, but it is a different instance of the same button. Possible fix:
TogglingPage turnOff() {
this.btnTurnOff.isDisplayed();
this.btnTurnOff.click();
TogglingPage newPage = new TogglingPage();
newPage.btnTurnOn.isDisplayed();
newPage.btnTurnOn.click();
TogglingPage newerPage = new TogglingPage();
newerPage.btnTurnOff.isDisplayed(); // ok
return newerPage;
}
Whenever you face this issue, just define the web element once again above the line in which you are getting an Error.
WebElement button = driver.findElement(By.xpath("xpath"));
button.click();
//here you do something like update or save
//then you try to use the button WebElement again to click
button.click();
Since the DOM has changed e.g. through the update action, you are receiving a StaleElementReference
Error.
WebElement button = driver.findElement(By.xpath("xpath"));
button.click();
//here you do something like update or save
//then you define the button element again before you use it
WebElement button1 = driver.findElement(By.xpath("xpath"));
//that new element will point to the same element in the new DOM
button1.click();
What is the line which gives exception ??
The reason for this is because the element to which you have referred is removed from the DOM structure
I was facing the same problem while working with IEDriver. The reason was because javascript loaded the element one more time after i have referred so my date reference pointed to an unexisting object even if it was right their on UI. I used the following workaround.
try {
WebElement date = driver.findElement(By.linkText(Utility.getSheetData(path, 7, 1, 2)));
date.click();
}
catch(org.openqa.selenium.StaleElementReferenceException ex)
{
WebElement date = driver.findElement(By.linkText(Utility.getSheetData(path, 7, 1, 2)));
date.click();
}
See if the same can help you !
Just break the loop when you find the element you want to click on it. for example:
List<WebElement> buttons = getButtonElements();
for (WebElement b : buttons) {
if (b.getText().equals("Next"){
b.click();
break;
}
try {
WebElement button = driver.findElement(By.xpath("xpath"));
button.click();
}
catch(org.openqa.selenium.StaleElementReferenceException ex)
{
WebElement button = driver.findElement(By.xpath("xpath"));
button.click();
}
This try/catch code actually worked for me. I got the same stale element error.
The thing here is that you're using a for loop outside of your conditional statement.
After the conditions in your IF statement are met you probably navigate to another page, thus when the for loop attempts to iterate once more, you get the stale element error because you're on a different page.
You can add a break at the end of your if statement, this worked for me.
This errors have two common causes: The element has been deleted entirely, or the element is no longer attached to the DOM.
If you already checked if it is not your case, you could be facing the same problem as me.
The element in the DOM is not found because your page is not entirely loaded when Selenium is searching for the element. To solve that, you can put an explicit wait condition that tells Selenium to wait until the element is available to be clicked on.
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID, 'someid')))
This could be done in newer versions of selenium in JS( but all supporting stalenessOf will work):
const { until } = require('selenium-webdriver');
driver.wait(
until.stalenessOf(
driver.findElement(
By.css(SQLQueriesByPhpMyAdminSelectors.sqlQueryArea)
)
),
5 * 1000
)
.then( driver.findElement(By.css(SQLQueriesByPhpMyAdminSelectors.sqlQueryArea))
.sendKeys(sqlString)
);
To handle it, I use the following click method. This will attempt to find and click the element. If the DOM changes between the find and click, it will try again. The idea is that if it failed and I try again immediately the second attempt will succeed. If the DOM changes are very rapid then this will not work.
public boolean retryingFindClick(By by) {
boolean result = false;
int attempts = 0;
while(attempts < 2) {
try {
driver.findElement(by).click();
result = true;
break;
} catch(StaleElementException e) {
}
attempts++;
}
return result;
}
In my case, I had a page where it was an input type='date'
whose reference I had got on page load, but When I tried to interact with it, it showed this exception
and that was quite meaningful as Javascript
had manipulated my control hence it was detached from the document and I had to re-get
its reference after the javascript had performed its job with the control.
So, this is how my code looked before the exception:
if (elemDate != null)
{
elemDate.Clear();
elemDate.SendKeys(model.Age);
}
Code after the exception was raised:
int tries = 0;
do
{
try
{
tries++;
if (elemDate != null)
{
// these lines were causing the exception so I had break after these are successfully executed because if they are executed that means the control was found and attached to the document and we have taken the reference of it again.
elemDate.Clear();
elemDate.SendKeys(model.Age);
break;
}
}
catch (StaleElementReferenceException)
{
System.Threading.Thread.Sleep(10); // put minor fake delay so Javascript on page does its actions with controls
elemDate = driver.FindElement(By.Id(dateId));
}
} while (tries < 3); // Try it three times.
So, Now you can perform further actions with your code or you can quit the driver if it was unsuccessful in getting the control to work.
if(tries > 2)
{
// element was not found, find out what is causing the control detachment.
// driver.Quit();
return;
}
// Hurray!! Control was attached and actions were performed.
// Do something with it...
Something that I have learnt so far is, catching exceptions to know about successful code execution is not a good idea, But, I had to do it and I found this
work-around
to be working well in this case.
PS: After writing all this, I just noticed the tags that this thread was for java
. This code sample is just for demonstration purpose, It might help people who have issue in C#
language. Or it can be easily translated to java
as it doesn't have much C#
specific code.
Source: Stackoverflow.com