2

While using Selenium Webdriver in C#, I get an exception when trying to select an element that exists under Shadow DOM.

The exception I am getting is: NoSuchElementException

How would you suggest to use Selenium with Shadow DOM?

Thanks,

Michal

img

Andrei Suvorkov
  • 5,559
  • 5
  • 22
  • 48
michal
  • 103
  • 1
  • 8

6 Answers6

4

Try to locate your element like this:

driver.FindElement(By.CssSelector('selector_otside_shadow_root /deep/ selector_inside_shadow_root')); 

in your case it would be:

driver.FindElement(By.CssSelector('app-home /deep/ #itemName1'));

You can test this approach in chrome://downloads/ link with this css_selector:

downloads-manager /deep/ downloads-item /deep/ [id=file-link]

in dev tools. As you can see there was needed to pass two shadow-root elements, so make sure that you have only one shadow-root element or use multiple /deep/ like in example above.

or you can use JavasciptExecutor like this:

IJavaScriptExecutor js = (IJavaScriptExecutor)_driver;
var element = js.ExecuteScript("return document.querySelector('selector_outside_shadow_root').shadowRoot.querySelector('selector_inside_shadow_root');");
  • Note: the first suggestion as far I know works only in Chrome, if you want a cross browser solution - use the second one.
Andrei Suvorkov
  • 5,559
  • 5
  • 22
  • 48
  • Unfortunately, The second option is not compatible with our selenium infrastructure. Perhaps you have any other suggestions? Thanks so much, – michal Jul 15 '18 at 09:38
  • Which browser are you using and show please the code you have tried for the first suggestion. – Andrei Suvorkov Jul 15 '18 at 09:39
  • chrome and IE. i tried: driver.FindElement(By.XPath("//app-home/deep/div[@id='itemName1']")).Click(); – michal Jul 15 '18 at 10:02
  • @michal I am sorry, didn't tested my first suggestion. So it works with css selectors. I am not sure about xPath, probably it is not possible. So I have edited my answer, please have a look and try. Also I have added an example link and example selector, so you can try it to go more in the details. – Andrei Suvorkov Jul 15 '18 at 10:22
  • I understand, Now it work good! do you know if it work in Internet Explorer also? Thanks so much! – michal Jul 15 '18 at 10:43
1

You can create a Method, that accepts the list of ShadowDom root locators, and builds js script to execute and get shadow element:

public static IWebElement GetElementFromShadowDom(this IWebDriver driver, params string[] selectors)
    {
        IJavaScriptExecutor js = (IJavaScriptExecutor)driver;

        var scriptString = "return document.querySelector";
        var selectorIndex = 0;
        var stopIndex = selectors.Length - 1;

        foreach (var selector in selectors)
        {
            var root = "('" + selector + "')";
            root += (selectorIndex != stopIndex && selectors.Length != 1) ? ".shadowRoot.querySelector" : null;                
            selectorIndex++;
            scriptString += root;
        }

        var webElement = (IWebElement)js.ExecuteScript(scriptString);
        return webElement;
    }
}
0

I had the same problem. I found some values in the

Inspect -> Properties -> value (it can be something else)

Try:

WebElement element = driver.findElement(By.cssSelector("div"));
System.out.println(element.getAttribute("value"));
Peyman Mohamadpour
  • 17,954
  • 24
  • 89
  • 100
Alex
  • 1
0

A very good repository having Selenium shadow DOM interactions written as Java Binding : https://github.com/sukgu/shadow-automation-selenium

This repo uses a bunch of JQueries to expand and identify elements in shadow dom.Please have a look.Should be easy to port it to C#

Anoop
  • 146
  • 1
  • 6
0

That gives an error Andre, I don't know what to do now: OpenQA.Selenium.JavaScriptException : javascript error: missing ) after argument list (Session info: MicrosoftEdge=104.0.1293.63)

changed it to:

var element = js.ExecuteScript("return document.querySelector('#shadow-root').shadowRoot.querySelector('#private-host');");

but that resulted in: OpenQA.Selenium.JavaScriptException : javascript error: Cannot read properties of null (reading 'shadowRoot')

GreenThus
  • 1
  • 1
0

Rules to find element inside Shadow DOM

  1. First find parent of Shadow Root element (also called Shadow Host) using normal Find Element method.
  2. Call the below helper methods to find the Shadow Root element by passing Shadow Host Element. Use below helper methods.
  3. In case of nested Shadow root. Keep repeating the above two steps. i.e. again find Shadow Host and then Shadow Root.
  4. If you have ID of the element. Then using the Shadow Root element. Find the element on which you want to perform action and perform desired action on it.
  5. If ID is not available - You can use any other identifier except XPath (XPath doesn't work in Shadow DOM).

You can also use SelectorsHub plugin to get the locator and sample code

Helper method 1

    /// <summary>
    /// Method 1 to fetch Shadow Root using Selenium's in build method
    /// </summary>
    /// <param name="shadowHostElement">IWebElement - Which is parent element of Shadow Root element. Also called Shadow Host</param>
    /// <returns>Shadow Root element in the form of ISearchContext</returns>
    public static ISearchContext GetShadowRootElement(IWebElement shadowHostElement)
    {
        ISearchContext shadowRootElement = shadowHostElement.GetShadowRoot();
        return shadowRootElement;
    }

Helper method 2

    /// <summary>
    /// Method 2 to fetch Shadow Root using Selenium's IJavaScriptExecutor
    /// </summary>
    /// <param name="driver">Instance of IWebDriver</param>
    /// <param name="shadowHostElement">IWebElement - Which is parent element of Shadow Root element. Also called Shadow Host</param>
    /// <returns>Shadow Root element in the form of ISearchContext</returns>
    public static ISearchContext GetShadowRootElementUsingJavaScript(IWebDriver driver, IWebElement shadowHostElement)
    {
        IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
        ISearchContext shadowRootElement = (ISearchContext)js.ExecuteScript("return arguments[0].shadowRoot", shadowHostElement);

        return shadowRootElement;
    }

Sample Code - Website with Shadow DOM

//1. Finding shadow host first - normally using FindElement            
IWebElement ShadowHostElement = Driver.FindElement(By.TagName("book-app"));
        
//2. Finding Shadow Root using Shadow Host element - use either one from below

ISearchContext shadowRoot = GetShadowRootElementUsingJavaScript(Driver, ShadowHostElement);
        ISearchContext shadowRoot = GetShadowRootElement(ShadowHostElement);

        IWebElement node = shadowRoot.FindElement(By.Id("input"));
        node.SendKeys("Something");
KR Akhil
  • 877
  • 3
  • 15
  • 32