referência de elemento obsoleto: o elemento não é anexado ao documento da página

101

Tenho uma lista com vários links em cada seção. Cada seção tem os mesmos links. Preciso clicar em um link específico em cada seção. Eu escrevi o código abaixo, mas quando ele executa, ele me dá um stale element reference: element is not attached to the page documenterro.

Este é o meu código:

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

}

Esta é a estrutura HTML é a seguinte

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

O rastreamento de erros é:

    Exception in thread "main" 
org.openqa.selenium.StaleElementReferenceException: stale element 
reference: element is not attached to the page document
Patil Prashanth
fonte

Respostas:

84

Qual é a linha que dá exceção ??

A razão para isso é porque o elemento ao qual você se referiu é removido da estrutura DOM

Eu estava enfrentando o mesmo problema enquanto trabalhava com o IEDriver. O motivo era porque o javascript carregou o elemento mais uma vez depois que eu fiz referência, de modo que minha referência de data apontou para um objeto inexistente, mesmo que estivesse correto em sua interface do usuário. Usei a seguinte solução alternativa.

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

Veja se o mesmo pode te ajudar!

Abhishek Singh
fonte
linkTexts [i] = e.getText (); linhas me dão erro durante o loop pela segunda vez
Patil Prashanth
1
O erro foi resolvido devido à atualização da página 1. primeiro, copiei todos os textos de link para uma variável de array String e depois usei para fazer um loop para clicar nos links necessários em cada seção. para (WebElement e: LeftNavLinks) {linkTexts [i] = e.getText (); i ++; } for (int j = 0; j <leftnavlen; j ++) {if (linkTexts [j] .equals (ben)) {String BenefitStatLi = "// * [@ id = 'navegação deslizante'] / li [% s ]/uma"; driver.findElement (By.xpath (String.format (BenefitStatLi, j + 1))). click (); driver.findElement (By.xpath ("// * @ id = 'divContentHolder'] / div [1] / a [1]")). click (); }
Patil Prashanth de
Portanto, foi pelo mesmo motivo. Os elementos identificados anteriormente foram removidos do DOM devido à atualização da página :)
Abhishek Singh
Sim, é verdade. Muito obrigado @AbhishekSingh
Rahal Kanishka
No meu caso, recebi essa exceção porque não tenho a instrução break no loop depois que o elemento necessário é clicado. Portanto, esteja atento a essas situações também.
Raj Asapu,
49

Sempre que você enfrentar esse problema, apenas defina o elemento da web mais uma vez acima da linha em que você está obtendo um erro.

Exemplo:

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

Como o DOM mudou, por exemplo, por meio da ação de atualização, você está recebendo um StaleElementReferenceerro.

Solução:

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

Rohit Goudar
fonte
Ei, isso parece ter resolvido meu erro. Mas não entendo por que tenho que definir o mesmo elemento, tendo-o definido anteriormente.
Abhi
Ótima ajuda. Mas você pode explicar por que o DOM mudou para a mesma variável? Obrigado.
JuBaer AD
@JuBaerAD - afirmaria que pode completamente no framework (react / vue / others). O HTML "direto" simples seria difícil de ver as coisas mudando. Mas usar uma estrutura pode fazer algo completamente por conta própria e o efeito colateral é este. Errado? Em primeiro lugar, a IU ainda funciona, ou seja, a IU do React ainda funciona e funciona conforme necessário? Sim, sem problemas ... Eu faço testes de IU há mais de 20 anos, vi IUs onde havia apenas controles reais ao fazer referência a um controle (divertido de testar, não), e cada vez que você acessava tecnicamente, obtinha um novo controle .. Mesma coisa aqui que eu suspeito ...
Dano
9

Esses erros têm duas causas comuns: o elemento foi totalmente excluído ou o elemento não está mais anexado ao DOM.

Se você já verificou se não é o seu caso, você pode estar enfrentando o mesmo problema que eu.

O elemento no DOM não foi encontrado porque sua página não foi totalmente carregada quando o Selenium está procurando o elemento. Para resolver isso, você pode colocar uma condição de espera explícita que diga ao Selenium para esperar até que o elemento esteja disponível para ser clicado.

from selenium.webdriver.support import expected_conditions as EC

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

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

Bruno Sanches
fonte
isso funcionou para mim. O link para o documento também ajudou a entender como ele funciona. Obrigado @Bruno_Sanches
Nikunj Kakadiya
8

