Search code examples
javascriptpolymerweb-component

Polymer Elements ShadowDOM child nodes - Setting style attributes dynamically


I'd like some ideas on how to set a font-size to a <paper-textarea>, dynamically (i.e set font-size from JS instead of CSS).

  • There's no default attribute in the docs for font-size.
  • It looks like it can only be set via a mixin (which cannot be changed via JS).

For example, <paper-textarea>'s DOM tree looks something like this:

  • <paper-textarea>
    • ShadowDOM begins
    • <div>
    • <div>
    • <div>
    • <textarea> <-- This is the actual textarea

Is there any way to target the child-node in it's shadowDOM via JS and set it's style directly?

An example, which of course doesn't work.

  • I bind property fontSize to the <paper-textarea>
  • Since there's no attribute fontSize to set for this particular element, it doesn't work.

<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<link href="paper-input/paper-textarea.html" rel="import">
<link href="paper-button/paper-button.html" rel="import">

<dom-module id="x-example">
  <style>
    :host {
      display: block;
      max-width: 320px;
    }
  </style>
  <template>
      <paper-textarea label="Textarea" value="{{inputData}}" font-size="{{fontSize}}"></paper-textarea>
    <paper-button class="primary" on-tap="incrementFontSize">Increment Font Size</paper-button>
  </template>
<script>
  HTMLImports.whenReady(function() {
    "use strict";

    Polymer({

      is: "x-example",
      properties: {
      	inputData: {
          type: String,
          value: "Hello World"
        },
        
        fontSize: {
          type: Number,
          value: 16
        }
      },
      
      incrementFontSize: function() {
        this.set("fontSize", this.fontSize + 4);  
      }

    });
  });
</script>

</dom-module>

<x-example></x-example>

Ideally, I would set up a fontSize observer and imperatively target the <paper-textarea>'s child node to set it's style when fontSize changes.

How can I target those nodes?

Note: I'd prefer an "official" way, if it exists. The Polymer spec changes often and flimsy hacks would tend to break when updating the library.


Solution

  • Here is one way of doing it:

    <dom-module id="dynamic-style">
     <template>
      <style>
       :host{
        --myfont:10px; 
       }
      paper-textarea{
        --paper-input-container-input:{
         font-size:var(--my-font);
       };
     }
     </style>
     <paper-textarea on-tap="updateStyle"></paper-textarea>
    </template>
    </dom-module>
    <script>
     Polymer({
      is:"dynamic-style",
       properties:{
        fontSize:{
         type:Number,
         value:10
        }
       },
       updateStyle:function(){
        var style={'--my-font':this.computeFont()+'px'}
        this.updateStyles(style);
       },
       computeFont:function(){
        return this.fontSize+=4;
       }
     })
    </script>
    

    Dynamic styling is currently an issue with Polymer and as per recommendation updateStyles is the only method through which it will work.

    And here's a working snippet based on your example:

    <base href="https://polygit.org/components/">
    <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
    <link href="polymer/polymer.html" rel="import">
    <link href="paper-input/paper-textarea.html" rel="import">
    <link href="paper-button/paper-button.html" rel="import">
    
    <dom-module id="x-example">
      <style is="custom-style">
        :host {
          display: block;
          max-width: 320px;
        }
        :root {
          --font-size: 12px;
          --paper-input-container-input: {
            font-size: var(--font-size);
          }
        }
      </style>
      <template>
        <paper-textarea label="Textarea" value="{{inputData}}"></paper-textarea>
        <paper-button class="primary" on-tap="incrementFontSize">Increment Font Size</paper-button>
      </template>
      <script>
        HTMLImports.whenReady(function() {
          "use strict";
    
          Polymer({
    
            is: "x-example",
            properties: {
              inputData: {
                type: String,
                value: "Hello World"
              },
    
              fontSize: {
                type: Number,
                value: 16
              }
            },
    
            incrementFontSize: function() {
              var style = {
                '--font-size': this.computeFont() + 'px'
              }
              this.updateStyles(style);
            },
    
            computeFont: function() {
              return this.fontSize += 4;
            }
    
          });
        });
      </script>
    
    </dom-module>
    
    <x-example></x-example>