dman
dman

Reputation: 11064

How to get child element from template element?

dom-change event object e in Polymer.dom(e).localTarget gives me <template is="dom-if" as it should. But how do I access the child <div id="uploadedImage1"? firstChild and firstElementChild are null.

  <template is="dom-if" if="[[uploadedImage1]]">
    <div id="uploadedImage1" class="row-image horizontal layout">
      foo
    </div>
  </template>

I thought maybe I need to do another query off of the localTarget object but no success:

    this.$['images-container'].addEventListener('dom-change', (e)=> {
      var bob = Polymer.dom(e).localTarget;
      var sue = Polymer.dom(bob).querySelector('div')
      console.log(sue);
    });

I also tried these to no success:

    this.$['images-container'].addEventListener('dom-change', (e)=> {
      var bob = Polymer.dom(e).localTarget;
      var sue = this.queryEffectiveChildren(bob);
      console.log(sue);
    });

    this.$['images-container'].addEventListener('dom-change', (e)=> {
      var bob = Polymer.dom(e).localTarget;
      var sue = bob.getEffectiveChildNodes();
      console.log(sue);
    });
  }

From children length documentation example, even Polymer.dom(this).children.length returned 0:

<dom-module id="image-uploader">
  <template>
    <style include="iron-flex iron-flex-factors iron-flex-alignment">


    <div id="images-container" class="horizontal layout center wrap">

      <div hidden="[[uploadedImage1]]" class="layout vertical center-center">
        <div class="row-image horizontal layout">
          <input
            type="file"
            name="file"
            data-uploaded="uploadedImage1"
            id="image1"
            accept="image/jpeg"
            class="inputfile" />
          <label
            for="image1"
            id="image1"
            class="image-show horizontal layout center center-justified">
            <iron-icon icon="add" prefix></iron-icon>
            Upload Image
          </label>
        </div>
        <i>*Main Image</i>
      </div>

      <template is="dom-if" id="foo" if="[[uploadedImage1]]">
        <div id="uploadedImage1">
          foo
        </div>
      </template>

    </div>

  </template>

  <script>
    Polymer({
      is: 'image-uploader',
      properties: {
        uploadedImage1: {
          type: Boolean,
          value: false
        }
      },
      ready: function(e) {
          console.log(Polymer.dom(this).children.length);

Upvotes: 4

Views: 4956

Answers (2)

KenZ
KenZ

Reputation: 45

Polymer.dom(e).localTarget.content gives me access to the documentFragment, at least it works on Chrome.

Upvotes: 0

brianreeve
brianreeve

Reputation: 305

There are a few problems I see, so I'll talk through them and give a working example at the end.

Understanding template

The <div id="uploadedImage1" ... element can and will never a child of the dom-if template as far as your component's local DOM is concerned.

Per the W3C Living Document, the template tag defines a chunk of markup (called a DocumentFragment) that doesn't physically exist in the main Document, and can be cloned and placed into the Document at runtime. It does not contain any of the rendered markup, so you can not ask the template about what it has been used for in the rest of the document.

To illustrate with a real-world example, consider a rubber stamp (the template) and a sheet of paper (the document). If you ink the stamp and press it onto the paper one or more times, you can't look at the rubber stamp and know what, where, or how many times it has stamped it's image on the paper. To get that information you have to look at the paper itself.

If you add a container element around the template, and log it to the console, you'll see pretty clearly what this looks like from the Document perspective:

<div id="container" class="style-scope my-el">
  <div id="uploadedImage1" class="style-scope my-el">
    foo
  </div>
  <template is="dom-if" id="foo" class="style-scope my-el"></template>
</div>

Understanding the dom-change event context

You had a reasonable idea to use Polymer's DOM method to find the element you want, however in all of your attempts, you start with context of the event target, which is the template.

Again, the template doesn't actually contain the element you want to find, so you will never locate it here.

Understanding Polymer's DOM Basics

The documentation you referenced is for accessing Light DOM children stamped to a <container>. In your case, you are not stamping any light DOM children into your elements shadow DOM. You are using dom-if templates to conditionally show/hide content based on state.


The Solution

You can achieve what you want by simply asking your component to find the element in your component's local DOM by using the $$ shorthand method. One thing to consider is for performance reasons, Polymer stamps the contents of a dom-if template once the condition becomes truthy, then subsequently uses CSS to show and hide it, so it may be such that the template is in the DOM but not shown.

Here is an excerpt from a working example (see fiddle) which achieves your goal by giving the dom-change listener access to the component object (via bind()) so it can a) access the component's local DOM to find the element and b) consider whether or not the dom-if condition is truthy.

this.$.foo.addEventListener('dom-change', function() {
  if (this.prop1) {
    console.log(this.$$('#uploadedImage1'));
  } else {
   console.log(null);
  }
}.bind(this));

Upvotes: 7

Related Questions