9

I see examples of using SVG elements within a custom element (such as here), but so far I haven't been able to figure out how to define a custom element to go inside an SVG element.

I've tried the following, and while the template content does appear in the web inspector, the circle doesn't appear visually.

<polymer-element name=my-element noscript>
  <template>
    <circle cx=10 cy=10 r=5 />
  </template>
</polymer-element>

<svg>
    <my-element />
</svg>

Is there a trick to getting custom elements to work within SVG elements?

exupero
  • 9,136
  • 8
  • 47
  • 63

3 Answers3

17

Unfortunately, you can't do this. Elements within the SVG namespace need to be within <svg>. Creating <my-element> creates a custom element that inherits from HTMLElement.

You can however, include <svg> in a custom element: http://jsbin.com/EXOWUFu/2/edit

Also, don't forget that custom elements cannot be self closing. So in your example, <my-element /> -> <my-element></<my-element>. This is because the HTML spec only allows a few elements to be self-closing.

Update

Turns out you can embed a custom element inside <svg> using <foreignObject>.

Demo: http://jsbin.com/hareyowi/1/edit

<foreignObject width="100" height="100">
  <x-foo></x-foo>
</foreignObject>
ebidel
  • 23,921
  • 3
  • 63
  • 76
  • 1
    So just to be clear: Polymer/Web Components can't be used to create inline SVG trees? At all? – Rudolph Gottesheim Apr 23 '14 at 14:45
  • My example creates an svg tree dynamically using Polymer. What you can't do is inline HTML in SVG like you're doing. That's generally true and has nothing to do with web components. – ebidel Apr 23 '14 at 21:25
  • It would be great if polymer supported this somehow. – mikea Jun 10 '14 at 04:05
  • See my update. `` is hacky, but works – ebidel Jun 11 '14 at 23:38
  • About the update: That's *still* not what most people would expect. The example is technically 4 different SVG documents laid out on top of one another. The truth is that Web Components, and subsequently Polymer, don't work fully with other namespaces just yet. – Ben Lesh Jul 28 '14 at 15:50
  • @BenLesh You said it doesn't work just yet, how do you know that. If its on the roadmap when will it start working. – user568109 Nov 03 '14 at 11:09
  • I know they don't work because the `` tag is in a different namespace than SVG. So a template like `` will simply not work, because `` will be an `SVGElement` not an `HTMLContextElement`. It's one of many reasons why Web Components don't work well with SVG. – Ben Lesh Nov 04 '14 at 05:15
  • @BenLesh I was more keen on not yet part. Is there any work in progress. – user568109 Nov 18 '14 at 11:47
  • I honestly don't know. Considering the number of issues SVG faces when trying to use Web Components, namespaces issues, createShadowRoot existing but throwing errors, problems registering elements that inherit from SVGElement, etc. ... I'd be really surprised if it works as of this comment, or if it's even being worked on. – Ben Lesh Nov 18 '14 at 17:28
  • @BenLesh Hey the fix is in for canary and is awaiting stable release. The reported bug: https://code.google.com/p/chromium/issues/detail?id=416217 – user568109 Nov 26 '14 at 09:55
9

The Polymer FAQ shows that a template can be used within an <svg> element:

<svg>
    <template repeat="{{l in lights}}">
        <circle cx="100" cy="{{l.cy}}" r="50" fill="{{l.color}}"/>
    </template>
</svg>

https://www.polymer-project.org/0.5/resources/faq.html#templateinsvg

Indeed this works in Polymer 0.5! But it does not work in Polymer 0.9 or 1.0.

In changed Polymer 1.0 syntax it looks like this:

<svg>
    <template is="dom-repeat" items="{{lights}}">
        <circle cx="100" cy$="{{item.cy}}" r="50" fill$="{{item.color}}"/>
    </template>
</svg>

The element is not rendered and the browser console shows:

TypeError: node is undefined   in polymer.html:28

The error refers to Polymer's _parseNodeAnnotations() function.

That the FAQ example works in 0.5 apparently shows there is no reason why SVG should not work in Web Components or Polymer in principle. This let me hope the Polymer team will fix that soon.

Jörg Richter
  • 667
  • 7
  • 8
2

According to this open ticket (https://github.com/Polymer/polymer/issues/1976), you actually CAN have a template element nested inside an svg element. The fix I will link to below is a port of the code from 0.5. The issue is described in detail here and in the open ticket, so no need to go over it again.

Important:

If you would like to indicate to Google and the polymer team that this is indeed an important feature, please make sure you leave a comment on the ticket.

  • It does give the intended functionality back in polymer 1.0.
  • It is required to be included at the top of the .js file of each web component which may use it.
  • It also assumes a specific html structure (which you will see below).
  • It only works in chrome right now.

Snippet: https://gist.github.com/bendavis78/15528ca2f501c44f2fa4

foo-svg.css

foo-svg svg {
    border: 2px solid yellowgreen;
    fill: transparent;
    height: 400px;
    width: 400px;
}

foo-svg.html

<dom-module id="foo-svg">
<link rel="stylesheet" href="foo-svg.css"/>

<template>
    <svg class="clock" version="1.1" xmlns="http://www.w3.org/2000/svg">
        <template is="dom-repeat" items="{{positions}}" as="cx">
            <circle cx$="{{cx}}" cy="25" r="25" stroke="black" />
        </template>
    </svg>
</template>
<script src="foo-svg.js"></script>

foo-svg.js

(function () {
  // START HACK
  var doc = document.currentScript.ownerDocument;
  var root = doc.querySelector('dom-module > template').content;
  var templates = root.querySelectorAll('svg template');
  var el, template, attribs, attrib, count, child, content;
  for (var i=0; i<templates.length; i++) {
    el = templates[i];
    template = el.ownerDocument.createElement('template');
    el.parentNode.insertBefore(template, el);
    attribs = el.attributes;
    count = attribs.length;
    while (count-- > 0) {
      attrib = attribs[count];
      template.setAttribute(attrib.name, attrib.value);
      el.removeAttribute(attrib.name);
    }
    el.parentNode.removeChild(el);
    content = template.content;
    while ((child = el.firstChild)) {
      content.appendChild(child);
    }
  }
  // END HACK

  Polymer({
    is: 'foo-svg',

    properties: {
      paths: {
        type: Array,
        value: []
      }
    },

    ready () {
      this.positions = [0, 100, 200, 300];
    }
  });
})();
deadbabykitten
  • 159
  • 3
  • 14