css-selectorsdartpolymerdart-polymershadow-dom

How to query elements within shadow DOM from outside in Dart?


How can I select nodes within shadow DOM? Consider the following example:

structure of "unshadowed" DOM

<app-element>
  #shadow-root
    <h2></h2>
    <content>
      #outside shadow
      <h2></h2>
    </content>
    <ui-button>
      #shadow-root
        <h2></h2>
  </ui-button>
</app-element>

index.html

<body>
<app-element>
  <!-- OK: querySelect('app-element').querySelect('h2') -->
  <!-- OK: querySelect('app-element h2') -->
  <!-- There is no problem to select it -->
  <h2>app-element > content > h2</h2>
</app-element>
</body>

templates.html

<polymer-element name="ui-button" noscript>
  <template>
    <!-- FAIL: querySelect('app-element::shadow ui-button::shadow h2') -->
    <h2>app-element > ui-button > h2</h2>
  </template>
</polymer-element>

<polymer-element name="app-element" noscript>
  <template>
    <!-- FAIL: querySelect('app-element::shadow').querySelect('h2') -->
    <!-- FAIL: querySelect('app-element::shadow h2') -->
    <!-- FAIL: querySelect('app-element').shadowRoot.querySelect('h2') -->
    <h2>app-element > h2</h2>
    <content></content>
    <ui-button></ui-button>
  </template>
</polymer-element>

In comments like "OK: querySelect()" I show selectors I've tried to run from outside any shadowed DOM.

I've already read the following article: http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/?redirect_from_locale=ru and based on the fact that it was said in the article, query like: document.querySelector('app-element::shadow h2'); in JS should work as expected. However in Dart it doesn't work.

What do I wrong?


Solution

  • Outdated

    ShadowDom was changed significantly since I posted this answer, this is why this answer doesn't apply anymore.

    Original Answer

    If you use a custom main, ensure that Polymer is properly initialized before you try to interact with your Polymer elements (see how to implement a main function in polymer apps for more details).

    I usually suggest to avoid a custom main and create an app-element (or whatever name you prefer) and put your initialization code into attached (ensure to call super.attached();) or in ready() (doesn't need the super call).

    It seems in this case it's not in the shadow DOM but a child.

    This should work:

    querySelector('h2');
    

    It's only in the shadow DOM when it is within your elements <template>...</template> not when you wrap it in the tag of your custom element.

    <polymer-element name="some-element">
      <template>
        <!-- this becomes the shadow DOM -->
        <content>
         <!-- 
           what gets captureD by the content element becomes a child or some-element
           -->
         </content>
      </template>
    </polymer-element>
    
    <body>
      <some-element>
        <!-- these elements here are captured by the 
             content tag and become children of some-element -->
        <div>some text</div>
      </some-element>
    </body>
    

    If you want to search

    inside the shadow DOM of the current element

    shadowRoot.querySelect('h2');
    

    inside the shadow DOM of an element inside the shadow DOM

    shadowRoot.querySelector('* /deep/ h2');
    shadowRoot.querySelector('ui-button::shadow h2');
    

    from outside the current element

    import 'dart:html' as dom;
    ...
    dom.querySelector('* /deep/ h2');
    // or (only in the shadow DOM of <app-element>)
    dom.querySelector('app-element::shadow h2');
    dom.querySelector('app-element::shadow ui-button::shadow h2');
    // or (arbitrary depth)
    dom.querySelector('app-element /deep/ h2');