web-dev-qa-db-ja.com

古い要素参照:要素はページ文書に添付されていません

各セクションの下に複数のリンクがあるリストがあります。各セクションには同じリンクがあり、各セクションの下にある特定のリンクをクリックする必要があります。以下のコードを作成しましたが、実行すると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("[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++;
    }
}

}

これは以下のような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
51
Patil Prashanth

例外を与える行は何ですか?

これは、参照先の要素がDOM構造から削除されるためです。

IEDriverでの作業中に同じ問題に直面していました。理由は、javascriptが参照した後にもう一度要素を読み込んだため、UIで正しい場合でも、日付参照が存在しないオブジェクトを指していたためです。次の回避策を使用しました。

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

同じことがあなたを助けることができるかどうかを確認してください!

60
Abhishek Singh

この問題に直面したときはいつでも、エラーが発生している行の上にあるWeb要素をもう一度定義するだけです。

例:

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

10
rohit goudar

それを処理するには、次のクリック方法を使用します。これにより、要素を見つけてクリックしようとします。検索とクリックの間にDOMが変更されると、再試行されます。失敗した場合、すぐに再試行すると2回目の試行が成功するという考え方です。 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;
}
5
Alisha Raju

このエラーには2つの一般的な原因があります。要素が完全に削除された、または要素がDOMに添付されなくなった。

それがあなたのケースではないかどうか既にチェックした場合、あなたは私と同じ問題に直面している可能性があります。

Seleniumが要素を検索しているときにページが完全にロードされていないため、DOMの要素は見つかりません。これを解決するには、要素がクリックされるまで待機するように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

2
Bruno Sanches

ここでのことは、条件文の外でforループを使用しているということです。

IFステートメントの条件が満たされた後、おそらく別のページに移動します。したがって、forループがもう一度繰り返そうとすると、別のページにいるため、古い要素エラーが発生します。

Ifステートメントの最後にbreakを追加できます。これは私にとってはうまくいきました。

2
Adrian Coroi

このコードを使用してください:

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

クリックする要素が見つかったら、ループを解除します。例えば:

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

これは、JSの新しいバージョンのSeleniumで実行できます(ただし、すべてのサポート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)
  );
1
SkorpEN
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
Charan Raz

@Abhishek Singhによると、問題を理解する必要があります。

例外を与える行は何ですか?これは、参照先の要素が削除済み DOM構造からであるためです。

そして、あなたはもうそれを参照することはできません(どの要素のIDが変更されたか想像してください)。

コードに従ってください:

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はドライバーによって検出されました-OK
  2. btnTurnOffbtnTurnOnに置き換えられました-わかりました
  3. btnTurnOnはドライバーによって検出されました。 - OK
  4. btnTurnOnbtnTurnOffに置き換えられました-わかりました
  5. seleniumの意味でもう存在しない要素に対して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
J.Wincewicz

私の場合、ページの読み込み時に参照を取得したinput type='date'のページがありましたが、操作しようとすると、このexceptionが表示され、Javascriptはコントロールを操作したため、ドキュメントから切り離され、JavaScriptがコントロールでジョブを実行した後、その参照をre-getする必要がありました。だから、これは私のコードが例外の前にどのように見えたかです:

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#言語に問題がある人に役立つかもしれません。または、C#固有のコードがあまりないため、Javaに簡単に変換できます。

0
Jamshaid Kamran