How to automate shadow DOM elements using selenium?
There is a very good plugin that can be used with selenium project shadow-automation-selenium. It helps in writing much better, readable and maintainable code. Using this you can access multi level of shadow DOM (upto 4 levels ) . This uses simple css selector to identify elements.
WebElement findElement(String cssSelector)
: use this method if want single element from DOM
List<WebElement> findElements(String cssSelector)
: use this if you want to find all elements from DOM
WebElement findElements(WebElement parent, String cssSelector)
: use this if you want to find a single elements from parent object DOM
List<WebElement> findElements(WebElement parent, String cssSelector)
: use this if you want to find all elements from parent object DOM
WebElement getShadowElement(WebElement parent,String selector)
: use this if you want to find a single element from parent DOM
List<WebElement> getAllShadowElement(WebElement parent,String selector)
: use this if you want to find all elements from parent DOM
boolean isVisible(WebElement element)
: use this if you want to find visibility of element
boolean isChecked(WebElement element)
: use this if you want to check if checkbox is selected
boolean isDisabled(WebElement element)
: use this if you want to check if element is disabled
String getAttribute(WebElement element,String attribute)
: use this if you want to get attribute like aria-selected and other custom attributes of elements.
void selectCheckbox(String label)
: use this to select checkbox element using label.
void selectCheckbox(WebElement parentElement, String label)
: use this to select checkbox element using label.
void selectRadio(String label)
: use this to select radio element using label.
void selectRadio(WebElement parentElement, String label)
: use this to select radio element from parent DOM using label.
void selectDropdown(String label)
: use this to select dropdown list item using label (use this if only one dropdown is present or loaded on UI).
void selectDropdown(WebElement parentElement, String label)
: use this to select dropdown list item from parent DOM using label.
How to use this plugin:
You will have to dependency in your project.
Maven
<dependency>
<groupId>io.github.sukgu</groupId>
<artifactId>automation</artifactId>
<version>0.0.4</version>
<dependency>
for html tag that resides under a shadow-root dom element
<properties-page id="settingsPage">
<textarea id="textarea">
</properties-page>
You can use this code in your framework to grab the textarea element Object.
import io.github.sukgu.*;
Shadow shadow = new Shadow(driver);
WebElement element = shadow.findElement("properties-page#settingsPage>textarea#textarea");
String text = element.getText();
How to interact with the elements within #shadow-root (open) while Clearing Browsing Data of Chrome Browser using cssSelector
If you are trying to get 'Clear Data' element then you can use the below js to get the element and then perform.
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')
Here is the sample script.
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();
Edit 2: Explanation
Problem: Selenium does not provide explicit support to work with Shadow DOM elements, as they are not in the current dom. That's the reason why we will get NoSuchElementException
exception when try to access the elements in the shadow dom
.
Shadow DOM:
Note: We will be referring to the terms shown in the picture. So please go through the picture for better understanding.
Solution:
In order to work with shadow element first we have to find the shadow host
to which the shadow dom is attached. Here is the simple method to get the shadow root based on the shadowHost.
private static WebElement getShadowRoot(WebDriver driver,WebElement shadowHost) {
JavascriptExecutor js = (JavascriptExecutor) driver;
return (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
}
And then you can access the shadow tree element using the shadowRoot Element.
// 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"));
In order to simplify all the above steps created the below method.
public static WebElement getShadowElement(WebDriver driver,WebElement shadowHost, String cssOfShadowElement) {
WebElement shardowRoot = getShadowRoot(driver, shadowHost);
return shardowRoot.findElement(By.cssSelector(cssOfShadowElement));
}
Now you can get the shadowTree Element with single method call
WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS_Goes_here));
WebElement shadowTreeElement = getShadowElement(driver,shadowHost,"shadow_tree_element_css");
And perform the operations as usual like .click()
, .getText()
.
shadowTreeElement.click()
This Looks simple when you have only one level of shadow DOM. But here, in this case we have multiple levels of shadow doms. So we have to access the element by reaching each shadow host and root.
Below is the snippet using the methods that mentioned above (getShadowElement and 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();
You can achieve all the above steps in single js call as at mentioned at the beginning of the answer (added below just to reduce the confusion).
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')");
Screenshot:
How to access the html nested within multiple shadowRoot using Selenium and Python
The desired information interms of innerHTML is within multiple #shadow-root (open).
Solution
To extract the information you need to use shadowRoot.querySelectorAll()
and you can use the following Locator Strategy:
Code Block:
driver.get("https://www.powerlanguage.co.uk/wordle/")
time.sleep(1)
sends=driver.find_element(By.XPATH, "/html/body")
sends.click()
sends.send_keys("adieu")
sends.send_keys(Keys.ENTER)
inner_texts = [my_elem.get_attribute("outerHTML") for my_elem in driver.execute_script("""return document.querySelector('game-app').shadowRoot.querySelector('game-row').shadowRoot.querySelectorAll('game-tile[letter]')""")]
for inner_text in inner_texts:
print(inner_text)Console Output:
<game-tile letter="a" evaluation="absent" reveal=""></game-tile>
<game-tile letter="d" evaluation="absent"></game-tile>
<game-tile letter="i" evaluation="correct"></game-tile>
<game-tile letter="e" evaluation="absent"></game-tile>
<game-tile letter="u" evaluation="absent"></game-tile>
References
You can find a couple of relevant discussions in:
- Can't locate elments within shadow-root (open) using Python Selenium
- How to get past a cookie agreement page using Python and Selenium?
How to interact with Cookie pop within #shadow-root (open) using Selenium
The element OK is within #shadow-root (open).
Solution
To click on OK you have to use shadowRoot.querySelector()
and you can use the following Locator Strategy:
Code Block:
driver.get("https://www.immowelt.de/immobilienpreise")
time.sleep(5)
element = driver.execute_script("""return document.querySelector('#usercentrics-root').shadowRoot.querySelector("button[data-testid='uc-accept-all-button']")""")
element.click()
References
You can find a couple of relevant discussions in:
- How to handle the popup "Accepting all cookies" when the element is data-testid - Using Selenium in Python
- Can't locate elments within shadow-root (open) using Python Selenium
- How to get past a cookie agreement page using Python and Selenium?
Selenium | shadow root | Element input is not reachable by keyboard
You can use JavascriptExecutor
to set the values as well.
//inputFIELD.sendKeys("test");
((JavascriptExecutor)driver).executeScript("arguments[0].setAttribute('value', 'test')", inputButton);
or using actions chain:
new Actions(driver).moveToElement(inputButton).sendKeys("test").build().perform();
Related Topics
Why Does Sys.Exit() Not Exit When Called Inside a Thread in Python
How to Have Assignment in a Condition
Cannot Concatenate 'Str' and 'Float' Objects
Libxml Install Error Using Pip
Check If Two Unordered Lists Are Equal
Python Pandas Counting the Occurrences of a Specific Value
Check If a String in a Pandas Dataframe Column Is in a List of Strings
How to Create a Custom Activation Function with Keras
Python: How to Make the Ansi Escape Codes to Work Also in Windows
Why Do Some Functions Have Underscores "_" Before and After the Function Name
How to Disable Logging on the Standard Error Stream
How to Dynamically Create Derived Classes from a Base Class
How Does Perspective Transformation Work in Pil
Python Equivalent of Setinterval()
Does Python Have a Bitfield Type