'Web Components - Using <slot> to fill in text with no tags
Consider the following HTML and JS
customElements.define("my-comp", class extends HTMLElement {
constructor() {
super();
let template = document.getElementById('my-template');
let templateContent = template.content;
const shadowRoot = this.attachShadow({
mode: 'open'
});
shadowRoot.appendChild(templateContent.cloneNode(true));
}
})
<html>
<style>
span {
border: 1px solid red
}
</style>
<template id="my-template">
I'm a new custom component using slots with value <slot name="slot1">default</slot>
</template>
<my-comp>
<span slot="slot1">slotted value</span>
</my-comp>
<p> </p>
<my-comp>
<n slot="slot1">without span</n>
</my-comp>
</html>
This will allow my page to show a <my-comp> with the inner HTML being my template text and then a <span> element containing my slotted in value of slotted value.
However, if the document had say a global CSS styling on <span>, I would see that styling on my inserted slotted value, when the template intended it to be just a string.
How might I be able to add just a text string without any CSS rules potentially applying to it? Currently I'm using an invalid element name <n> to get around the issue.
Solution 1:[1]
<slot> are styled by the container element, not (necessarily) by global CSS
In your example that was the global DOM:
<style>
<web-component>
<span slot="slot1">
--shadowRoot
<slot name="slot1"> // styled by <style>
If you want to protect your <span>; you have to add one extra shadowRoot
<style>
<web-component>
--shadowRoot
<span slot="slot1">
--shadowRoot
<slot name="slot1"> // NOT styled by <style>
Note how I moved the <span> inside the first shadowRoot.
In the example below I used <div> instead of <span> for easier layout"
Also note where I added an extra <style>, because <slot> are styled by its container element
The Web Component creates two shadowRoots;
and MOVES the <style> and <div> elements inside the first shadowRoot
using: container.append(...this.children);
But the this.children are only available when the whole <my-comp> is parsed;
that is why a setTimeout is required
<style>
div { border: 2px solid green }
</style>
<template id="my-template">
<div>
Web Component with <b>reflected</b> content: <slot name="slot1">default</slot>
</div>
</template>
<div>Web Component with an extra shadowRoot:</div>
<my-comp>
<!-- this content is "moved down" one shadowRoot -->
<style>
div { color: red /* styles slot within this <my-comp> */ }
</style>
<div slot="slot1"><h2>Forget R18! Web Components rule!</h2></div>
</my-comp>
<script>
customElements.define("my-comp", class extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'});
}
connectedCallback() {
setTimeout(() => { // wait till innerHTML is parsed
let container = document.createElement("span");
this.shadowRoot.append(container);
container.append(...this.children);
container.attachShadow({mode:'open'})
.append(document.getElementById('my-template').content.cloneNode(true));
});
}
})
</script>
Also note you might want to work with extra <template> tags because that extra <style> tag I added will style the global DOM for as long it is not moved yet to a shadowRoot... causing FOUCs.
But that would have complicated the above example.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
