简体   繁体   English

访问shadow DOM中的元素

[英]Accessing elements in the shadow DOM

Is it possible to find elements inside the Shadow DOM with python-selenium? 是否可以使用python-selenium 在Shadow DOM中找到元素

Example use case: 用例示例:

I have this input with type="date" : inputtype="date"

 <input type="date"> 

And I'd like to click the date picker button on the right and choose a date from the calendar. 我想点击右边的日期选择按钮 ,然后从日历中选择一个日期。

If you would inspect the element in Chrome Developer Tools and expand the shadow-root node of the date input, you would see the button is appearing as: 如果您要检查Chrome开发者工具中的元素并展开日期输入的影子根节点,您会看到该按钮显示为:

<div pseudo="-webkit-calendar-picker-indicator" id="picker"></div>

Screenshot demonstrating how it looks in Chrome: 屏幕截图演示了它在Chrome中的外观:

在此输入图像描述

Finding the "picker" button by id results into NoSuchElementException : 按ID查找“选择器”按钮会导致NoSuchElementException

>>> date_input = driver.find_element_by_name('bday')
>>> date_input.find_element_by_id('picker')
...
selenium.common.exceptions.NoSuchElementException: Message: no such element

I've also tried to use ::shadow and /deep/ locators as suggested here : 我也试着使用::shadow/deep/定位器的建议在这里

>>> driver.find_element_by_css_selector('input[name=bday]::shadow #picker')
...
selenium.common.exceptions.NoSuchElementException: Message: no such element
>>>
>>> driver.find_element_by_css_selector('input[name=bday] /deep/ #picker')
...
selenium.common.exceptions.NoSuchElementException: Message: no such element

Note that I can change the date in the input by sending keys to it: 请注意,我可以通过向其发送密钥来更改输入中的日期:

driver.find_element_by_name('bday').send_keys('01/11/2014')

But, I want to set the date specifically by choosing it from a calendar. 但是,我想通过从日历中选择日期来具体设置日期。

There's no way to access the shadow root of native HTML 5 elements. 无法访问本机HTML 5元素的影子根。

Not useful in this case, but with Chrome it's possible to access a custom created shadow root: 在这种情况下没用,但使用Chrome可以访问自定义创建的阴影根:

 var root = document.querySelector("#test_button").createShadowRoot(); root.innerHTML = "<button id='inner_button'>Button in button</button" 
 <button id="test_button"></button> 

The root can then be accessed this way: 然后可以通过以下方式访问root:

 var element = document.querySelector("#test_button").shadowRoot;

If you want to automate a click on the inner button with selenium python (chromedriver version 2.14+): 如果你想用selenium python(chromedriver版本2.14+)自动点击内部按钮:

 >>> outer = driver.execute_script('return document.querySelector("#test_button").shadowRoot')
 >>> inner = outer.find_element_by_id("inner_button")
 >>> inner.click()

Update 9 Jun 2015 2015年6月9日更新

This is the link to the current Shadow DOM W3C Editor's draft on github: 这是github上当前Shadow DOM W3C编辑器草案的链接:

http://w3c.github.io/webcomponents/spec/shadow/ http://w3c.github.io/webcomponents/spec/shadow/

If you're interested in browsing the blink source code, this is a good starting point . 如果您对浏览blink源代码感兴趣, 这是一个很好的起点

The accepted answer has a drawback, many times the shadow host elements are hidden withing shadow trees that's why the best way to do it is to use the selenium selectors to find the shadow host elements and inject the script just to take the shadow root: 接受的答案有一个缺点,很多时候阴影主机元素隐藏着阴影树,这就是为什么最好的方法是使用selenium选择器来查找阴影主机元素并注入脚本只是为了获取阴影根:

def expand_shadow_element(element):
  shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
  return shadow_root

#the accepted answer code then becomes 
outer = expand_shadow_element(driver.find_element_by_css_selector("#test_button"))
inner = outer.find_element_by_id("inner_button")
inner.click()

To put this into perspective I just added a testable example with Chrome's download page, clicking the search button needs open 3 nested shadow root elements: 为了说明这一点,我刚刚在Chrome的下载页面添加了一个可测试的示例,单击搜索按钮需要打开3个嵌套的阴影根元素: 在此输入图像描述

import selenium
from selenium import webdriver
driver = webdriver.Chrome()


def expand_shadow_element(element):
  shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
  return shadow_root

driver.get("chrome://downloads")
root1 = driver.find_element_by_tag_name('downloads-manager')
shadow_root1 = expand_shadow_element(root1)

root2 = shadow_root1.find_element_by_css_selector('downloads-toolbar')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_css_selector('cr-search-field')
shadow_root3 = expand_shadow_element(root3)

search_button = shadow_root3.find_element_by_css_selector("#search-button")
search_button.click()

Doing the same using the accepted answer approach has the drawback that it hard-codes the queries, is less readable and you cannot use the intermediary selections for other actions: 使用接受的答案方法执行相同操作的缺点是,它会对查询进行硬编码,可读性较差,并且您无法将中间选择用于其他操作:

search_button = driver.execute_script('return document.querySelector("downloads-manager").shadowRoot.querySelector("downloads-toolbar").shadowRoot.querySelector("cr-search-field").shadowRoot.querySelector("#search-button")')
search_button.click()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM