veraltete Elementreferenz: Das Element ist nicht an das Seitendokument angehängt

96

Ich habe eine Liste mit mehreren Links unter jedem Abschnitt. Jeder Abschnitt hat die gleichen Links, die ich brauche, um unter jedem Abschnitt auf einen bestimmten Link zu klicken. Ich habe den folgenden Code geschrieben, aber wenn er ausgeführt wird, gibt er mir einen stale element reference: element is not attached to the page documentFehler.

Das ist mein Code:

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

}

Dies ist die HTML-Struktur wie folgt

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

Die Fehlerverfolgung lautet:

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

Antworten:

80

Was ist die Linie, die Ausnahme gibt?

Der Grund dafür ist, dass das Element, auf das Sie verwiesen haben, aus der DOM-Struktur entfernt wird

Bei der Arbeit mit IEDriver hatte ich das gleiche Problem. Der Grund war, dass Javascript das Element noch einmal geladen hat, nachdem ich verwiesen habe, sodass meine Datumsreferenz auf ein nicht vorhandenes Objekt zeigte, auch wenn es auf der Benutzeroberfläche richtig war. Ich habe die folgende Problemumgehung verwendet.

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

Sehen Sie, ob das gleiche Ihnen helfen kann!

Abhishek Singh
quelle
linkTexts [i] = e.getText (); Zeilen gibt mir Fehler beim Schleifen zum zweiten Mal
Patil Prashanth
1
Der Fehler wurde durch Seitenaktualisierung 1 behoben. Zuerst habe ich alle Linktexte in eine String-Array-Variable kopiert und später verwendet, damit die Schleife auf die erforderlichen Links unter jedem Abschnitt klickt. für (WebElement e: LeftNavLinks) {linkTexts [i] = e.getText (); i ++; } für (int j = 0; j <leftnavlen; j ++) {if (linkTexts [j] .equals (ben)) {String BenefitStatLi = "// * [@ id = 'Sliding-Navigation'] / li [% s ]/ein"; driver.findElement (By.xpath (String.format (BenefitStatLi, j + 1))). click (); driver.findElement (By.xpath ("// * @ id = 'divContentHolder'] / div [1] / a [1]"). click (); }
Patil Prashanth
So war es aus dem gleichen Grund. Zuvor identifizierte Elemente wurden aufgrund einer Seitenaktualisierung aus dem DOM entfernt :)
Abhishek Singh
Ja, in der Tat. Vielen Dank @AbhishekSingh
Rahal Kanishka
In meinem Fall habe ich diese Ausnahme erhalten, da ich keine break-Anweisung in der Schleife habe, nachdem auf das erforderliche Element geklickt wurde. Achten Sie auch auf solche Situationen.
Raj Asapu
47

Wenn Sie auf dieses Problem stoßen, definieren Sie das Webelement einfach erneut über der Zeile, in der ein Fehler angezeigt wird.

Beispiel:

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

Da sich das DOM z. B. durch die Aktualisierungsaktion geändert hat, wird ein StaleElementReferenceFehler angezeigt.

Lösung:

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
quelle
Hey, das scheint meinen Fehler gelöst zu haben. Aber ich verstehe nicht, warum ich dasselbe Element definieren muss, nachdem ich es zuvor definiert habe.
Abhi
8

Um damit umzugehen, verwende ich die folgende Klickmethode. Dadurch wird versucht, das Element zu finden und darauf zu klicken. Wenn sich das DOM zwischen Suchen und Klicken ändert, wird es erneut versucht. Die Idee ist, dass der zweite Versuch erfolgreich sein wird, wenn es fehlschlägt und ich es sofort erneut versuche. Wenn die DOM-Änderungen sehr schnell sind, funktioniert dies nicht.

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

Diese Fehler haben zwei häufige Ursachen: Das Element wurde vollständig gelöscht oder das Element ist nicht mehr mit dem DOM verbunden.

Wenn Sie bereits überprüft haben, ob dies nicht Ihr Fall ist, haben Sie möglicherweise das gleiche Problem wie ich.

Das Element im DOM wird nicht gefunden, da Ihre Seite nicht vollständig geladen ist, wenn Selenium nach dem Element sucht. Um dies zu lösen, können Sie eine explizite Wartebedingung festlegen, die Selenium anweist, zu warten, bis das Element zum Klicken verfügbar ist.

from selenium.webdriver.support import expected_conditions as EC

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

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

Bruno Sanches
quelle
das hat bei mir funktioniert. Auch der Link zum Dokument war hilfreich, um zu verstehen, wie es funktioniert. Danke @Bruno_Sanches
Nikunj Kakadiya
4

Die Sache hier ist, dass Sie eine for-Schleife außerhalb Ihrer bedingten Anweisung verwenden.

Nachdem die Bedingungen in Ihrer IF-Anweisung erfüllt sind, navigieren Sie wahrscheinlich zu einer anderen Seite. Wenn die for-Schleife erneut versucht zu iterieren, wird der Fehler "Veraltetes Element" angezeigt, da Sie sich auf einer anderen Seite befinden.

Sie können am Ende Ihrer if-Anweisung eine Pause einfügen, dies hat bei mir funktioniert.

Adrian Coroi
quelle
Ich habe eine Weile gebraucht, um zu verstehen, warum ein Fehler aufgetreten ist!
BEWARB
3

Brechen Sie einfach die Schleife, wenn Sie das Element finden, auf das Sie klicken möchten. zum Beispiel:

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

Verwenden Sie diesen Code:

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

Dieser Try / Catch-Code hat tatsächlich bei mir funktioniert. Ich habe den gleichen veralteten Elementfehler.

Charan Raz
quelle
1
Unterscheidet sich diese Antwort irgendwie von der hier am besten bewerteten Antwort?
SiKing
1

Dies könnte in neueren Versionen von Selen in JS erfolgen (aber alle unterstützenden StalenessOf funktionieren):

 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)
  );
