skip to Main Content

I do not use any frameworks and try to create an HTML content template with <template and slots for using it in slightly modified ways in my main document. I could use JavaScript string templates and add it to the document but would prefer to have the HTML not pollute my script file.
The setup is like this:

Definition:

template.html

<template id="my-template">
  <style>
    /*** static style attributes ***/
    .myTemplate {
      position: relative;
      
      --radius: 30px;  /* I gladly take <slot> here if possible */
    }
    .myTemplate > .drawing {
      height: 100%;
    }
    .myTemplate > .text {
      font-size: 1em;

      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
    }
    .myTemplate circle {
      /* e.g. stroke width should be related to the radius, ignore setting the radius */
      stroke-width: calc(.5 * var(--radius));
      /* imagine several more depending attributes */
    }
  </style>
  
  <div class="myTemplate">
    <div class="drawing">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
        <circle cx="50%" cy="50%" r="30"
         />
      </svg>
    </div>
    <div class="text"><slot name="text">foo</slot></div><!-- this slot wobks as expected -->
  </div>
</template>

and

style.css

/* free to modify style attributes */
.myTemplate {
  height: 300px;
  width: 300px;
  font-size: 100px;

  background: yellow;
}
.myTemplate circle {
  fill: #fff0;
  stroke: #e28;
}

Expected usage:

script.js

const templateContent = document.getElementById('my-template').content;

let clone1 = templateContent.cloneNode(true);
clone1.querySelector('slot[name=text]').textContent = 'bar';
document.body.appendChild(clone1);

let clone2 = templateContent.cloneNode(true);
clone2.querySelector('slot[name=text]').textContent = 'baz';
// clone2.querySelector('slot[name=radius]').textContent = 22; /* no luck here */
// clone2.style.setProperty('--radius', '22');  /* does not work as well*/
document.body.appendChild(clone2);

So, there are several attributes which base on the same value (here: --radius) and of course I would like to just set this one value instead of half a dozen others.

I neither could make this work using slots nor accessing style.methods of the template’s instance. One crucial aspect is that individual clones must be parametrized differently.

What am I doing wrong? What would be a better approach?

2

Answers


  1. This can be done by modifying the textContent property of the style tag. For example if your template had the following slug:

    <template id="my-template">
       <style>
          /*** static style attributes ***/
          .myTemplate {
             position: relative;
      
             --radius: slot-radius;  /*< Slot here named slot-radius */
          }
          /*...*/
       </style>
    </template>
    

    Then you can do the following JavaScript:

    let clone1 = templateContent.cloneNode(true);
    let style = clone1.querySelector('style');
    style.textContent = style.textContent.replace('slot-radius', '30px');
    

    This purely a string replace so you would have to be creative with your names to avoid collisions. I would prefix all of the slots, and you need to accept that your templates contain malformed HTML to begin with, but it will work.

    Login or Signup to reply.
  2. The problem you are having is that you are forgetting that .content on a template is returning a DocumentFragment and not a proper HTML element.

    So the following won’t work since a DocumentFragment doesn’t have a style attribute:

    clone2.style.setProperty("--radius", '22px');
    

    But this will work:

    clone2.querySelector(".myTemplate").style.setProperty("--radius", '22px');
    

    P.S.
    If you just did something like .firstElementChild, you would find the style tag inside the template, rather than the div you want.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search