旧元素参考:元素未附加到页面文档


101

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

Answers:


84

给出异常的行是什么?

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

看看是否同样可以帮助您!


linkTexts [i] = e.getText(); 线在第二次循环时给我错误
Patil Prashanth

1
解决该错误是由于页面刷新1引起的。首先,我将所有链接文本复制到String数组变量中,然后用于for循环以单击每个部分下的所需链接。for(WebElement e:LeftNavLinks){linkTexts [i] = e.getText(); i ++; } for(int j = 0; j <leftnavlen; j ++){if(linkTexts [j] .equals(ben)){字符串BenefitStatLi =“ // * [@ id ='sliding-navigation'] / li [%s ]/一种”; driver.findElement(By.xpath(String.format(BenefitStatLi,j + 1)))。click(); driver.findElement(By.xpath(“ // * @ id ='divContentHolder'] / div [1] / a [1]”))。click(); }
Patil Prashanth

出于同样的原因。由于页面刷新,先前标识的元素已从DOM中删除:)
Abhishek Singh

是的,的确如此。非常感谢@AbhishekSingh
Rahal Kanishka,

在我的情况下,我遇到了这个异常,因为单击所需元素后循环中没有break语句。因此也要注意这种情况。
拉吉·阿萨普

48

每当遇到此问题时,只需在出现错误的行上方再次定义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();


嘿,这似乎已经解决了我的错误。但是我不明白为什么我必须定义先前定义过的相同元素。
阿披

很大的帮助。但是,您能否解释一下为什么DOM更改了相同的变量?谢谢。
JuBaer AD

@JuBaerAD-将断言它可以完全在框架上(react / vue / others)。简单的“直接” HTML很难看到事情发生变化。但是使用框架可能会完全自己做一些事情,副作用是这样。错误?首先还是最重要的是,UI仍然可以正常工作,即React UI仍然可以根据需要工作和运行吗?是的,没问题...我已经进行了20多年的UI测试,我看到UI在引用控件时只有实际的控件(有趣的是没有测试),并且每次从技术上来说您都获得了一个新控件..我怀疑这里也是...
Dano

9

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


这对我有用。此外,链接到文档有助于理解其工作原理。感谢@Bruno_Sanches
Nikunj Kakadiya'Apri 29'20

8

为了处理它,我使用以下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;
}

4

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

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

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


花了我一段时间了解为什么会发生错误!
BEWARB

3

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

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

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

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
这个答案与这里投票最多的答案有什么不同吗?
斯金

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

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

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#特定的代码,因此可以轻松地将其翻译成。


-7

使用此代码来等待元素被附加:

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

    }

1
不适用于我,但仍然是一种有趣的方法
Yar

28
这是没有道理的。
山姆

1
这是有道理的,就其变量名称“ breakIt”而言,它不是最清楚的。这样做是,一旦不再抛出“元素未附加”错误,就会退出while(true)循环。
emery

'布尔doIt = true; while(doIt){试试{//在这里写代码} catch(Exception e){if(e.getMessage()。contains(“ element is not Attached”)){doIt = false; }}}'
张涛

1
闻起来像是烈酒
尤金·巴拉诺夫斯基
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.