I'm trying to get the value of my selected option, so when someone uses my web component they can access it. I think the problem has to do with the shadow root
__createOptions() {
const SELECT = this.shadowRoot.querySelector('select');
SELECT.addEventListener('change', event => {
this.value= event.target.value;
});
this.shadowRoot.addEventListener('slotchange', () => {
const OPTION = this.querySelector('option');
if (OPTION) {
SELECT.append(OPTION);
}
});
}
render() {
return html`
<div class="selectWrapper">
<select id="typeDropdown"></select>
</div>
<slot></slot>
`;
}
<wc-select value="">
<option value="1">Option 1</option>
<option value"2">Option 2</option>
<option value="3">Option 3</option>
</wc-select>
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
<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