Links
- MDN reference
- https://developer.mozilla.org/en-US/docs/Web/API/Web_components
- MDN git repository of examples
- https://github.com/mdn/web-components-examples
- Relevant specifications (from MDN)
- https://html.spec.whatwg.org/multipage/scripting.html#the-template-element
- https://dom.spec.whatwg.org/#interface-shadowroot
Example
The following code is also in webcomp-example.js.
Also, I’ll say that you don’t need to manually assign slots here, I’m just showing how to do it to remind myself.
class JmmWebComponentExample1 extends HTMLElement {
static #tmpl = jmmTemplateFragment(`\
<style>
:host {
display: flex;
flex-direction: column;
border: 1px solid rebeccapurple;
}
a {
font-weight: bold;
}
</style>
<a href="https://jmm.io/">I am a link inside the shadow DOM</a>
<slot>I’m a placeholder value.</slot>\
`);
constructor() {
super();
// The reason I’m doing this here instead of connectedCallback is that we don’t want to keep trying to attach a shadow if the element is moved.
// 2024-05-15
this.attachShadow({ mode: "open", slotAssignment: "manual" });
this.shadowRoot.replaceChildren(JmmWebComponentExample1.#tmpl.cloneNode(true));
}
connectedCallback() {
const shadow = this.shadowRoot;
// Query elements in the shadow Root
const [s1] = shadow.querySelectorAll(`slot`);
// Query elements in the document
const a1 = this.querySelector(`a`);
// Manually assign an element to a slot.
if (a1)
s1.assign(a1);
}
}
customElements.define("jmm-web-component-example-1", JmmWebComponentExample1);
function jmmTemplateFragment(innerHTML) {
return Object.assign(document.createElement('template'), { innerHTML }).content;
}
And with the following HTML elements:
<jmm-web-component-example-1><a href="https://jmm.io">I am not inside the shadow DOM</a></jmm-web-component-example-1>
<jmm-web-component-example-1/>
We get:
As of 2024-05-15 I’ve modified the code slightly to have more stuff happen in the constructor than the connectedCallback
.
In the original, if you tried moving an element, it would try attaching a shadow root to an element that already has one.
This version makes connectedCallback
a bit more idempotent.
Miscellaneous notes
- Elements in the shadow DOM don’t inherit CSS from outside. Slotted elements, however, do. The shadow DOM can seem to use variable values declared outside it, though. For example, you can define a color in some outer CSS and use it inside the shadow DOM.
- Slot elements cannot be assigned an element that isn’t attached and inside of the custom element. At least, I tested this in Firefox 123 on 2024-03-06.
-
connectedCallback
will be called every time an element is moved, so make it idempotent if the component can be relocated. -
I think you could set
innerHTML
on theshadowRoot
directly instead of a<template>
element.shadowRoot
is also aDocumentFragment
likeHTMLTemplateElement.content
. However, I think it may be faster to create a<template>
once than have to parseinnerHTML
repeatedly. I haven’t tested this though.