1

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>
Patricio Vargas
  • 5,236
  • 11
  • 49
  • 100

1 Answers1

1

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

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • Thanks so much for you. answer Danny. I'm runing into the issue where if someone uses my web component "my-element" how can they get the value of the ``select``. Assuming the user has no access to component's logic. So they are just going to import the component in their project and start using it – Patricio Vargas Apr 17 '20 at 20:23
  • 1
    with ``mode:"open"`` they can always access ``document.querySelector('my-element').shadowRoot.querySelector('select')``. or add a method that returns a pointer to ```` to attributes (and maybe extra methods) on ```` – Danny '365CSI' Engelman Apr 18 '20 at 08:20
  • This would be a lot easier with **Customized Built-In Elements** (extending ``HTMLSelectElement``) instead of **Autonomous Elements** (extending ``HTMLElement``) But [Apple refuses to implement Customized Built-In Elements](https://www.chromestatus.com/feature/4670146924773376). So if you do **not** have to deliver to Safari you can extend SELECT (supported in all other Browsers) – Danny '365CSI' Engelman Apr 18 '20 at 08:24