referencia de elemento obsoleto: el elemento no está adjunto al documento de página

97

Tengo una lista que tiene varios enlaces en cada sección. Cada sección tiene los mismos enlaces. Necesito hacer clic en un enlace en particular debajo de cada sección. He escrito el siguiente código pero cuando se ejecuta me da un stale element reference: element is not attached to the page documenterror.

Este es mi codigo:

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 es la estructura HTML es la siguiente

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

El seguimiento de error es:

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

Respuestas:

81

¿Cuál es la línea que da excepción?

La razón de esto es que el elemento al que se ha referido se elimina de la estructura DOM.

Me enfrentaba al mismo problema mientras trabajaba con IEDriver. La razón fue porque javascript cargó el elemento una vez más después de que hice referencia, por lo que mi referencia de fecha apuntó a un objeto inexistente incluso si estaba correcto en la interfaz de usuario. Usé la siguiente solución.

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

¡Vea si lo mismo puede ayudarlo!

Abhishek Singh
fuente
linkTexts [i] = e.getText (); las líneas me dan un error al realizar un bucle por segunda vez
Patil Prashanth
1
Se resolvió el error que se debía a la actualización de la página 1. Primero copié todos los textos de los enlaces a una variable de matriz de cadenas y luego usé el bucle for para hacer clic en los enlaces requeridos debajo de cada sección. para (WebElement e: LeftNavLinks) {linkTexts [i] = e.getText (); i ++; } for (int j = 0; j <leftnavlen; j ++) {if (linkTexts [j] .equals (ben)) {String BenefitStatLi = "// * [@ id = 'navegación deslizante'] / li [% s ]/una"; driver.findElement (By.xpath (String.format (BenefitStatLi, j + 1))). click (); driver.findElement (By.xpath ("// * @ id = 'divContentHolder'] / div [1] / a [1]")). click (); }
Patil Prashanth
Entonces fue por la misma razón. Los elementos identificados anteriormente se eliminaron de DOM debido a la actualización de la página :)
Abhishek Singh
Sí, de hecho lo hace. Muchas gracias @AbhishekSingh
Rahal Kanishka
En mi caso, obtuve esta excepción porque no tengo una declaración de interrupción en el ciclo después de hacer clic en el elemento requerido, así que tenga cuidado con estas situaciones también.
Raj Asapu
48

Siempre que se enfrente a este problema, simplemente defina el elemento web una vez más encima de la línea en la que está obteniendo un error.

Ejemplo:

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

Dado que el DOM ha cambiado, por ejemplo, a través de la acción de actualización, está recibiendo un StaleElementReference error.

Solución:

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
fuente
Oye, esto parece haber resuelto mi error. Pero no entiendo por qué tengo que definir el mismo elemento habiéndolo definido antes.
Abhi
8

Para manejarlo, utilizo el siguiente método de clic. Esto intentará encontrar y hacer clic en el elemento. Si el DOM cambia entre buscar y hacer clic, lo intentará de nuevo. La idea es que si falló y lo intento de nuevo inmediatamente, el segundo intento tendrá éxito. Si los cambios de DOM son muy rápidos, esto no 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
fuente
8

Estos errores tienen dos causas comunes: el elemento se ha eliminado por completo o el elemento ya no está adjunto al DOM.

Si ya marcó si no es su caso, podría estar enfrentando el mismo problema que yo.

El elemento en el DOM no se encuentra porque su página no está completamente cargada cuando Selenium está buscando el elemento. Para resolver eso, puede poner una condición de espera explícita que le indique a Selenium que espere hasta que el elemento esté disponible para hacer clic en él.

from selenium.webdriver.support import expected_conditions as EC

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

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

Bruno Sanches
fuente
esto funcionó para mí. También el enlace al documento fue útil para comprender cómo funciona. Gracias @Bruno_Sanches
Nikunj Kakadiya
4

La cosa aquí es que estás usando un bucle for fuera de tu declaración condicional.

Después de que se cumplan las condiciones en su declaración IF, probablemente navegue a otra página, por lo tanto, cuando el bucle for intente iterar una vez más, obtendrá el error de elemento obsoleto porque está en una página diferente.

Puede agregar un descanso al final de su declaración if, esto funcionó para mí.

Adrian Coroi
fuente
¡Me tomó un tiempo entender por qué ocurría el error!
BEWARB
3

Simplemente rompa el ciclo cuando encuentre el elemento en el que desea hacer clic. por ejemplo:

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

Utilice 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
fuente
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 de prueba / captura realmente funcionó para mí. Obtuve el mismo error de elemento obsoleto.

Charan Raz
fuente
1
¿Es esta respuesta de alguna manera diferente de la respuesta más votada aquí?
SiKing
1

Esto se podría hacer en versiones más recientes de selenium en JS (pero todo el soporte stalenessOf funcionará):

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

Según @Abhishek Singh, debes comprender el problema:

¿Cuál es la línea que da excepción? La razón de esto es que el elemento al que se ha referido se elimina de la estructura DOM.

y ya no puede hacer referencia a él (imagine qué ID de elemento ha cambiado).

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

Ahora, preguntémonos por qué.

  1. btnTurnOff fue encontrado por un conductor - ok
  2. btnTurnOfffue reemplazado por btnTurnOn- ok
  3. btnTurnOnfue encontrado por un conductor. - Okay
  4. btnTurnOnfue reemplazado por btnTurnOff- ok
  5. llamamos this.btnTurnOff.isDisplayed();al elemento que ya no existe en el sentido de Selenium; puedes verlo, funciona perfectamente, pero es una instancia diferente del mismo botón .

Posible solución:

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

En mi caso, tenía una página donde era una input type='date'referencia cuya referencia había obtenido al cargar la página, pero cuando traté de interactuar con ella, mostró esto exceptiony eso fue bastante significativo, ya que Javascripthabía manipulado mi control, por lo tanto, se separó del documento. y tuve re-getsu referencia después de que javascript hubiera realizado su trabajo con el control. Entonces, así es como se veía mi código antes de la excepción:

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

Código después de que se generó la excepción:

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.

Por lo tanto, ahora puede realizar más acciones con su código o puede salir del controlador si no logró que el control funcione.

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 he aprendido hasta ahora es que detectar excepciones para saber sobre la ejecución exitosa del código no es una buena idea, pero tenía que hacerlo y descubrí que esto work-aroundfuncionaba bien en este caso.

PD: Después de escribir todo esto, me di cuenta de las etiquetas para las que era este hilo java. Esta muestra de código es solo para fines de demostración, podría ayudar a las personas que tienen problemas con el C#idioma. O se puede traducir fácilmente javaya que no tiene mucho C#código específico.

Jamshaid Kamran
fuente
-7

use este código para esperar hasta que se adjunte el elemento:

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

    }

fuente
1
no funcionó para mí, pero sigue siendo un enfoque interesante
Yar
28
Esto no tiene ningún sentido.
Sam
1
Tiene sentido, simplemente no es el más claro en términos de su nombre de variable "breakIt". Lo que hace es salir del ciclo while (verdadero) tan pronto como el error "el elemento no está adjunto" ya no se lanza.
Emery
'boolean doIt = true; while (doIt) {try {// escribe tu código aquí} catch (Exception e) {if (e.getMessage (). contains ("element is not adjunto")) {doIt = false; }}} '
Tao Zhang
1
huele a espíritu goto
Eugene Baranovsky