javascripthtmlcustom-elementdocumentfragmenthtml-templates

Initialisation of Custom Elements Inside Document Fragment


Consider this HTML template with two flat x-elements and one nested.

<template id="fooTemplate">
  <x-element>Enter your text node here.</x-element>
  <x-element>
    <x-element>Hello, World?</x-element>
  </x-element>
</template>

How to initialise (fire constructor) all custom elements in cloned from fooTemplate document fragment without appending it to DOM, neither by extending built-in elements with is="x-element"; either entire fragment.

class XElement extends HTMLElement {
  constructor() { super(); }
  foo() { console.log( this ); }
} customElements.define( 'x-element', XElement );

const uselessf = function( temp ) {
  const frag = window[ temp ].content.cloneNode( true );

  /* Your magic code goes here:
  */ do_black_magic( frag );

  for (const e of  frag.querySelectorAll('x-element') )
    e.foo(); // This should work.

  return frag;
};

window['someNode'].appendChild( uselessf('fooTemplate') );

Note that script executes with defer attribute.


Solution

  • We can initialise template with this arrow function:

    const initTemplate = temp =>
      document.createRange().createContextualFragment( temp.innerHTML );
    
    const frag = initTemplate( window['someTemplate'] );
    

    Or with this method defined on template prototype (I prefer this way):

    Object.defineProperty(HTMLTemplateElement.prototype, 'initialise', {
      enumerable: false,
      value() {
        return document.createRange().createContextualFragment( this.innerHTML );
      }
    });
    
    const frag = window['someTemplate'].initialise();
    

    In any case in result this code will work fine:

    for (const elem of  frag.querySelectorAll('x-element') )
      elem.foo();
    
    window['someNode'].appendChild( frag );
    

    I'm not sure if these methods are the most effective way to initialise custom elements in template.

    Also note that there is no need for cloning template.