Search code examples
python-3.xseleniumselenium-webdriverui-automationshadow-dom

How to automate/access nested shadow DOM elements using selenium python?


I want to access/automate an element of nested shadow DOM. Ley's say for the given link https://books-pwakit.appspot.com/ I want to search some books by sending some book name. I am trying like this according to my learnt knowledge but it is not locating the element. My code is like this...

url = "https://books-pwakit.appspot.com/"
driver.get(url)

shadow_host1 = driver.find_element(By.CSS_SELECTOR, "book-app[apptitle='BOOKS']")
shadow_root1 = driver.execute_script('return arguments[0].shadowRoot', shadow_host1)

shadow_host2 = driver.find_element(By.CSS_SELECTOR, "app-header[effects='waterfall']")
shadow_root2 = driver.execute_script('return arguments[0].shadowRoot', shadow_host2)

shadow_host3 = driver.find_element(By.CSS_SELECTOR, '.toolbar-top')
shadow_root3 = driver.execute_script('return arguments[0].shadowRoot', shadow_host3)

shadow_host4 = driver.find_element(By.CSS_SELECTOR, '.toolbar-bottom')
shadow_root4 = driver.execute_script('return arguments[0].shadowRoot', shadow_host4)

shadow_host5 = driver.find_element(By.CSS_SELECTOR, 'book-input-decorator')
shadow_root5 = driver.execute_script('return arguments[0].shadowRoot', shadow_host5)

shadow_content = shadow_root5.find_element(By.CSS_SELECTOR, '#input')

shadow_content.send_keys("Twilight")   

Can anyone help me please and describe me why I am getting that error.


Solution

  • In order to get the inner shadow_hosts you need to apply the find_element() method on outer shadow_host element, not on the global driver object.
    The following code works:

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    
    options = Options()
    options.add_argument("start-maximized")
    
    webdriver_service = Service('C:\webdrivers\chromedriver.exe')
    driver = webdriver.Chrome(options=options, service=webdriver_service)
    wait = WebDriverWait(driver, 30)
    
    url = "https://books-pwakit.appspot.com/"
    driver.get(url)
    
    shadow_host1 = driver.find_element(By.CSS_SELECTOR, "book-app[apptitle='BOOKS']")
    shadow_root1 = driver.execute_script('return arguments[0].shadowRoot', shadow_host1)
    
    shadow_host2 = shadow_root1.find_element(By.CSS_SELECTOR, "app-header[effects='waterfall']")
    shadow_root2 = driver.execute_script('return arguments[0].shadowRoot', shadow_host2)
    
    shadow_host3 = shadow_root1.find_element(By.CSS_SELECTOR, '.toolbar-top')
    shadow_root3 = driver.execute_script('return arguments[0].shadowRoot', shadow_host3)
    
    shadow_host4 = shadow_root1.find_element(By.CSS_SELECTOR, '.toolbar-bottom')
    shadow_root4 = driver.execute_script('return arguments[0].shadowRoot', shadow_host4)
    
    shadow_host5 = shadow_root1.find_element(By.CSS_SELECTOR, 'book-input-decorator')
    shadow_root5 = driver.execute_script('return arguments[0].shadowRoot', shadow_host5)
    
    shadow_content = shadow_host5.find_element(By.CSS_SELECTOR, '#input')
    shadow_content.send_keys("Twilight")
    

    This is the result:

    enter image description here