Search code examples
javascriptweb-componentlit-elementlit-html

How to pass event to a child in LitElement


I want to make a drop-down menu and that when clicking on the input, the menu is displayed with a toggle that removes or places the 'hidden' class

I have this method

toggleMenu() {
    this.classList.toggle("hidden");
}

And here the template.

render(){
   return html`
       <input @click="${this.toggleMenu}" type="button">
       <ul class="hidden">
           <slot></slot>
       </ul>
   `;
}

Solution

  • One straightforward solution is to add a property to your custom element, e.g. open, that is toggled in your toggleMenu method:

    static get properties() {
      return {
        open: { type: Boolean },
      };
    }
    
    constructor() {
      super();
      this.open = false;
    }
    
    toggleMenu() {
      this.open = !this.open;
    }
    

    Then in your render method set the <ul>'s class attribute based on the value of this.open:

    render(){
      return html`
        <button @click=${this.toggleMenu} type="button">Toggle</button>
        <ul class=${this.open ? '' : 'hidden'}>
          <slot></slot>
        </ul>
      `;
    }
    

    You can see this working in the below snippet:

    // import { LitElement, css, html } from 'lit-element';
    const { LitElement, css, html } = litElement;
    
    class DropDownMenu extends LitElement {
      static get properties() {
        return {
          open: { type: Boolean },
        };
      }
    
      static get styles() {
        return css`
          ul.hidden {
            display: none;
          }
        `;
      }
    
      constructor() {
        super();
        this.open = false;
      }
      
      toggleMenu() {
        this.open = !this.open;
      }
    
      render(){
        return html`
          <button @click=${this.toggleMenu} type="button">Toggle</button>
          <ul class=${this.open ? '' : 'hidden'}>
            <slot></slot>
          </ul>
        `;
      }
    }
    customElements.define('drop-down-menu', DropDownMenu);
    <script src="https://bundle.run/[email protected]"></script>
    
    <drop-down-menu>
      <li>Item 1</li>
      <li>Item 2</li>
    </drop-down-menu>

    If you want to apply additional classes to the <ul> you'll want to look into the classMap function as described in the LitElement docs.


    Alternatively, you can add reflect: true to the open property declaration, which lets you show or hide the <ul> using CSS alone, rather than setting its class in render:

    static get properties() {
      return {
        open: {
          type: Boolean,
          reflect: true,
        },
      };
    }
    
    static get styles() {
      return css`
        ul {
          display: none;
        }
        :host([open]) ul {
          display: block;
        }
      `;
    }
    

    Here it is in a working snippet:

    // import { LitElement, css, html } from 'lit-element';
    const { LitElement, css, html } = litElement;
    
    class DropDownMenu extends LitElement {
      static get properties() {
        return {
          open: {
            type: Boolean,
            reflect: true,
          },
        };
      }
    
      static get styles() {
        return css`
          ul {
            display: none;
          }
          :host([open]) ul {
            display: block;
          }
        `;
      }
    
      constructor() {
        super();
        this.open = false;
      }
      
      toggleMenu() {
        this.open = !this.open;
      }
    
      render(){
        return html`
          <button @click=${this.toggleMenu} type="button">Toggle</button>
          <ul>
            <slot></slot>
          </ul>
        `;
      }
    }
    customElements.define('drop-down-menu', DropDownMenu);
    <script src="https://bundle.run/[email protected]"></script>
    
    <drop-down-menu>
      <li>Item 1</li>
      <li>Item 2</li>
    </drop-down-menu>

    Both of these are common approaches and the best one for your application will depend on your use case and personal preferences.