Mam listę, która zawiera wiele linków w każdej sekcji. Każda sekcja ma te same łącza, których potrzebuję, aby kliknąć określone łącze w każdej sekcji. Napisałem poniższy kod, ale po wykonaniu stale element reference: element is not attached to the page document
wyświetla mi się błąd.
To jest mój kod:
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++;
}
}
}
To jest struktura HTML, jak poniżej
<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>
Ślad błędu to:
Exception in thread "main"
org.openqa.selenium.StaleElementReferenceException: stale element
reference: element is not attached to the page document
źródło
Ilekroć napotkasz ten problem, po prostu zdefiniuj ponownie element sieciowy powyżej linii, w której pojawia się błąd.
Przykład:
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();
Ponieważ DOM zmienił się, np. W wyniku akcji aktualizacji, otrzymujesz
StaleElementReference
błąd.Rozwiązanie:
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();
źródło
Te błędy mają dwie typowe przyczyny: element został całkowicie usunięty lub element nie jest już powiązany z DOM.
Jeśli już sprawdziłeś, czy to nie jest Twój przypadek, możesz mieć ten sam problem co ja.
Element w DOM nie został znaleziony, ponieważ Twoja strona nie jest całkowicie załadowana, gdy Selenium szuka elementu. Aby rozwiązać ten problem, możesz umieścić jawny warunek oczekiwania, który mówi Selenium, aby czekał, aż element będzie dostępny do kliknięcia.
from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) element = wait.until(EC.element_to_be_clickable((By.ID, 'someid')))
Zobacz: https://selenium-python.readthedocs.io/waits.html
źródło
Aby sobie z tym poradzić, używam następującej metody kliknięcia. Spowoduje to próbę znalezienia i kliknięcia elementu. Jeśli DOM zmieni się między znalezieniem a kliknięciem, spróbuje ponownie. Chodzi o to, że jeśli się nie powiedzie i spróbuję natychmiast, druga próba się powiedzie. Jeśli zmiany DOM są bardzo szybkie, to nie zadziała.
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; }
źródło
Chodzi o to, że używasz pętli for poza instrukcją warunkową.
Po spełnieniu warunków w instrukcji IF prawdopodobnie przechodzisz do innej strony, więc gdy pętla for ponownie spróbuje wykonać iterację, zostanie wyświetlony błąd przestarzałego elementu, ponieważ jesteś na innej stronie.
Możesz dodać przerwę na końcu swojego oświadczenia if, to zadziałało dla mnie.
źródło
Po prostu przerwij pętlę, gdy znajdziesz element, który chcesz kliknąć. na przykład:
List<WebElement> buttons = getButtonElements(); for (WebElement b : buttons) { if (b.getText().equals("Next"){ b.click(); break; }
źródło
Użyj tego kodu:
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"); } } } }
źródło
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(); }
Ten kod try / catch faktycznie zadziałał dla mnie. Otrzymałem ten sam błąd nieaktualnego elementu.
źródło
Można to zrobić w nowszych wersjach selenu w JS (ale wszystkie wspierające nieaktualność będą działać):
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) );
źródło
Zgodnie z @Abhishek Singh's musisz zrozumieć problem:
i nie możesz już się do niego odwoływać (wyobraź sobie, jaki identyfikator elementu się zmienił).
Postępuj zgodnie z kodem:
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(); } }
Zastanówmy się teraz, dlaczego?
btnTurnOff
został znaleziony przez kierowcę - okbtnTurnOff
został zastąpiony przezbtnTurnOn
- okbtnTurnOn
został znaleziony przez kierowcę. - dobrzebtnTurnOn
został zastąpiony przezbtnTurnOff
- okthis.btnTurnOff.isDisplayed();
się do elementu, który już nie istnieje w sensie Selenium - widać, działa doskonale, ale jest to inna instancja tego samego przycisku .Możliwa poprawka:
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; }
źródło
W moim przypadku miałem stronę, na której była to
input type='date'
referencja, którą otrzymałem podczas ładowania strony, ale kiedy próbowałem z nią wejść w interakcję, pokazało toexception
i to było dość znaczące, ponieważJavascript
manipulowałem moją kontrolą, dlatego zostało odłączone od dokumentu i musiałemre-get
odwołanie po tym, jak javascript wykonał swoją pracę z formantem. A więc tak wyglądał mój kod przed wyjątkiem:if (elemDate != null) { elemDate.Clear(); elemDate.SendKeys(model.Age); }
Kod po zgłoszeniu wyjątku:
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.
Więc teraz możesz wykonać dalsze działania z kodem lub możesz zamknąć sterownik, jeśli nie udało mu się uruchomić kontroli.
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...
PS: Po napisaniu tego wszystkiego, właśnie zauważyłem tagi, dla których był ten wątek
java
. Ten przykładowy kod służy tylko do celów demonstracyjnych. Może pomóc osobom, które mają problem zC#
językiem. Lub można go łatwo przetłumaczyć,java
ponieważ nie ma zbytC#
konkretnego kodu.źródło
użyj tego kodu, aby poczekać, aż element zostanie dołączony:
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; } }
źródło