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

时间:2013-08-14 07:49:37

标签: java list selenium-webdriver

我的列表在每个部分下都有多个链接。每个部分都有相同的链接,我需要点击每个部分下的特定链接。我写了下面的代码,但是当它执行时,它会给我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("hourbank5@....com");
    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

12 个答案:

答案 0 :(得分:52)

什么是给出异常的行?

原因是因为您引用的元素已从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();
}

看看它是否可以帮到你!

答案 1 :(得分:5)

要处理它,我使用以下click方法。 这将尝试查找并单击该元素。如果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 :(得分:5)

每当遇到此问题时,只需在出现错误的行上方再次定义web元素, 例如:-

WebElement button = driver.findElement(By.xpath("xpath"));

button.click();

///在这里您进行更新或保存

//同样使用按钮WebElement单击

button.click();

解决方案:-

WebElement button = driver.findElement(By.xpath("xpath"));
button.click();

///在这里您进行更新或保存

//再次定义按钮元素,然后再使用

WebElement button1 = driver.findElement(By.xpath("xpath"));
button1.click();

//使得新元素将指向新DOM中的同一元素

答案 3 :(得分: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");
            }
        }
}
}

答案 4 :(得分:2)

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

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

您可以在if语句的末尾添加中断,这对我有用。

答案 5 :(得分: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

答案 6 :(得分:1)

当您找到要在其上单击的元素时,只需打破循环。 例如:

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

答案 7 :(得分: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代码实际上对我有用。我遇到了同样的陈旧元素错误。

答案 8 :(得分:1)

这可以在JS中较新版本的硒中完成(但所有支持 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)
  );

答案 9 :(得分: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. 我们在硒意义上不再存在的元素上调用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;
  }

答案 10 :(得分:0)

在我的情况下,我有一个页面是input type='date',该页面的引用是我在页面加载时获得的,但是当我尝试与之交互时,它显示了此exception,这相当由于Javascript操纵了我的控件,因此很有意义,因此它与文档分离,在JavaScript用控件执行其工作之后,我不得不re-get对其进行引用。 因此,这是我的代码在异常发生前的样子:

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#

答案 11 :(得分:-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;
        }

    }