skip to Main Content

So, I’m creating my first web component and I’m struggling with the event handling side with elements within the shadow root.

The goal with this component is to display a list of results from an endpoint where the user types in an input field and then grab the text from the selected element and display it on the same input field.

The HTMl is this:

<div class="results">
<ul>
<li class="result-item"><i></i><span>Amalfiküste</span></li>
<li class="result-item"><i></i><span>Guatemala</span></li>
<li class="result-item"><i></i><span>Italien, Malta</span></li>
<li class="result-item"><i></i><span>Malaysia</span></li>
<li class="result-item"><i></i><span>Malediven</span></li>
<li class="result-item"><i></i><span>Mallorca</span></li>
<li class="result-item"><i></i><span>Malta</span></li>
</ul>
</div>

I then query the result-item:

let resultItem = document.querySelector('destination-search').shadowRoot.querySelectorAll('.result-item');

And the function to get the selected value into the input:

function selectTextIntoInput(resultItem, searchInput, shadowRoot) {
    for (let i = 0; i < resultItem.length; i++) {
        resultItem[i].addEventListener("click", function (event) {
            searchInput.value = resultItem[i].innerText;

            const destinationEvent = new CustomEvent('destination-search', {
                detail: resultItem[i].innerText,
                bubbles: true,
                composed: true
            });

            shadowRoot.dispatchEvent(destinationEvent);
        });
    }
}

I’ve checked the console to see if I was querying the result-item fields and it’s working fine. The problem is clicking each one ,select the text into the input and dispatch it as a custom event to be used by another component.

How do we handle click events within shadow root elements?

2

Answers


  1. You need to use composed option in the event, and also need to use arrow function

    like this below updated code:-

    function selectTextIntoInput(resultItem, searchInput, shadowRoot) {
            for (let i = 0; i < resultItem.length; i++) {
                resultItem[i].addEventListener("click", (event) => {
                    searchInput.value = resultItem[i].innerText;
    
                    const destinationEvent = new CustomEvent('destination-search', {
                        detail: resultItem[i].innerText,
                        bubbles: true,
                        composed: true
                    });
    
                    this.dispatchEvent(destinationEvent);
                }, { composed: true });
            }
        }
    Login or Signup to reply.
  2. Instead of
    <li class="result-item"><i></i><span>Amalfiküste</span></li>

    Make it a Web Component <result-item>

    And keep all (interaction) logic inside the Web Component

    customElements.define("result-item",class extends HTMLElement{
      connectedCallback(){
        let input = this.closest("destination-search").querySelector("input");
        input.addEventListener("keyup", (evt) => {
          let match = this.innerText.includes(evt.target.value);
          this.closest("li").style.display = match ? "list-item" : "none";
        });
        this.onclick = (evt) => input.value = evt.target.innerText;  
        this.style.cursor = "pointer";
        //wait till lightDOM is parsed
        setTimeout(()=>this.innerHTML = `<b>${this.innerHTML}</b>`);
      }
    });
    body{ font:12px arial }
    <destination-search>
      <input>
      <ul>
        <li><result-item>Amsterdam</result-item></li>
        <li><result-item>Rotterdam</result-item></li>
        <li><result-item>The Hague</result-item></li>
        <li><result-item>Utrecht</result-item></li>
        <li><result-item>Eindhoven</result-item></li>
        <li><result-item>Tilburg</result-item></li>
        <li><result-item>Groningen</result-item></li>
        <li><result-item>Almere</result-item></li>
        <li><result-item>Breda</result-item></li>
        <li><result-item>Nijmegen</result-item></li>
        <li><result-item>Haarlem</result-item></li>
        <li><result-item>Arnhem</result-item></li>
        <li><result-item>Zaanstad</result-item></li>
        <li><result-item>'s-Hertogenbosch</result-item></li>
        <li><result-item>Amersfoort</result-item></li>
        <li><result-item>Zierikzee</result-item></li>
      </ul>
    </destination-search>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search