1

I have an application made with Polymer that I need to automate. The problem is it has a lot of Shadow DOMs.

I'm using FluentAutomation that only knows to work with CSS selectors and some jQuery.

Is there a way to identify the elements inside a Shadow DOMs using CSS selectors?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Adrian M
  • 103
  • 7
  • "a lot of Shadow DOMs" there is only one Shadow DOM. Can you clarify what you mean? – TylerH Mar 07 '18 at 14:31
  • I'm not familiar with Polymer / shadow DOMs. I just saw multiple ones when navigating to my desired elements in the DOM. – Adrian M Mar 08 '18 at 15:02

2 Answers2

2

do you mean to select a dom node that is part of a shadow dom?

as there I no selector that pierces shadow dom you will have to provide the full path to a dom node. Example Source:

<my-app>
  #shadow-root
    <h3 part="header">My App Header</h3>
    <my-dialog>
      #shadow-root
        <p part="header">My Dialog Header</p>
        <my-alert>
          #shadow-root
            <span part="header">My Alert Header</span>
        </my-alert>
        <my-alert>
          #shadow-root
            <span part="header">My Alert Header</span>
        </my-alert>
    </my-dialog>
</my-app>

To select the first my-alert you would need to do

document.querySelector('my-app').shadowRoot.querySelector('my-dialog').shadowRoot.querySelector('my-alert');

if you have ids like so

<my-app id="app">
  #shadow-root
    <h3 part="header">My App Header</h3>
    <my-dialog id="dialog">
      #shadow-root
        <p part="header">My Dialog Header</p>
        <my-alert id="alert1">
          #shadow-root
            <span part="header">My Alert Header</span>
        </my-alert>
        <my-alert id="alert2">
          #shadow-root
            <span part="header">My Alert Header</span>
        </my-alert>
    </my-dialog>
</my-app>

You can use a more optimized path.

document.querySelector('my-app').$.dialog.$.alert1

PS: if you are interested there is a selector in the works that lets you pierce the shadow dom for certain “exported” dom parts…
Spec: https://tabatkins.github.io/specs/css-shadow-parts/
Blog Post: https://meowni.ca/posts/part-theme-explainer/

daKmoR
  • 1,774
  • 12
  • 24
  • Thanks for your answer, I ended up executing javascript like the solution you provided. – Adrian M Mar 08 '18 at 14:43
  • My remaining problem is that I am unable to identify the element by the CSS path or using jquery for example E:contains('t') , because of that shadow dome. That limits my actions from the FluentAutomation framework, that has actions like Click,Hover,Type,Find, taking as parameter a CSS selector... – Adrian M Mar 08 '18 at 14:58
  • 1
    can you by any chance provide an element instead of a css selector? (maybe some additional options?) if not it's still probably easier to make a merge request for that option... then to create a jquery plugin that can use a special "marker" to also traverse shadow doms... (if that is even easily possible?) – daKmoR Mar 08 '18 at 22:05
  • I dev colleague helped me by creating a script that searches through all shadow domes and returns the element : – Adrian M Nov 20 '18 at 10:17
1

I ended up using a script provided by a colleague that searches the elements in the shadow domes :

    (() => {
            if (window.DeepShadowDom !== undefined) {
                return;
            }

            const findAll = (selector, root = document) => {
                let elements = [ ];

                Array.prototype.push.apply(elements, root.querySelectorAll(selector));

                Array.prototype.slice.call(root.querySelectorAll('*'))
                    .filter(e => e.shadowRoot !== null)
                    .forEach((currentElement, index, array) => {

                        let candidates = findAll(selector, currentElement.shadowRoot);

                        Array.prototype.push.apply(elements, candidates);
                    });

                return elements;
            };

            const find = (selector, root = document) => {
                let elements = findAll(selector, document);

                if (elements.length > 1) {
                    throw new Error(`Multiple results returned by selector '${selector}'`);
                }

                return elements[0];
            };

            window.DeepShadowDom = { find, findAll };
        })();

the element can be then found by selecting DeepshadowDom.Find(selector)

Adrian M
  • 103
  • 7