Skorphen
quelle
0

Laut @Abhishek Singh müssen Sie das Problem verstehen:

Was ist die Linie, die Ausnahme gibt? Der Grund dafür ist, dass das Element, auf das Sie verwiesen haben , aus der DOM-Struktur entfernt wird

und Sie können nicht mehr darauf verweisen (stellen Sie sich vor, welche ID des Elements sich geändert hat).

Folgen Sie dem Code:

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

Fragen wir uns jetzt warum?

  1. btnTurnOff wurde von einem Fahrer gefunden - ok
  2. btnTurnOffwurde ersetzt durch btnTurnOn- ok
  3. btnTurnOnwurde von einem Fahrer gefunden. - OK
  4. btnTurnOnwurde ersetzt durch btnTurnOff- ok
  5. Wir rufen this.btnTurnOff.isDisplayed();das Element auf, das im Selen-Sinne nicht mehr existiert - Sie können es sehen, es funktioniert perfekt, aber es ist eine andere Instanz derselben Schaltfläche .

Mögliche Lösung:

  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. Princewicz
quelle
0

In meinem Fall hatte ich eine Seite, auf der es sich um eine Seite handelte, input type='date'deren Referenz ich beim Laden der Seite erhalten hatte, aber als ich versuchte, damit zu interagieren, zeigte dies exceptionund das ziemlich aussagekräftig, da Javascriptmeine Steuerung manipuliert worden war und sie daher vom Dokument getrennt wurde und ich musste auf re-getseine Referenz verweisen, nachdem das Javascript seine Arbeit mit der Kontrolle ausgeführt hatte. So sah mein Code vor der Ausnahme aus:

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

Code nach Auslösung der Ausnahme:

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.

Jetzt können Sie weitere Aktionen mit Ihrem Code ausführen oder den Treiber beenden, wenn die Steuerung nicht funktioniert.

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

Was ich bisher gelernt habe, ist, dass es keine gute Idee ist, Ausnahmen zu erfassen, um über eine erfolgreiche Codeausführung Bescheid zu wissen. Aber ich musste es tun und fand, dass dies work-aroundin diesem Fall gut funktioniert.

PS: Nachdem ich das alles geschrieben hatte, bemerkte ich nur die Tags, für die dieser Thread war java. Dieses Codebeispiel dient nur zu Demonstrationszwecken. Es kann Personen helfen, die Probleme mit der C#Sprache haben. Oder es kann leicht übersetzt werden, javada es nicht viel C#spezifischen Code hat.

Jamshaid Kamran
quelle
-7

Verwenden Sie diesen Code, um zu warten, bis das Element angehängt ist:

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

    }

quelle
1
hat bei mir nicht funktioniert, aber immer noch ein interessanter Ansatz
Yar
28
Das macht überhaupt keinen Sinn.
Sam
1
Es macht Sinn, es ist einfach nicht das klarste in Bezug auf den Variablennamen "breakIt". Das bedeutet, dass Sie aus der while (true) -Schleife ausbrechen, sobald der Fehler "Element nicht angehängt" nicht mehr ausgelöst wird.
Schmirgel
'boolean doIt = true; while (doIt) {versuche {// schreibe deinen Code hier} catch (Ausnahme e) {if (e.getMessage (). enthält ("Element ist nicht angehängt")) {doIt = false; }}} '
Tao Zhang
1
riecht nach goto Geist
Eugene Baranovsky