Accessing Shadow DOM tree with Selenium

后端 未结 9 815
执念已碎
执念已碎 2020-11-28 09:57

Is it possible to access elements within a Shadow DOM using Selenium/Chrome webdriver?

Using the normal element search methods doesn\'t work, as is to be expected.

9条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-11-28 10:49

    The accepted answer is no longer valid and some of the other answers have some drawbacks or are not practical (the /deep/ selector doesn't work and is deprecated, document.querySelector('').shadowRoot works only with the first shadow element when shadow elements are nested), sometimes the shadow root elements are nested and the second shadow root is not visible in document root, but is available in its parent accessed shadow root. I think is better to use the selenium selectors and inject the script just to take the shadow root:

    def expand_shadow_element(element):
      shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
      return shadow_root
    
    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:

    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 approach suggested in the other answers 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()
    

提交回复
热议问题