Shadow DOM & Templates

by mgiuffrida

HTML

<template id="simpleTemplate">
  <h1>Template Header</h1>
  <p>Lorem ipsum dolor sit amet.</p>
</template>
<template id="wcTemplate">
  <h1>
    Header A
  </h1>
  <x-foo>
    This is the x-foo in the template.
  </x-foo>
</template>
<x-foo>This is the x-foo in the main document.</x-foo>

JavaScript

// Tested with Chrome and FF. In FF, enable dom.webcomponents in about:config.

// Some setup.
function assert(x) {
  if (x) return;
  console.trace();
  throw Error(x);
}
function assertUndefined(x) {
  assert(x === undefined);
}
console.clear();

// document.createElement(foo) --> foo.ownerDocument === document
// importNode https://dom.spec.whatwg.org/#dom-document-importnode




//
// DocumentFragment (https://dom.spec.whatwg.org/#documentfragment)
//

// The DocumentFragment interface, like Document and Element, extends the Node interface.
// It is not an Element.
var fragment = new DocumentFragment();
assert(fragment.nodeName == '#document-fragment');
assertUndefined(fragment.tagName);
assertUndefined(fragment.id);
assertUndefined(fragment.innerHTML);

assert(fragment.ownerDocument == document);
assert(fragment.parentNode === null);


//
// HTMLTemplateElement
//

// <template> creates a template element. Templates are never rendered.
var tmpl = document.querySelector('template#simpleTemplate');
assert(tmpl instanceof HTMLTemplateElement);

// At first glance, templates are like any other HTMLElement.
assert(tmpl.tagName == 'TEMPLATE');
assert(tmpl.innerHTML.includes('<h1>Template Header</h1>'));

// Templates have a read-only DocumentFragment property, 'content'.
assert(tmpl.content instanceof DocumentFragment);

assert(tmpl.content.nodeName == '#document-fragment');
assert(!tmpl.content.tagName && !tmpl.content.id && !tmpl.content.innerHTML);

// It is a standalone node? associated with a separate Document.
assert(tmpl.ownerDocument === document);
assert(tmpl.content.ownerDocument != document);
assert(tmpl.content.parentNode === null);


var XFoo = document.registerElement('x-foo', {
  prototype: {
    __proto__: HTMLElement.prototype,
    createdCallback: function() {
      this._created = true;
    },
  }
});

// Element is created immediately.
assert(document.querySelector('x-foo')._created)


var wcTmpl =...