java - 如何在使用 cssSelector 清除 Chrome 浏览器的浏览数据时与 #shadow-root (open) 中的元素进行交互

标签 java css selenium css-selectors shadow-dom

我一直在关注讨论How to automate shadow DOM elements using selenium?使用 #shadow-root (open) 元素。

在访问 url chrome://settings/clearBrowserData 时出现的 Clear browser data 弹出窗口中找到 Clear data 按钮的过程中 通过 Selenium 我无法找到以下元素:

#shadow-root (open)
<settings-privacy-page>

快照:

settings-privacy-page

使用 Selenium以下是我的代码试验和遇到的相关错误:

  • 尝试 1:

    WebElement root5 = shadow_root4.findElement(By.tagName("settings-privacy-page"));
    
    • 错误:

      Exception in thread "main" org.openqa.selenium.JavascriptException: javascript error: b.getElementsByTagName is not a function
      
  • 尝试 2:

    WebElement root5 = shadow_root4.findElement(By.cssSelector("settings-privacy-page"));
    
    • 错误:

      Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"settings-privacy-page"}
      
  • 尝试 3:

    WebElement root5 = (WebElement)((JavascriptExecutor)shadow_root4).executeScript("return document.getElementsByTagName('settings-privacy-page')[0]");
    
    • 错误:

      Exception in thread "main" java.lang.ClassCastException: org.openqa.selenium.remote.RemoteWebElement cannot be cast to org.openqa.selenium.JavascriptExecutor
      

Incase 如果它有帮助,初始代码块(直到上面的行)工作完美:

driver.get("chrome://settings/clearBrowserData");
WebElement root1 = driver.findElement(By.tagName("settings-ui"));
WebElement shadow_root1 = expand_shadow_element(root1);

WebElement root2 = shadow_root1.findElement(By.cssSelector("settings-main#main"));
WebElement shadow_root2 = expand_shadow_element(root2);

WebElement root3 = shadow_root2.findElement(By.cssSelector("settings-basic-page[role='main']"));
WebElement shadow_root3 = expand_shadow_element(root3);

WebElement root4 = shadow_root3.findElement(By.cssSelector("settings-section[page-title='Privacy and security']"));
WebElement shadow_root4 = expand_shadow_element(root4);

PS:expand_shadow_element() 完美运行。

最佳答案

如果您正在尝试获取“清除数据”元素,那么您可以使用下面的 js 获取该元素然后执行。

return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')

这是示例脚本。

driver.get("chrome://settings/clearBrowserData");
driver.manage().window().maximize();
JavascriptExecutor js = (JavascriptExecutor) driver; 
WebElement clearData = (WebElement) js.executeScript("return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')");
// now you can click on clear data button
clearData.click();

编辑 2:解释

问题:Selenium 不提供对 Shadow DOM elements 的明确支持,因为它们不在当前 dom 中。这就是为什么我们在尝试访问 shadow dom 中的元素时会得到 NoSuchElementException 异常的原因。

影子 DOM: enter image description here

注意:我们将引用图片中显示的术语。所以请通过图片更好地理解。

解决方案:

为了使用shadow element,首先我们必须找到shadow dom 附加到的shadow host。下面是根据shadowHost获取shadow root的简单方法。

private static WebElement getShadowRoot(WebDriver driver,WebElement shadowHost) {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    return (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
}

然后您可以使用 shadowRoot 元素访问影子树元素。

// get the shadowHost in the original dom using findElement
WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS"));
// get the shadow root
WebElement shadowRoot = getShadowRoot(driver,shadowHost);
// access shadow tree element
WebElement shadowTreeElement = shadowRoot.findElement(By.cssSelector("shadow_tree_element_css"));

为了简化上述所有步骤,创建了以下方法。

public static WebElement getShadowElement(WebDriver driver,WebElement shadowHost, String cssOfShadowElement) {
    WebElement shardowRoot = getShadowRoot(driver, shadowHost);
    return shardowRoot.findElement(By.cssSelector(cssOfShadowElement));
}

现在你可以通过单一方法调用获取 shadowTree 元素

WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS_Goes_here));
WebElement shadowTreeElement = getShadowElement(driver,shadowHost,"shadow_tree_element_css");

并照常执行操作,如 .click().getText()

shadowTreeElement.click()

当您只有一层影子 DOM 时,这看起来很简单。但是在这里,在这种情况下,我们有多个级别的影子 dom。所以我们必须通过到达每个影子主机和根来访问该元素。 enter image description here

下面是使用上述方法(getShadowElement 和 getShadowRoot)的代码片段

// Locate shadowHost on the current dom
WebElement shadowHostL1 = driver.findElement(By.cssSelector("settings-ui"));

// now locate the shadowElement by traversing all shadow levels
WebElement shadowElementL1 = getShadowElement(driver, shadowHostL1, "settings-main");
WebElement shadowElementL2 = getShadowElement(driver, shadowElementL1,"settings-basic-page");
WebElement shadowElementL3 = getShadowElement(driver, shadowElementL2,"settings-section > settings-privacy-page");
WebElement shadowElementL4 = getShadowElement(driver, shadowElementL3,"settings-clear-browsing-data-dialog");
WebElement shadowElementL5 = getShadowElement(driver, shadowElementL4,"#clearBrowsingDataDialog");
WebElement clearData = shadowElementL5.findElement(By.cssSelector("#clearBrowsingDataConfirm"));
System.out.println(clearData.getText());
clearData.click();

您可以在答案开头提到的单个 js 调用中实现上述所有步骤(在下面添加只是为了减少混淆)。

WebElement clearData = (WebElement) js.executeScript("return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')");

截图: enter image description here

关于java - 如何在使用 cssSelector 清除 Chrome 浏览器的浏览数据时与 #shadow-root (open) 中的元素进行交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56380091/

相关文章:

html - 如何在我的案例中构建模态

c# - Selenium SendKey 与 jquery inputmask 冲突

python - 选择没有索引的下一个元素

java - 如何使用 JFrame 按钮启动第二个 JFrame 而无需在 Java 中启动更多 JFrame?

java - 如何使用 Jsoup 从 css 中提取值?

Java 套接字 : How to send and serialize object between multiple applications

javascript - 更改 p 标签中单行的颜色,然后将其改回

javascript - 同时使用 onepage-scroll.js 和 scrollReveal.js

java - utf 8 编码无法使用 java 正常工作

Python 和 Selenium 查找名称不准确的元素