1
votes

Given the following html template:

<div class="Page">
    Hello <slot name="personName"></slot>. Your name is <slot name="personName"></slot>.
</div>

How is it possible (if at all) to fill both slots with one value using custom elements?

The below demo code will produce:

Hello Bob, Your name is .

Is this intended? Is this the wrong way of displaying a single value in multiple locations of a template?

let tmpl = document.createElement("template");
tmpl.innerHTML = `
  <div>
    Hello <slot name="personName"></slot>. Your name is <slot name="personName"></slot>.
  </div>
`;

class MyElement extends HTMLElement {
  constructor() {
    super();
    let shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.appendChild(tmpl.content.cloneNode(true));
  }
}

customElements.define("x-myelement", MyElement);
<x-myelement>
  <span slot="personName">Bob</span>
</x-myelement>
2
"message": "ReferenceError: customElements is not defined" in FF.connexo
@connexo Oh the joy of web standards :) there is limited support for web components in FF unfortunatelymonokh
document.registerElement from web components spec v0 probably works.connexo
it's the normal behavior. You'll need to duplicate the span element or use another solution to achieve what you want to do.Supersharp
@connexo document.registerElement is dead. Firefox will soon support the V1 spec. @Monokh's question is clearly about the V1 spec since it uses <slot> @Monokh I do not believe that there is plans to have more then one slot able to take the same source elements. At least not as part of V1Intervalia

2 Answers

2
votes

It's the normal behavior.

If you want to reuse a variable multiple times, a good solution is to use template literals with placeholders.

In a template literal string (that is a string delimited with back-ticks), placeholders are replaced by the value of the matching variable.

let name = "Bob"
let template = `Hello ${name}. Your name is ${name}`
// template will contain "Hello Bob. Your name is Bob"

You can get the content of the light DOM using querySelector() on the slot attribute, or on any other attribute you prefer. To get the value, use property textContent, innerHTML, or even outerHTML if you wan to keep the surrounding <span> element.

class MyElement extends HTMLElement {
    connectedCallback() {
        var person_name = this.querySelector( '[slot=personName]' ).outerHTML
        this.attachShadow({ mode: "open" })
            .innerHTML = `Hello ${person_name}. Your name is ${person_name}`
    }
}

customElements.define("x-myelement", MyElement)
<x-myelement>
    <span slot="personName">Bob</span>
</x-myelement>
0
votes

take a look at LitHtml and MDN

make name as an attribute of element, then inject it into the template.

const tmpl = document.createElement('template');
tmpl.innerHTML = `
<style>span{font-weight:bold}</style>
<p>Hello <span id="name"></span> Your name is <span id="name2"></span></p>`;
class MyElement extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "open" });
    shadow.appendChild(tmpl.content.cloneNode(true));

    const name = this.getAttribute('personName');
    shadow.querySelector('#name').innerText = name;
    shadow.querySelector('#name2').innerText = name;
  }
}
customElements.define("x-myelement", MyElement);
<x-myelement personName="Bob"></x-myelement>

slots are not supported by all browser, especially named slots.

second, slotted content are not within the scope of the component (shadow dom), unless that was what you where going for?