Skip to content

Lit-element – Importing a component inside another component and then accessing the DOM of the imported component

I’ve honestly racked my brain on this for a couple days. I’m decently new to lit-element and web components in general.

Basically, I’m building a new component called <date-picker> and it uses the Flatpickr plugin. This Date Picker component imports another component called <textfield>. Inside of that component there is a wrapper(.date-picker) with an input field and an icon. I need to be able to access the wrapper so I can click the input field and icon to trigger the calendar popup. But no matter what I do, I can’t reach those the dom elements inside the component. I’ve tried accessing them using this.shadowRoot.querySelector('.date-picker') along with the light dom document.querySelector('.date-picker') among a variety of other things. Sample code below. I appreciate any insight you can offer.

Date picker component:

render() {
  return html`
    <textfield iconalt="Calendar" iconname="calendar" label="Calendar" optionaltext="hide"></textfield>
  `;
}

updated() {
  var datePickerShadow = this.shadowRoot.querySelector('.date-picker'); // gets el in shadow dom
  var datePickerLight = document.querySelector('.date-picker'); // gets el in light dom

  var importantDate = [Date.parse('2021-1-27'), Date.parse('2021-1-5'), Date.parse('2021-2-9')];
    var datePicker = flatpickr(datePickerLight, {
        wrap: true,
        disable: ["2021-01-30", "2021-01-21", "2021-01-08", new Date(2025, 4, 9) ],
        allowInput: true,
        clickOpens: false,
    })
}

Answer

If <textfield> is a custom element then its tagname is illegal: custom element tags must contain at least a -. This is probably preventing the browser from upgrading it (and thus rendering its content and executing its logic).

Anyway if .date-picker is inside of <textfield>‘s template, neither of the querySelector calls you tried would work: the first selects inside <date-picker>‘s shadow dom but does not recurse in child components, while the second completely misses shadow doms.

What you can do is:

  • Use cascaded querySelectors

    class DatePicker extends LitElement {
    
      async firstUpdated() {
        const textField = this.renderRoot.querySelector('text-field');
        await textField.updated; // Wait for first render of text-field
        const datePicker = textField.renderRoot.querySelector('.date-picker');
      }
    
    }
    
  • Move .date-picker in <date-picker>‘s template if possible

  • Pass .date-picker to <text-field> which renders it in a slot

    // This way you can directly select .date-picker
    
    render() {
      return html`
        <text-field>
          <div class="date-picker"></div>
        </text-field>
      `;
    }
    
  • Instantiate the picker inside <text-field> and expose the instance using a property (or part of its functionality through methods and properties).

(I’d avoid the first option if possible)