Question

I'm writing a custom <figure>-like element for which I'd like to put a <figcaption> under the main content. The <figcaption> needs to include some content (text with potential formatting) from the child nodes of the custom element, for which I'm using <content select="span.caption"></content>. However, per the Shadow DOM spec, the <span> has already been distributed to the earlier <content></content> element that laid out the body of the custom element.

I've tried using <content select=":not(span.caption)"></content> to avoid distributing the caption, but that appears not to be a compound selector and matches nothing.

What's the recommended way to get the elements to render in the order I want?

Here's the code with the problem, which is also at http://jsbin.com/vizoqusu/3/edit:

<!DOCTYPE html>
<html>
<head>
  <script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.1.4/platform.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.1.4/polymer.js"></script>
  <meta charset="utf-8">
</head>
<body>
  <polymer-element name="my-figure" noscript>
    <template>
      <figure>
        <content></content>
        <figcaption>Figure 7: <content select="span.caption"></content></figcaption>
      </figure>
    </template>
  </polymer-element>

  <my-figure>
    Hello World!
    <span class="caption">The caption</span>
  </my-figure>
</body>
</html>
Was it helpful?

Solution

This actually does work today under ShadowDOM polyfill, with two caveats:

  1. :not only takes simple selectors as arguments, so you need to do something like *:not(.caption)
  2. If you use a selector at all, plain text nodes are ignored, so you have to wrap your other content in some kind of element.

Here is an example:

http://jsbin.com/vizoqusu/5/edit

  <polymer-element name="my-figure" noscript>
    <template>
      <figure>
        <content select="*:not(.caption)"></content>
        <figcaption>Figure 7: <content select="span.caption"></content></figcaption>
      </figure>
    </template>
  </polymer-element>

  <my-figure>
    <span>Hello World!</span>
    <span class="caption">The caption</span>
  </my-figure>

As for the native system, I used your use case to argue for inclusion of :not() into the specification, you can see it here:

https://www.w3.org/Bugs/Public/show_bug.cgi?id=24867

OTHER TIPS

I see a few options here:

You could use getDistributedNodes to first get the content of the element and then inject the text (Hello World) and caption at the right points as follows:

  <polymer-element name="my-figure">
    <template>
      <figure id="figureHolder">
        <content id="content"></content>
        <figcaption>Figure 7: <content id="caption" select="span.caption"></content></figcaption>
      </figure>
    </template>

    <script>
  Polymer('my-figure', {
    caption: '',
    ready: function() {
      var nodes = this.$.content.getDistributedNodes();
      this.$.caption.innerHTML = nodes[1].innerHTML;
      this.$.content.getDistributedNodes()[0].data = nodes[0].data;
      this.$.content.getDistributedNodes()[1].innerHTML = '';
    }
  });
      </script>
  </polymer-element>

  <my-figure>
    Hello World!
    <span class="caption">The caption</span>
  </my-figure>

You could move your caption into an attribute and this allows you to maintain a clean separation between the content of your figure and the caption itself:

  <polymer-element name="my-figure" attributes="caption" noscript>
    <template>
      <figure>
        <content></content>
        <figcaption>Figure 7: {{caption}} </figcaption>
      </figure>
    </template>
  </polymer-element>

  <my-figure caption="The caption">
    Hello World!
  </my-figure>

You could move your main content (e.g Hello World) into it's own span and add a class to allow targeting it from inside your template directly, so:

 <polymer-element name="my-figure" noscript>
    <template>
      <figure>
        <content select="span.hello"></content>
        <figcaption>Figure 7: 
          <content select="span.caption"></content></figcaption>
      </figure>
    </template>
  </polymer-element>

  <my-figure>
    <span class="hello">Hello World!</span>
    <span class="caption">The caption</span>
  </my-figure>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top