polymer: template doesn't work inside svg elements

For example:

    <dom-module id="test-element">
      <template>
        <svg class="clock" version="1.1" xmlns="http://www.w3.org/2000/svg">
          <template is="dom-repeat" items="{{positions}}" as="cx">
            <ellipse r="25" cx$="{{cx}}" cy="25" stroke="black"></ellipse> 
          </template>
        </svg>
      </template>
      <script>
        Polymer({
          is: 'test-element',
          ready: function() {
            this.positions = [0, 100, 200, 300];
          },
        });
      </script>
    </dom-module>

This produces the following JS error:

Uncaught TypeError: Cannot read property 'nodeType' of undefined

The problem seems to be that the dom template object doesn’t have a content property. This worked fine in Polymer 0.5.

Demo here: http://jsbin.com/wiqowo/31/edit?html,output

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 11
  • Comments: 41 (5 by maintainers)

Most upvoted comments

Provided a workaround for now, tested in FF/Safari/IE, bundled/unbundled. https://github.com/garryyao/polymer-svg-template

We have a proposal for a core solution:

Detect if a <template> is inside an element that is in a SVG namespace (if it has namespaceURI which is SVG), then swap its contents into an svg element, do the parsing and then swap it back. This should be in parse-template.

The way to handle the transformation, is to expose a function Polymer.svg which transforms a tagged string into the corresponding svg namespace and returns the corrected template.

This whole process ensures that the elements are parsed in the correct namespace, to make sure the actual nodes are created. Then once the parsing has completed, we can swap the correctly constructed nodes back into the template.

We will be working on this issue this week to hopefully finally fix svg compatibility with templates + custom elements.

+1 for native implementation

Although there is already a pull-request #3372 that could fix the namespace issue, here would be a work-around, that would build up the svg-node recursivly node by node.

For that there could be a container-element:

<dom-module id="svg-container">
  <template>
    <svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>
    <content select="svg-component"></content>
  </template>

  <script>
    Polymer({
      is: 'svg-container',

      attached: function() {
        for (var i = 0; i < this.attributes.length; i++) {
          this.$.svg.setAttribute(Polymer.CaseMap.dashToCamelCase(this.attributes[i].name), this.attributes[i].value);
        }
      }
    });
  </script>
</dom-module>

and a element as svg-component, that creates the kind of svg-element, to be attached:

<dom-module id="svg-component">
  <template>
    <content select="svg-component"></content>
  </template>
  <script>
    SvgComponent = Polymer({
      is: 'svg-component',

      _node: {},
      _namespace: 'http://www.w3.org/2000/svg',

      get rootElement () {
        if (this.parentNode.nodeName === 'SVG-CONTAINER')
          return this.parentNode.$.svg;
        else
          return this.parentNode._node;
      },

      factoryImpl: function(attributes) {
        for (var key in attributes)
          this.setAttribute(key, attributes[key]);
      },

      attached: function() {
        var is = this.attributes.is.value;

        if (is !== undefined) {
          this._node = document.createElementNS(this._namespace, is);
          for (var i = 0; i < this.attributes.length; i++) {
            if (this.attributes[i].name !== 'is')
              this._node.setAttribute(this.attributes[i].name, this.attributes[i].value);
          }
          this.rootElement.appendChild(this._node);
        }
      }
    });
  </script>
</dom-module>

Property binding would have to be formally and also event-binding, but you could use that to do it automatically. It would definitely be better, that SVG namespace is implemented in Polymer as an option for registration of new elements .

And in a test: plunker

It looks like some code didn’t make it over from 0.5 that explicitly addressed this issue. Template elements with a <svg> parent have the same namespaceURI (http://www.w3.org/2000/svg), so the browser doesn’t implement HTMLTemplateElement. The bootstrap code in 0.5 basically transplanted all child nodes of any svg-namespaced template into a new html-namespaced template, and then inserted that node back in its place.

If anyone else is looking for a workaround, you can put this snippet at the top of your element’s <script> tag (it must be evaluated before call to Polymer()). It basically performs the same operation as it was done it 0.5. note: this snippet only works in chrome atm – not sure yet how to get firefox working