Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Created November 29, 2023 22:54
Show Gist options
  • Select an option

  • Save dfkaye/feab681496ac9fc8bc63e6b3006f796e to your computer and use it in GitHub Desktop.

Select an option

Save dfkaye/feab681496ac9fc8bc63e6b3006f796e to your computer and use it in GitHub Desktop.
attaching shadow DOM to a template element: No, you can't do that, but you can
// 25 August 2023
// attaching shadow DOM to a built-in element:
// what are the implications?
// continued from https://gist.github.com/dfkaye/e6b72291d3e31517d71229d703aad754
/************************** Using a template element **************************/
// YOU CAN'T DO THAT.
// You can't attach a shadow DOM to a template element but you can append slot
// elements to a template element.
//
// However, the content of the slot element is visible in the DOM if its parent
// template element is styled with display: block (which you should never do),
// whereas the content of a slot inside a DIV element with a shadow DOM is not.
// The safety fix for that, just in case, is to hide the slot with CSS rule
// `display: none;`.
var body = document.body;
var template = document.createElement('template');
template.style = `
background: gray;
color: blue;
// This hacks renders template elements visible in the DOM,
// which you should never do with a template element.
display: block;
font-family: sans-serif;
outline: 1px solid aqua;
padding: 2em;
`;
// template elements cannot be shadow hosts.
// template.attachShadow({
// mode: 'open',
// delegatesFocus: true,
// slotAssignment: 'named'
// });
// "DOMException: Operation not supported"
body.replaceChildren(template);
function parse(json) {
var data;
try { data = JSON.parse(json); }
catch (error) {
console.error(`malformed JSON has caused "${error}", "${json}".`);
}
console.warn({json});
console.dir({data});
}
var observer = new MutationObserver(function (list, observer) {
for (var item of list) {
var { type, target, attributeName, addedNodes } = item;
console.log({ type, target, attributeName, addedNodes });
if (type == "attributes" && attributeName == "changes" ) {
parse(target.getAttribute(attributeName));
}
if (type == "childList" && addedNodes[0]) {
parse(addedNodes[0].textContent);
// immediate removal from the shadow DOM
// target.textContent = "";
}
if (type == "childList" && !addedNodes.length) {
var contents = [];
item.removedNodes.forEach(node => contents.push(node.textContent));
var content = contents.join("");
console.error("removed", content);
}
}
});
console.warn(template.outerHTML);
var slot = document.createElement("slot");
slot.setAttribute("name", "changes");
// 10 September 2023
// inert test - inert has no effect on displayability...
slot.setAttribute("inert", true);
slot.style = `
background: blue;
padding: 1em;
/* 10 Sept 2023 */
/* hide the slot from the display */
display: none;
`;
template.appendChild(slot);
console.warn(template.outerHTML);
//
// Listen to the slot element for changes, rather than the template...
observer.observe(slot, {
attributes: true,
childList: true,
subtree: true
});
var data = {
"profile": {
"field": 30,
"radio": "individual",
"checkbox": "red",
//"checkbox2": "aqua",
"array": [true, false],
"address": {
"street": "1234 5th Street",
"street2": "Suite Sixteen",
"city": null,
"state": NaN,
"postalCode": {}
}
},
"right": { "path" : { "name": "wrong keyname" } }
};
/* 10 Sept 2023 */
/* remove the template from the DOM */
// Recall the mutation observer gist showing that observers work on nodes that
// are not attached to the DOM? Same applies here. Although we see "TEST" in the
// display, the template element's slot is still accessible to the display, even
// via its children[] array...
//body.replaceChildren("TEST");
// update slot attribute
template.children["changes"].setAttribute("changes", JSON.stringify(data));
// update slot content
template.children["changes"].textContent = JSON.stringify(data);
// 10 September 2023
// interesting results due to race conditions...
setTimeout(() => template.children["changes"].removeAttribute("changes"), 1);
setTimeout(() => template.children["changes"].textContent = "bonk", 1);
setTimeout(() => {
console.warn(template.outerHTML);
// '<template style="..."></template>'
console.log(template.children["changes"].outerHTML);
// '<slot name="changes" inert="true" style="... display: none;">bonk</slot>'
}, 2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment