посилання на застарілий елемент: елемент не прикріплений до документа сторінки


97

У мене є список, який містить кілька посилань у кожному розділі. Кожен розділ має однакові посилання. Мені потрібно натиснути певне посилання під кожним розділом. Я написав наведений нижче код, але при його запуску він видає мені stale element reference: element is not attached to the page documentпомилку.

Це мій код:

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

}

Це структура HTML, як показано нижче

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

Трасування помилок:

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

Відповіді:


81

Який рядок дає виняток ??

Причиною цього є те, що елемент, на який ви посилалися, вилучається зі структури DOM

З тією ж проблемою я стикався під час роботи з IEDriver. Причиною було те, що javascript завантажив елемент ще раз після того, як я посилався, тому моє посилання на дату вказувало на неіснуючий об'єкт, навіть якщо він був правильним для їхнього інтерфейсу. Я використав наступне обхідне рішення.

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

Подивіться, чи те саме може вам допомогти!


linkTexts [i] = e.getText (); lines видає мені помилку під час повторного циклу
Patil Prashanth

1
Вирішено помилку через оновлення сторінки 1. Спочатку я скопіював усі тексти посилань у змінну масиву String, а пізніше використав для циклу for, щоб натиснути на потрібні посилання під кожним розділом. для (WebElement e: LeftNavLinks) {linkTexts [i] = e.getText (); i ++; } for (int j = 0; j <leftnavlen; j ++) {if (linkTexts [j] .equals (ben)) {String BenefitStatLi = "// * [@ id = 'sliding-navigation'] / li [% s ] / a "; driver.findElement (By.xpath (String.format (BenefitStatLi, j + 1))). click (); driver.findElement (By.xpath ("// * @ id = 'divContentHolder'] / div [1] / a [1]")). click (); }
Патіл Прашант

Так було з тієї ж причини. Визначені раніше елементи були вилучені з DOM через оновлення сторінки :)
Абхішек Сінгх

Так, справді так. Велике спасибі @AbhishekSingh
Rahal Kanishka

У моєму випадку я отримав цей виняток, оскільки у мене немає оператора break в циклі після натискання необхідного елемента, тому слідкуйте і за такими ситуаціями.
Радж Асапу,

48

Щоразу, коли ви стикаєтеся з цією проблемою, просто ще раз визначте веб-елемент над рядком, у якому ви отримуєте помилку.

Приклад:

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

Оскільки DOM змінився, наприклад, завдяки дії оновлення, ви отримуєте StaleElementReferenceпомилку.

Рішення:

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


Гей, здається, це вирішило мою помилку. Але я не розумію, чому я повинен визначати той самий елемент, визначивши його раніше.
Абхі,

8

Для цього я використовую наступний метод клацання. Це спробує знайти та клацнути елемент. Якщо 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;
}

8

Ці помилки мають дві загальні причини: Елемент повністю видалено або елемент більше не приєднаний до 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')))

Див .: https://selenium-python.readthedocs.io/waits.html


це спрацювало для мене. Також посилання на документ допомогло зрозуміти, як це працює. Дякую @Bruno_Sanches
Nikunj Kakadiya

4

Справа в тому, що ви використовуєте цикл for поза вашим умовним оператором.

Після того, як умови у вашому операторі IF будуть дотримані, ви, ймовірно, перейдете на іншу сторінку, отже, коли цикл for намагається повторити ітерацію ще раз, ви отримуєте помилку застарілого елемента, оскільки ви знаходитесь на іншій сторінці.

Ви можете додати перерву в кінці вашого твердження if, це спрацювало для мене.


Мені знадобився час, щоб зрозуміти, чому сталася помилка!
BEWARB

3

Просто розірвіть цикл, коли знайдете елемент, який ви хочете натиснути на ньому. наприклад:

  List<WebElement> buttons = getButtonElements();
    for (WebElement b : buttons) {
        if (b.getText().equals("Next"){
            b.click();
            break;
        }

2

Використовуйте цей код:

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

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

Цей код спроби / лову насправді спрацював для мене. Я отримав ту саму помилку застарілого елемента.


1
Чи ця відповідь якось відрізняється від відповіді з найбільшим рейтингом тут?
SiKing

1

Це можна зробити в нових версіях селену в JS (але всі підтримуючі застарілість роботи будуть працювати):

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

0

На думку @Abhishek Singh's, вам потрібно зрозуміти проблему:

Який рядок дає виняток ?? Причиною цього є те, що елемент, на який ви посилалися, вилучається зі структури DOM

і ви більше не можете посилатися на нього (уявіть, який ідентифікатор елемента змінився).

Дотримуйтесь коду:

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

А тепер давайте запитати, чому?

  1. btnTurnOff був знайдений водієм - добре
  2. btnTurnOffбуло замінено на btnTurnOn- добре
  3. btnTurnOnбув знайдений водієм. - в порядку
  4. btnTurnOnбуло замінено на btnTurnOff- добре
  5. ми звертаємось this.btnTurnOff.isDisplayed();до елемента, який вже не існує в сенсі селену - ви це бачите, він чудово працює, але це інший екземпляр тієї самої кнопки .

Можливе виправлення:

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

0

У моєму випадку у мене була сторінка, де це input type='date'посилання, яке я отримав під час завантаження сторінки, але коли я спробував з нею взаємодіяти, це показало це, exceptionі це було цілком значущим, оскільки Javascriptманіпулювало моїм контролем, отже, воно було відірвано від документа і я мав re-getпосилання на нього після того, як javascript виконав свою роботу з контролем. Отже, ось як виглядав мій код до винятку:

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

Щось, про що я дізнався дотепер, - ловити винятки, щоб знати про успішне виконання коду, - це не гарна ідея, але мені довелося це зробити, і я виявив, що це work-aroundдобре працює у цьому випадку.

PS: Після написання всього цього я просто помітив теги, для яких призначена ця тема java. Цей зразок коду призначений лише для демонстрації. Він може допомогти людям, які мають проблеми з C#мовою. Або його можна легко перекласти, javaоскільки він не має особливо C#конкретного коду.


-7

використовуйте цей код, щоб почекати, поки елемент буде приєднаний:

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

    }

1
не спрацював для мене, але все-таки цікавий підхід
Яр

28
Це не має сенсу ніколи.
Сем

1
Це має сенс, це просто не найбільш зрозуміло з точки зору назви змінної "breakIt". Що це робить, це вирватися з циклу while (true), як тільки помилка "елемент не прикріплений" більше не викидається.
наждак

'логічне значення doIt = true; while (doIt) {try {// напиши свій код тут} catch (Exception e) {if (e.getMessage (). contains ("element is not attach")) {doIt = false; }}} '
Тао Чжан,

1
пахне як гото дух
Євген Барановський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.