古い要素の参照:要素がページドキュメントに添付されていません


101

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

回答:


84

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

これは、参照した要素が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();
}

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


linkTexts [i] = e.getText(); 行は、2回目のループ中にエラーが発生します
Patil Prashanth 2013

1
ページの更新1が原因で発生したエラーを解決しました。最初にすべてのリンクテキストを文字列配列変数にコピーし、後でforループを使用して各セクションの下の必要なリンクをクリックしました。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(); }
Patil Prashanth 2013

それで同じ理由でした。以前に特定された要素は、ページの更新によりDOMから削除されました:)
Abhishek Singh

はい、確かにそうです。どうもありがとう@AbhishekSingh
Rahal Kanishka

私の場合、必要な要素がクリックされた後、ループ内にbreakステートメントがないため、この例外が発生しました。そのような状況にも注意してください。
Raj Asapu 2017

48

この問題に直面したときはいつでも、エラーが発生している行の上にもう一度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();


ねえ、これは私のエラーを解決したようです。しかし、以前に定義したのと同じ要素を定義する必要がある理由がわかりません。
Abhi

大きな助け。しかし、同じ変数に対してDOMが変更された理由を説明できますか?ありがとう。
JuBaerAD20年

@ JuBaerAD-フレームワーク(react / vue / others)で完全に実行できると主張します。単純な「直接」HTMLは、物事が変化するのを見るのが難しいでしょう。しかし、フレームワークを使用すると、それ自体で完全に何かを行う可能性があり、副作用はこれです。違う?何よりもまず、UIは引き続き機能しますか?つまり、React UIは引き続き機能し、必要に応じて機能しますか?はい、問題ありません...私は20年以上UIテストを行ってきました。コントロールを参照したときに実際のコントロールしかなく(テストしないのが楽しい)、技術的にアクセスするたびに新しいコントロールを取得するUIを見てきました。私は疑うだろうここ..同じこと...
端午

9

このエラーには、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


これは私のために働いた。また、ドキュメントへのリンクは、それがどのように機能するかを理解するのに役立ちました。おかげ@Bruno_Sanches
Nikunj Kakadiya

8

それを処理するために、私は次のクリック方法を使用します。これにより、要素を見つけてクリックしようとします。検索とクリックの間にDOMが変更された場合、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;
}

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

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構造から削除されているためです。

そして、あなたはもうそれを参照することができません(どの要素の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 運転手が見つけた-わかりました
  2. btnTurnOffに置き換えられましたbtnTurnOn-ok
  3. btnTurnOn運転手によって発見されました。- OK
  4. btnTurnOnに置き換えられましたbtnTurnOff-ok
  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
私にはうまくいきませんでしたが、それでも興味深いアプローチ
Yar

28
これは今まで何の意味もありません。
サム

1
それは理にかなっています、それはその変数名「breakIt」に関して最も明確ではありません。これは、「要素がアタッチされていません」というエラーがスローされなくなるとすぐに、while(true)ループから抜け出すことです。
emery 2017

'ブールdoIt = true; while(doIt){try {//ここにコードを記述} catch(Exception e){if(e.getMessage()。contains( "element is not attach")){doIt = false; }}} '
Tao Zhang

1
後藤スピリットのようなにおい
Eugene Baranovsky 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.