陈旧元素引用:元素未附加到页面文档

问题描述 投票:43回答:11

我有列表,每个部分下有多个链接。每个部分都有相同的链接,我需要点击每个部分下的特定链接。我写了下面的代码但是当它执行时它给了我stale element reference: element is not attached to the page document错误。

这是我的代码:

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++;
    }
}

}

这是HTML结构如下

<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>

错误跟踪是:

    Exception in thread "main" 
org.openqa.selenium.StaleElementReferenceException: stale element 
reference: element is not attached to the page document
java list selenium-webdriver
11个回答
58
投票

什么是给出异常的线?

原因是因为您引用的元素已从DOM结构中删除

我在使用IEDriver时遇到了同样的问题。原因是因为javascript在我引用之后再次加载元素所以我的日期引用指向一个未存在的对象,即使它在UI上是正确的。我使用了以下解决方法。

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();
}

看看是否可以帮助你!


0
投票

根据@Abhishek Singh的说法,你需要了解这个问题:

什么是给出异常的线?原因是因为您引用的元素已从DOM结构中删除

而且你不能再引用它了(想象一下,哪个元素的ID已经改变了)。

遵循以下代码:

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();
  }
}

现在,让我们想知道为什么?

  1. btnTurnOff被司机发现 - 好的
  2. btnTurnOffbtnTurnOn取代 - 好的
  3. btnTurnOn被一名司机发现。 - 好
  4. btnTurnOnbtnTurnOff取代 - 好的
  5. 我们在selenium意义上的元素上调用this.btnTurnOff.isDisplayed(); - 你可以看到它,它完美地工作,但它是同一个按钮的不同实例。

可能的修复:

  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;
  }

0
投票

在我的情况下,我有一个页面,它是一个input type='date',我的页面加载参考,但当我尝试与它进行交互时,它显示了这个exception,这是非常有意义的,因为Javascript操纵我的控制,因此它是分离的从文档和我必须re-get它的参考后javascript已执行其控件的工作。所以,这就是我的代码在异常之前的样子:

if (elemDate != null)
{ 
    elemDate.Clear(); 
    elemDate.SendKeys(model.Age);
}

引发异常后的代码:

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.

因此,现在您可以使用代码执行进一步的操作,或者如果驱动程序未能成功运行,则可以退出驱动程序。

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...

到目前为止我学到的东西是,捕获异常知道成功执行代码并不是一个好主意,但是,我必须这样做,我发现这个work-around在这种情况下运行良好。

PS:写完这一切之后,我刚刚注意到这个帖子用于java的标签。此代码示例仅用于演示目的,它可能有助于在C#语言中出现问题的人。或者它可以很容易地转换为java,因为它没有太多的C#特定代码。


-6
投票

使用此代码等待元素连接:

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;
        }

    }

10
投票

无论何时遇到此问题,只需再次在获取错误的行上方定义Web元素。

Example:

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();

由于DOM已经改变,例如通过更新操作,您收到StaleElementReference错误。

Solution:

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();


5
投票

要处理它,我使用以下单击方法。这将尝试查找并单击该元素。如果DOM在查找和单击之间发生变化,它将再次尝试。这个想法是,如果它失败了,我立即再次尝试,第二次尝试将成功。如果DOM变化非常快,那么这将不起作用。

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;
}

2
投票

使用此代码:

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");
            }
        }
}
}

2
投票

这里的事情是你在条件语句之外使用for循环。

在满足IF语句中的条件之后,您可能会导航到另一个页面,因此当for循环尝试再次迭代时,您会得到陈旧元素错误,因为您位于不同的页面上。

你可以在if语句的末尾添加一个break,这对我有用。


2
投票

此错误有两个常见原因:元素已完全删除,或元素不再附加到DOM。

如果您已经检查过不是您的情况,那么您可能会遇到与我相同的问题。

找不到DOM中的元素,因为当Selenium搜索元素时,您的页面未完全加载。要解决这个问题,您可以设置一个明确的等待条件,告诉Selenium等待元素可用于单击。

from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID, 'someid')))

见:https://selenium-python.readthedocs.io/waits.html


1
投票

当你找到想要点击它的元素时,只需打破循环。例如:

  List<WebElement> buttons = getButtonElements();
    for (WebElement b : buttons) {
        if (b.getText().equals("Next"){
            b.click();
            break;
        }

1
投票
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();
}

这个try / catch代码实际上对我有用。我得到了相同的陈旧元素错误。


1
投票

这可以在JS的较新版本的selenium中完成(但是所有支持的stalenessOf都可以):

 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)
  );
© www.soinside.com 2019 - 2024. All rights reserved.