I was too quick in my comment.
<SLOTs>
can not be targeted like 'normal' DOM elements
(like many) You are running into the trap of thinking slotted content
is MOVED to ShadowDOM <slots>
It is NOT.
slotted content is only REFLECTED in shadowDOM, it still remains invisible! in lightDOM
You can not access the reflected content with .querySelector
or .children[]
... because it is not there (in shadowDOM).. it still is in lightDOM.
For the same reason you style slotted content in lightDOM:
Use CSS selectors like :first-child inside shadow dom
To append lightDOM <OPTIONs>
into a shadowDOM <SELECT`>
1. You either move them from lightDOM to shadowDOM:
let select = this.shadowRoot.querySelector('select');
let host = this.shadowRoot.getRootNode().host;
let options = host.querySelectorAll('option');
select.append(...options);
#1 is the easiest one, as it does not require any <slots>
in shadowDOM
2. You were on the right track with the slotchange
event
it requires a (named/unnamed) <slot></slot>
in the shadowDOM template.
You find your lightDOM nodes in:
BE AWARE, this gets you ALL nodeTypes including text
nodes because there are linebreaks and whitespace in the <my-element>
innerHTML!
<my-element>
<option>Grow up</option>
<option>Learn React</option>
<option>Learn Lit</option>
<option>Forget W3C standard Custom Elements API</option>
<H1 slot=title>My Never Todo List</hH>
</my-element>
Luckily the <SELECT>
doesn't care, so you can dump assignedNodes
straight in..
this.shadowRoot.addEventListener('slotchange', (evt) => {
if (!evt.target.name) { // only for unnamed slot
this.shadowRoot.querySelector('select')
.append(...evt.target.assignedNodes());
}
});
note! The <options>
were reflected to the UNnamed slot,
<H1 slot=title>
reflected to the <slot name=title>
(they should have named them reflections instead of slots)
Click Show Code Snippet for complete code
customElements.define("my-element", class extends HTMLElement {
connectedCallback() {
let template = document.getElementById(this.nodeName);
this.attachShadow({
mode: 'open'
}).append(template.content.cloneNode(true));
this.shadowRoot.addEventListener('slotchange', (evt) => {
if (!evt.target.name) { // only for unnamed slot
let select = this.shadowRoot.querySelector('select');
select.append(...evt.target.assignedNodes());
}
});
}
})
<template id=MY-ELEMENT>
<style>
:host {
display: block;
}
select{
font-size:1.5em;
}
</style>
<slot name=title></slot>
<select multiple>
</select>
<slot></slot>
</template>
<my-element>
<option>Grow up</option>
<option>Learn React</option>
<option>Learn Lit</option>
<option>Forget W3C standard Custom Elements API</option>
<h1 slot=title>My Never Todo List</h1>
</my-element>
JSFiddle playground with both options:
https://jsfiddle.net/CustomElementsExamples/v2f9zmu5/
More SLOT related answers can be found with StackOverflow Search: Custom Elements SLOTs