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

Java 陈旧元素引用:元素未附加到页面文档,java,list,selenium-webdriver,Java,List,Selenium Webdriver,我有一个列表,每个部分下都有多个链接。每个部分都有相同的链接,我需要单击每个部分下的特定链接。我已经写了下面的代码,但是当它执行时,它给了我陈旧的元素引用:元素没有附加到页面文档中错误 这是我的代码: public static void main(String[] args) throws InterruptedException { WebDriver driver = new ChromeDriver(); driver.navigate().to("url......"

我有一个列表,每个部分下都有多个链接。每个部分都有相同的链接,我需要单击每个部分下的特定链接。我已经写了下面的代码,但是当它执行时,它给了我
陈旧的元素引用:元素没有附加到页面文档中
错误

这是我的代码:

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

}

给出例外的行是什么

这是因为您引用的元素已从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();
}
看看是否同样可以帮助你

使用以下代码:

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");
            }
        }
}
}
公共类链接测试
{   
公共静态void main(字符串[]args)
{
WebDriver=newfirefoxdriver();
驱动程序。导航()。到(“file:///C:/Users/vkiran/Desktop/xyz.html");
列出alllinks=driver.findElements(By.xpath(“//*[@id='slide-navigation']///a”);
字符串a[]=新字符串[alllinks.size()];

对于(int i=0;i使用此代码等待元素连接:

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

    }

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

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


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

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

  List<WebElement> buttons = getButtonElements();
    for (WebElement b : buttons) {
        if (b.getText().equals("Next"){
            b.click();
            break;
        }
List buttons=getButtonElements();
for(WebElement b:按钮){
if(b.getText().equals(“Next”){
b、 单击();
打破
}

要处理它,我使用以下单击方法。 这将尝试查找并单击元素。如果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;
}
public boolean retryingFindClick(By){
布尔结果=假;
int=0;
而(尝试次数<2次){
试一试{
driver.findElement(by).click();
结果=真;
打破
}捕获(StaleElementException e){
}
尝试++;
}
返回结果;
}

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

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


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

请参阅:

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

例子:
WebElement-button=driver.findElement(By.xpath(“xpath”);
按钮。单击();
//在这里,您可以执行更新或保存之类的操作
//然后再次尝试使用按钮WebElement单击
按钮。单击();
由于DOM已更改(例如通过更新操作),因此您将收到一个
StaleElementReference
错误

解决方案:
WebElement-button=driver.findElement(By.xpath(“xpath”);
按钮。单击();
//在这里,您可以执行更新或保存之类的操作
//然后在使用按钮元素之前再次定义它
WebElement button1=driver.findElement(By.xpath(“xpath”);
//该新元素将指向新DOM中的同一元素
按钮1.单击();

根据@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();
  }
}
现在,让我们想知道为什么

  • btnTurnOff
    被驱动程序找到-确定
  • btnTurnOff
    已由
    btnTurnOn
    替换-确定
  • btnTurnOn
    被一个驱动程序找到。-好的
  • btnTurnOn
    已替换为
    btnTurnOff
    -正常
  • 我们在元素上调用
    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;
      }
    

    在我的例子中,我有一个页面,它是一个
    input type='date'
    ,我在页面加载时得到了它的引用,但是当我尝试与它交互时,它显示了这个
    异常
    ,这非常有意义,因为
    Javascript
    操纵了我的控件,因此它与文档分离,我必须
    重新获得
    I在javascript对控件执行其工作后,ts引用。
    这就是我的代码在异常之前的样子:

    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.
    
    到目前为止,我学到的是,抓住例外,去了解 关于成功的代码执行不是一个好主意,但是,我不得不这么做 它和我发现这个
    解决方案在这种情况下运行良好

    PS:在写了所有这些之后,我注意到这个线程用于
    java
    的标记
    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();
      }
    }
    
      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;
      }
    
    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...