154

我的列表在每个部分下都有多个链接。每个部分都有相同的链接,我需要单击每个部分下的特定链接。我已经编写了下面的代码,但是当它执行时它给了我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
4

13 回答 13

109

给出异常的行是什么?

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

看看能不能帮到你!

于 2013-08-14T07:58:00.163 回答
76

每当您遇到此问题时,只需在出现错误的行上方再次定义 Web 元素。

例子:

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错误。

解决方案:

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

于 2019-01-17T06:40:42.800 回答
18

此错误有两个常见原因:元素已被完全删除,或者元素不再附加到 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

在 Java 中

import org.openqa.selenium.support.ui.ExpectedConditions;

WebDriverWait wait = new WebDriverWait(driver, 10);
element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));

见:https ://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html

于 2018-09-21T18:29:26.090 回答
8

为了处理它,我使用以下点击方法。这将尝试查找并单击该元素。如果 DOM 在 find 和 click 之间发生变化,它将重试。这个想法是,如果它失败了,我会立即再试一次,第二次尝试就会成功。如果 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;
}
于 2018-01-09T04:05:18.903 回答
4

这里的问题是您在条件语句之外使用了 for 循环。

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

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

于 2016-10-16T18:50:06.653 回答
3

当您找到要单击的元素时,只需中断循环即可。例如:

  List<WebElement> buttons = getButtonElements();
    for (WebElement b : buttons) {
        if (b.getText().equals("Next"){
            b.click();
            break;
        }
于 2017-10-15T10:04:43.663 回答
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");
            }
        }
}
}
于 2013-12-24T07:41:25.833 回答
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 代码实际上对我有用。我得到了同样的陈旧元素错误。

于 2018-06-25T15:48:26.283 回答
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)
  );
于 2018-08-28T10:44:29.787 回答
0

就我而言,我使用的定位器有多个结果,因此它无法识别正确的元素来执行操作,最终引发异常。因此,拥有一个独特的定位器解决了我的问题。

于 2021-05-21T07:03:04.807 回答
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#特定的代码。

于 2019-07-18T02:47:45.953 回答
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. btnTurnOff被替换为btnTurnOn- 好的
  3. btnTurnOn被司机发现。- 好的
  4. btnTurnOn被替换为btnTurnOff- 好的
  5. 我们调用this.btnTurnOff.isDisplayed();在 Selenium 意义上不再存在的元素 - 你可以看到它,它完美地工作,但它是同一个按钮的不同实例

可能的修复:

  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;
  }
于 2019-03-04T22:38:25.800 回答
-8

使用此代码等到附加元素:

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

    }
于 2015-06-25T22:20:29.500 回答