ссылка на устаревший элемент: элемент не прикреплен к документу страницы


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 дает мне ошибку при повторении цикла во второй раз
Патил Прашант

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 = 'скользящая навигация'] / li [% s ] / а "; driver.findElement (By.xpath (String.format (BenefitStatLi, j + 1))). click (); driver.findElement (By.xpath ("// * @ id = 'divContentHolder'] / div [1] / a [1]")). click (); }
Патил Прашант

Так было по той же причине. Выявленные ранее элементы были удалены из модели DOM в связи с обновлением страницы :)
Абхишек Сингх,

Да, действительно так. Большое спасибо @AbhishekSingh
Rahal

В моем случае я получил это исключение, так как у меня нет оператора 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();


Эй, похоже, моя ошибка решена. Но я не понимаю, почему я должен определять тот же элемент, определив его ранее.
Abhi

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
Никундж Какадия

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

Этот код try / catch действительно сработал для меня. У меня такая же ошибка устаревшего элемента.


1
Этот ответ как-то отличается от ответа, получившего наибольшее количество голосов?
SiKing

1

Это можно было бы сделать в новых версиях селена в JS (но все поддерживающие stalenessOf будут работать):

 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, вам нужно понять проблему:

Какая строка дает исключение ?? Причина в том, что упомянутый вами элемент удален из структуры 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();элемент, который больше не существует в смысле Selenium - вы можете увидеть его, он работает отлично, но это другой экземпляр той же кнопки .

Возможное исправление:

  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 ("элемент не прикреплен")) {doIt = false; }}} '
Тао Чжан

1
пахнет духом гото
Евгений Барановский
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.