user3587590 user3587590 - 5 months ago 21
Javascript Question

Accessing a div inside of a Polymer element template

I'm trying to use core-animation on a div, to animate its position. To do that, i have to select it with document.getElementById().
The problem is, i have a rather complex structure in my index.html file and i can't find a way to select that div.

Here's index.html structure (i need to select #el): http://i.imgur.com/phWyArO.jpg

My index.html file:

<template is="auto-binding" id="t">

<!-- Route controller. -->
<flatiron-director route="{{route}}" autoHash></flatiron-director>

<!-- Keyboard nav controller. -->
<core-a11y-keys id="keys" target="{{parentElement}}"
keys="up down left right space space+shift"
on-keys-pressed="{{keyHandler}}"></core-a11y-keys>
<core-header-panel>
<core-toolbar class="panel-personal" hidden?="{{shortView}}">
...
</core-toolbar>
<core-toolbar class="panel-nav">
<paper-tabs valueattr="hash" selected="{{route}}" selectedModel="{{selectedPage}}"
on-core-select="{{menuItemSelected}}" link flex style="width:100%; height:100%;" id="tabs">
<template repeat="{{page, i in pages}}">
<paper-tab><a href="#{{page.hash}}">{{page.date_month}}<br/><small>{{page.date_year}}</small></a></paper-tab>
</template>
</paper-tabs>
</core-toolbar>
<nav class="menu">
...
</nav>


<div horizontal layout fit>

<core-animated-pages id="pages" selected="{{route}}" valueattr="hash"
transitions="slide-from-right"
on-tap="{{cyclePages}}" flex self-stretch>
<template repeat="{{page, i in pages}}">
<section hash="{{page.hash}}" class="card-wrapper">
<div flex fit>
<div class="card-container" vertical layout fit >
<h1>{{page.name}}</h1>
<h2>{{page.category}}</h2>
<paper-button raised class="project_button"><a href="{{page.link}}" target="_blank"><core-icon icon="social:share" ></core-icon> visit project</a> </paper-button>
</div>
<div center-justified layout fit class="card-content">
<div>
<h4>Project description</h4>
<p>{{page.desc}}
</p>
</div>
</div>
<div class="card-background" id="el" fit></div>
</div>
</section>
</template>
</core-animated-pages>
</div>

</core-header-panel>
</template>


How can i select that #el div?

Answer

If your element is inside a template, then it does not belong to the DOM but the shadow DOM. As a complement, in the Web Components site there is an article on the topic and Rob Dodson wrote a detailed article about the basics of shadow DOM.

Edit : Polymer 1.0 introduced a specific concept, Shady DOM...

I admit the relationship between a polymer element, multiple templates and shadow DOM is not really obvious to me. At first glance, a shadow DOM is associated to a template. A few explanations are available in that other article by Rob Dodson about templates repetition. It seems with repetitions things become subtle: here you have a single shadow DOM and the ID becomes pointless. The worse is that I guess you have nested shadow DOMs because of the nested templates. What is not clear at all to me is here: what about visibility in such a context ?

Edit : more informations about visibility are available in that later exchange...

As a conclusion (in my understanding), because of the template repetition you could have multiple identical IDs even inside the repeated template shadow DOM. The valid strategy would be to use a class for selection instead of an ID.

Javascript is associated to a Polymer element with a method call. To select via Javascript in the shadow DOM, you should use the querySelector. The solution you suggested with document.getElementById() is valid for the DOM. The point is about nested templates. You have a hint with another exchange on the topic. A solution is suggested with this.shadowRoot.querySelectorAll(...) but an issue in the Polymer project covers the point: this could work but probably not a good practice. The details in the Polymer documentation about node finding are here: in that article there is a sample looking like your problem.

According to the Polymer doc, when you call the Polymer element method, assuming you want the selection in the ready event and a container ID assigned in your nesting template, this should look something like this:

<polymer-element name="my-element"...>
  <template ...>
    <div id="container">
      <template ...>
        ...
        <div class="el">
        </div>
        ...
      </template>
    </div>
  </template>
  <script>
Polymer('my-element', {
  ready: function() {
    this.$.container.querySelector('.el');
  }
});
  </script>
</polymer-element>