Created
November 29, 2023 22:54
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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