Tham chiếu phần tử cũ: phần tử không được đính kèm với tài liệu trang


97

Tôi có danh sách có nhiều liên kết dưới mỗi phần. Mỗi phần có các liên kết giống nhau, tôi cần nhấp vào một liên kết cụ thể dưới mỗi phần. Tôi đã viết đoạn mã dưới đây nhưng khi nó thực thi nó báo cho tôi stale element reference: element is not attached to the page documentlỗi.

Đây là mã của tôi:

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

}

Đây là cấu trúc HTML như bên dưới

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

Dấu vết Lỗi là:

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

Câu trả lời:


81

Dòng đưa ra ngoại lệ là gì ??

Lý do cho điều này là vì phần tử mà bạn đã tham chiếu đến bị xóa khỏi cấu trúc DOM

Tôi đã gặp phải vấn đề tương tự khi làm việc với IEDriver. Lý do là vì javascript đã tải phần tử một lần nữa sau khi tôi đã tham chiếu, vì vậy tham chiếu ngày của tôi đã trỏ đến một đối tượng không tồn tại ngay cả khi nó nằm ngay trên giao diện người dùng của chúng. Tôi đã sử dụng cách giải quyết sau.

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

Xem nếu điều tương tự có thể giúp bạn!


linkTexts [i] = e.getText (); dây chuyền mang lại cho tôi lỗi khi vòng lặp lần thứ hai
Patil Prashanth

1
Đã giải quyết lỗi là do làm mới trang 1. đầu tiên tôi sao chép tất cả các văn bản liên kết vào một biến mảng Chuỗi và sau đó được sử dụng để lặp lại để nhấp vào các liên kết được yêu cầu dưới mỗi phần. for (WebElement e: LeftNavLinks) {linkTexts [i] = e.getText (); i ++; } for (int j = 0; j <leftnavlen; j ++) {if (linkTexts [j] .equals (ben)) {String BenefitStatLi = "// * [@ id = 'slide-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 (); }
Patil Prashanth

Vì vậy, nó cũng vì lý do tương tự. Các phần tử được xác định trước đó đã bị xóa khỏi DOM do làm mới trang :)
Abhishek Singh

Có thực sự nó .Thank một @AbhishekSingh nhiều
Rahal Kanishka

Trong trường hợp của tôi, tôi có ngoại lệ này vì tôi không có câu lệnh break trong vòng lặp sau khi phần tử bắt buộc được nhấp vào. Vì vậy, hãy chú ý những trường hợp như vậy.
Raj Asapu

48

Bất cứ khi nào bạn gặp phải vấn đề này, chỉ cần xác định lại phần tử web phía trên dòng mà bạn đang gặp Lỗi.

Thí dụ:

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

Vì DOM đã thay đổi, ví dụ như thông qua hành động cập nhật, bạn đang nhận được StaleElementReferenceLỗi.

Giải pháp:

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


Này, điều này dường như đã giải quyết được lỗi của tôi. Nhưng tôi không hiểu tại sao tôi phải xác định cùng một phần tử đã xác định nó trước đó.
Abhi

8

Để xử lý nó, tôi sử dụng phương pháp nhấp chuột sau. Thao tác này sẽ cố gắng tìm và nhấp vào phần tử. Nếu DOM thay đổi giữa tìm kiếm và nhấp chuột, nó sẽ thử lại. Ý tưởng là nếu nó không thành công và tôi thử lại ngay lần thử thứ hai sẽ thành công. Nếu các thay đổi DOM diễn ra rất nhanh thì điều này sẽ không hoạt động.

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

Lỗi này có hai nguyên nhân phổ biến: Phần tử đã bị xóa hoàn toàn hoặc phần tử không còn được gắn vào DOM.

Nếu bạn đã kiểm tra nếu đó không phải là trường hợp của bạn, bạn có thể gặp phải vấn đề tương tự như tôi.

Phần tử trong DOM không được tìm thấy vì trang của bạn không được tải hoàn toàn khi Selenium đang tìm kiếm phần tử. Để giải quyết điều đó, bạn có thể đặt một điều kiện chờ rõ ràng yêu cầu Selenium đợi cho đến khi phần tử có sẵn để được nhấp vào.

from selenium.webdriver.support import expected_conditions as EC

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

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


điều này đã làm việc cho tôi. Cũng liên kết đến tài liệu rất hữu ích trong việc hiểu cách thức hoạt động của nó. Cảm ơn @Bruno_Sanches
Nikunj Kakadiya

4

Vấn đề ở đây là bạn đang sử dụng vòng lặp for bên ngoài câu lệnh điều kiện của mình.

Sau khi các điều kiện trong câu lệnh IF của bạn được đáp ứng, bạn có thể điều hướng đến một trang khác, do đó khi vòng lặp for cố gắng lặp lại một lần nữa, bạn sẽ gặp lỗi phần tử cũ vì bạn đang ở một trang khác.

Bạn có thể thêm dấu ngắt vào cuối câu lệnh if, điều này phù hợp với tôi.


Tôi đã mất một lúc để hiểu tại sao lỗi lại xảy ra!
BEWARB

3

Chỉ ngắt vòng lặp khi bạn tìm thấy phần tử bạn muốn nhấp vào nó. ví dụ:

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

2

Sử dụng mã này:

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

Mã thử / bắt này thực sự làm việc cho tôi. Tôi gặp cùng một lỗi phần tử cũ.


1
Câu trả lời này bằng cách nào đó khác với câu trả lời được bình chọn nhiều nhất ở đây?
SiKing

1

Điều này có thể được thực hiện trong các phiên bản selen mới hơn trong JS (nhưng tất cả các ứng dụng hỗ trợ steenessOf sẽ hoạt động):

 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

Theo @Abhishek Singh's, bạn cần hiểu vấn đề:

Dòng cho ngoại lệ là gì ?? Lý do cho điều này là vì phần tử mà bạn đã tham chiếu đến bị xóa khỏi cấu trúc DOM

và bạn không thể tham chiếu đến nó nữa (hãy tưởng tượng ID của phần tử đã thay đổi).

Làm theo mã:

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

Bây giờ, chúng ta hãy tự hỏi tại sao?

  1. btnTurnOff được tìm thấy bởi một người lái xe - ok
  2. btnTurnOffđã được thay thế bởi btnTurnOn- ok
  3. btnTurnOnđược tìm thấy bởi một người lái xe. - đồng ý
  4. btnTurnOnđã được thay thế bởi btnTurnOff- ok
  5. chúng tôi gọi this.btnTurnOff.isDisplayed();phần tử không tồn tại nữa theo nghĩa Selenium - bạn có thể thấy nó, nó hoạt động hoàn hảo, nhưng nó là một trường hợp khác của cùng một nút .

Có thể sửa chữa:

  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

Trong trường hợp của tôi, tôi có một trang mà nó là một input type='date'tham chiếu mà tôi đã nhận được khi tải trang, nhưng Khi tôi cố gắng tương tác với nó, nó cho thấy điều này exceptionvà điều đó khá có ý nghĩa vì Javascriptđã thao túng quyền kiểm soát của tôi do đó nó bị tách khỏi tài liệu và tôi phải re-gettham chiếu đến nó sau khi javascript đã thực hiện công việc của nó với điều khiển. Vì vậy, đây là cách mã của tôi trông như thế nào trước khi ngoại lệ:

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

Mã sau khi ngoại lệ được nêu ra:

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.

Vì vậy, Bây giờ bạn có thể thực hiện các hành động khác với mã của mình hoặc bạn có thể thoát khỏi trình điều khiển nếu nó không thành công trong việc điều khiển hoạt động.

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

Điều mà tôi đã học được cho đến nay là, bắt các ngoại lệ để biết về việc thực thi mã thành công không phải là một ý kiến ​​hay, Nhưng tôi phải làm điều đó và tôi thấy điều này work-aroundhoạt động tốt trong trường hợp này.

PS: Sau khi viết tất cả những điều này, tôi chỉ nhận thấy các thẻ mà chủ đề này dành cho java. Mẫu mã này chỉ dành cho mục đích trình diễn, Nó có thể giúp những người gặp vấn đề về C#ngôn ngữ. Hoặc nó có thể dễ dàng được dịch sang javavì nó không có nhiều C#mã cụ thể.


-7

sử dụng mã này để đợi cho đến khi phần tử được đính kèm:

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
không hiệu quả với tôi nhưng vẫn là một cách tiếp cận thú vị
Yar

28
Điều này không có ý nghĩa gì bao giờ hết.
Sam

1
Nó có ý nghĩa, nó không phải là rõ ràng nhất về tên biến của nó "breakIt". Điều đó xảy ra là thoát khỏi vòng lặp while (true) ngay sau khi lỗi "phần tử không được đính kèm" không còn được ném ra nữa.
emery

'boolean doIt = true; while (doIt) {try {// viết mã của bạn tại đây} catch (Exception e) {if (e.getMessage (). chứa ("phần tử không được đính kèm")) {doIt = false; }}} '
Tao Zhang,

1
mùi như goto tinh thần
Eugene Baranovsky
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.