当元素 DOM 未更改时,Webdriver 陈旧元素异常

问题描述 投票:0回答:2

我在 Java 中使用 WebDriver。

我想从下面的金额字段中获取所有金额值,因此我计划循环遍历每个表行,并查找是否选中了复选框,将金额设置为定义的数字。

enter image description here

示例源代码可以在这里找到: http://eric-lin.net/upload/index.php

我使用以下Java方法来查找金额字段值:

public void fillInAllAmountForSelectedItems() {

    List<WebElement> allItems = driver
            .findElements(By
                    .xpath("//table[@id = 'bulkPaymentForm:itemTable']//tbody[@id = 'bulkPaymentForm:itemTable:tbody_element']//tr[contains(@class, 'handCursor row-border tranItemRow')]"));
    System.out.println(allItems.size());
    //return 3, expected

    waitTimer(2, 1000);

    for (WebElement item : allItems) {
        System.out.println(item.findElement(By.xpath("//td[4]"))
                .getAttribute("Value"));
    }       
}

大多数时候,foreach循环会因为陈旧元素异常而失败。我不明白,因为 DOM 没有改变。

当它工作时,foreach 循环不会打印任何内容,因此看起来它没有正确找到元素。

我该如何解决这个问题?我需要做什么才能实现此功能的目的,为所有选中的项目填写金额值?

提前非常感谢。

java selenium webdriver selenium-webdriver
2个回答
3
投票

处理

StaleElementException
通常是一个尝试重试,直到成功的故事。 DOM 可能会因多种原因而刷新,从而导致元素变得陈旧。

处理元素列表时有时使用且通常有效的策略是逐项获取每个项目。 here发布了一个很好的解决方案,示例代码如下。

您将需要构建 xpath /css 选择器来相应地引用所需的表格单元格。请注意,该值将从

input
内的金额
td
字段中检索 - 如果您尝试从
td
获取文本,它将是空白的,就像您现在看到的那样。

//get the number of items that are required
int size = driver.findElements(By.cssSelector("table#mytable>tbody>tr>td[4]/input")).size();

//now work with each one individually, rather than with a list
for(int i = 1; i <= size; i++) {
    String locator = String.format("table#mytable>tbody>tr[%d]>td[4]/input", i);
    WebElement inputField = driver.findElement(By.cssSelector(locator));
    //get or set the value of the input element
    System.out.println(inputField.getAttribute("value"));
}

0
投票

发生陈旧是因为

您的初始目标 (

variable
) 制作了 HTML 外观的本地副本。当 DOM 更改时(例如,您转到下一页),它仍然具有相同的初始状态,这被认为是陈旧的。

修复

修复非常简单,您所要做的就是再次重新定位/重新分配该目标。这是考虑到您有多个页面和相同的目标 HTML 标记,但您不想每次都刷新页面,这是浪费时间。 在

for
循环中重定向 HTML 标签的示例

示例

我打赌你们一定在使用 python,我很抱歉删除了 javascript 代码。

for (let x = 0; x <= totalPages; x++) {
  // Skip clicking the next page if x is 0 [first page]
  if (x > 0) {
    await driver
      .wait(
        until.elementLocated(By.xpath("input your next button xpath")),
        patience, // input your time in milliseconds
      )
      .click();

    // time taken to get to next page
    await driver.sleep(5000);
  }

  // this is your target, this will retarget everytime the loop runs
  const tableBody = await driver.wait(
    until.elementsLocated(By.tagName("tbody")),
    patience,
  );

  // write your other business logic as you like
}

© www.soinside.com 2019 - 2024. All rights reserved.