Para lidar com isso, uso o seguinte método de clique. Isso tentará localizar e clicar no elemento. Se o DOM mudar entre localizar e clicar, ele tentará novamente. A ideia é que se ele falhar e eu tentar novamente imediatamente, a segunda tentativa terá sucesso. Se as alterações do DOM forem muito rápidas, isso não funcionará.

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;
}
Alisha Raju
fonte
4

O que acontece aqui é que você está usando um loop for fora de sua instrução condicional.

Depois que as condições em sua instrução IF forem atendidas, você provavelmente navegará para outra página, portanto, quando o loop for tenta iterar mais uma vez, você obtém o erro de elemento obsoleto porque está em uma página diferente.

Você pode adicionar uma pausa no final de sua instrução if, isso funcionou para mim.

Adrian Coroi
fonte
Levei um tempo para entender por que o erro estava ocorrendo!
BEWARB
3

Basta interromper o loop quando encontrar o elemento que deseja clicar nele. por exemplo:

  List<WebElement> buttons = getButtonElements();
    for (WebElement b : buttons) {
        if (b.getText().equals("Next"){
            b.click();
            break;
        }
אדם נחמיאס
fonte
2

Use este código:

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

Este código try / catch realmente funcionou para mim. Recebi o mesmo erro de elemento obsoleto.

Charan Raz
fonte
1
Esta resposta é de alguma forma diferente da resposta mais votada aqui?
SiKing
1

Isso pode ser feito em versões mais recentes do selênio em JS (mas todos os que suportam stalenessOf funcionarão):

 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)
  );
SkorpEN
fonte
0

De acordo com @Abhishek Singh, você precisa entender o problema:

Qual é a linha que dá exceção ?? A razão para isso é porque o elemento ao qual você se referiu é removido da estrutura DOM

e você não pode mais se referir a ele (imagine qual ID do elemento mudou).

Siga o código:

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

Agora, vamos nos perguntar por quê?

  1. btnTurnOff foi encontrado por um motorista - ok
  2. btnTurnOfffoi substituído por btnTurnOn- ok
  3. btnTurnOnfoi encontrado por um motorista. - Está bem
  4. btnTurnOnfoi substituído por btnTurnOff- ok
  5. chamamos this.btnTurnOff.isDisplayed();o elemento que não existe mais no sentido do selênio - você pode ver, ele funciona perfeitamente, mas é uma instância diferente do mesmo botão .

Possível correção:

  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;
  }
J.Wincewicz
fonte
0

No meu caso, eu tinha uma página onde era uma input type='date'referência cuja referência eu obtive no carregamento da página, mas quando tentei interagir com ela, ela mostrou queexception e aquilo foi bastante significativo, pois Javascripthavia manipulado meu controle, portanto, foi destacado do documento e tive a re-getsua referência após o javascript ter executado seu trabalho com o controle. Então, é assim que meu código parecia antes da exceção:

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

Código depois que a exceção foi levantada:

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.

Portanto, agora você pode executar outras ações com seu código ou pode encerrar o driver se ele não teve êxito em fazer o controle funcionar.

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

Algo que aprendi até agora é que capturar exceções para saber sobre execução de código bem-sucedida não é uma boa ideia, mas, eu tinha que fazer isso e descobri que work-around que estava funcionando bem neste caso.

PS: Depois de escrever tudo isso, acabei de notar as tags para as quais este tópico se destina java. Este exemplo de código é apenas para fins de demonstração, pode ajudar as pessoas que têm problemas com a C#linguagem. Ou pode ser facilmente traduzido javaporque não tem muito C#código específico.

Jamshaid K.
fonte
-7

use este código para esperar até que o elemento seja anexado:

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

    }

fonte
1
não funcionou para mim, mas ainda assim uma abordagem interessante
Yar
28
Isso não faz sentido algum.
Sam
1
Faz sentido, mas não é o mais claro em termos de seu nome de variável "breakIt". O que isso faz é interromper o loop while (true) assim que o erro "elemento não anexado" não estiver mais sendo lançado.
esmeril de
'boolean doIt = true; while (doIt) {try {// escreva seu código aqui} catch (Exception e) {if (e.getMessage (). contains ("o elemento não está anexado")) {doIt = false; }}} '
Tao Zhang
1
cheira a espírito goto
Eugene Baranovsky