Search code examples
javascripthtmlcsspolymerweb-component

Best way to deal with polymer element inheritance


Given an element like:

<polymer-element name="custom-element">
    <template>
        <style>
            #container {
                color: red;
            }
        </style>
        <div id="container" on-click="{{clickContainer}}">
            ... lots of other stuff here ...
        </div>
    </template>
    <script>
        Polymer('custom-element', {
            clickContainer: function() {

            }
        });
    </script>
</polymer-element>

I'd like to have another element that wraps the first:

<polymer-element name="my-custom-element" extends="custom-element">
    <!-- extra styling -->
    <script>
        Polymer('my-custom-element', {
            clickContainer: function() {
                this.super();
            }
        });
    </script>
</polymer-element>

My problems:

  • What's the best way to specify additional styling ?
  • Can I wrap the base element in additional markup (like another container) ?
  • Can I select elements from the base element ? Something like <content select=".stuff"> but for the base's shadow markup.

Solution

    • What's the best way to specify additional styling ?
    1. Put a template inside the subclass (my-custom-element), as usual.
    2. Include a <shadow></shadow> element where you want the superclass template to appear.
    3. Put a style tag into the new template.
    4. To style elements that come from the superclass template, use a selector like this:

    :host::shadow .someclass { ... }

    See example below.

    • Can I wrap the base element in additional markup (like another container) ?

    Yes, you can put whatever markup you want around the <shadow></shadow>.

    <div>
      <shadow></shadow>
    </div>
    
    • Can I select elements from the base element? Something like <content select=".stuff"> but for the base's shadow markup.

    No. You cannot project like that (it's the reverse direction from all other projections).

    If you really want to cherry-pick nodes out of the older shadow-root, this can be done in code by pulling nodes directly out of this.shadowRoot.olderShadowRoot. But this can be tricky because the superclass may have expectations about the structure.

    Example code:

    <polymer-element name="my-custom-element" extends="custom-element">
    <template>
    
      <style>
          /* note that :host::shadow rules apply 
             to all shadow-roots in this element,
             including this one */
          :host::shadow #container { 
            color: blue;
          }
          :host {
            /* older shadow-roots can inherit inheritable 
               styles like font-family */
            font-family: sans-serif;
          }
      </style>
      <p>
        <shadow></shadow>
      </p>
    
    </template>
    <script>
    
      Polymer('my-custom-element', {
        clickContainer: function() {
          this.super();
        }
      });
    
    </script>
    </polymer-element>
    

    ProTips:

    • olderShadowRoot will exist whether or not you include the <shadow></shadow> tag, but it will not be part of the rendered DOM unless you do.
    • to prevent olderShadowRoot(s) from being created you can override parseDeclarations (source). Any of parseDeclarations, parseDeclaration, fetchTemplate can be overidden for various effects.