Dropdown menu click eventListener producing a different target element based on area clicked

949 Views Asked by At

I have a MaterializeCSS dropdown menu implemented with this HTML. The event listener works only if the dropdown item is clicked in the upper portion.


    <div class="left">
      <span id="notificationTotal" class="new badge red" style="display:none"> 
      </span>
        <a class="dropdown-trigger" href="#!" data-target="notificationsDropdown">
          <i class="material-icons"> message</i>
        </a>
      </div>
      <ul id="notificationsDropdown" class="dropdown-content">
    </ul>

I'm using the following Javascript to populate the menu with notifications. This is working just fine.

    // Reset Notification Dropdown
    notificationsDropdown.innerHTML = '';

    notifications.forEach(notif => {
      const displayTime = moment(notif.date).fromNow();
      let typeIcon = 'sms';
      if (notif.type === 'chat') {
        typeIcon = 'lock';
      }

      notificationsDropdown.innerHTML += `<li class="notification">
      <a style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;font-size:14px" href="#" class="blue-text">
      <span class="js-patientName">
      ${notif.contact.firstName} ${notif.contact.lastName}</span>
      <span class="js-notificationPhone" style="display:none">${
        notif.contact.phone
      }</span>
      <span class="js-patientId" style="display:none">${
        notif.patientId
      }</span>
      <span class="js-patientDOB" style="display:none">${
        notif.contact.birthDate
      }</span>
      <p style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;padding-top: 0;">
        <i style="display: inline-flex; vertical-align:middle" class="tiny material-icons">${typeIcon}</i>
        <span class="black-text" style="font-size:12px">
        ${displayTime}
        </span>
      </p>
      </a></li>`;
    });
    notificationsDropdown.innerHTML += `<li class="divider" class="blue-text"></li><li><a href="/notifications" class="blue-text">See All Notifications</a></li>`;
  }

The dropdown gets populated and when a user clicks on a particular dropdown .li entry depending on the exact location they click, it may or not work. The user must click at the main top of the dropdown item.

This is the event listener code that extracts the values from the hidden span elements.

  document
    .querySelectorAll('#notificationsDropdown', '.li .a .notification')
    .forEach(input =>
      input.addEventListener('click', async e => {
        // console.log('clicked', e.target);

        console.log(e.target.parentNode);

        const name = e.target.children[0].textContent.trim();
        const phone = e.target.children[1].textContent.trim();
        const patientId = e.target.children[2].textContent.trim();
        const birthDate = e.target.children[3].textContent.trim();
        console.log('patientid ', patientId);

        const patient = {
          name,
          patientId,
          phone,
          birthDate
        };

Is there a way I can rewrite the eventListener code to resolve this issue? Possibly instead of using e.target.children[insert_number_here].textContent I could use .closest('js-patientId') or similar ?

This is how the HTML is rendered to the page. This is an example of a single notification:

<ul
  id="notificationsDropdown"
  class="dropdown-content"
  tabindex="0"
  style="display: block; width: 177.297px; left: 1648.7px; top: 0px; height: 251px; transform-origin: 100% 0px; opacity: 1; transform: scaleX(1) scaleY(1);"
>
  <li class="notification">
    <a
      style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;font-size:14px"
      href="#"
      class="blue-text"
    >
      <span class="js-patientName">ANDREW TAYLOR</span>
      <span class="js-notificationPhone" style="display:none">
        5555551212
      </span>
      <span class="js-patientId" style="display:none">
        1
      </span>
      <span class="js-patientDOB" style="display:none">
        1960-01-01
      </span>
      <p style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;padding-top: 0;">
        <i
          style="display: inline-flex; vertical-align:middle"
          class="tiny material-icons"
        >
          sms
        </i>
        <span class="black-text" style="font-size:12px">
          2 hours ago
        </span>
      </p>
    </a>
  </li>
  <li class="divider" />
  <li>
    <a href="/notifications" class="blue-text">
      See All Notifications
    </a>
  </li>
</ul>;
2

There are 2 best solutions below

0
Andrew Taylor On BEST ANSWER

I was able to remove the in each li. Then adjust the value assignment to use the following:

e.target.parentNode.children[0].textContent.trim();
0
Harshit Agarwal On

For dropdowns, the better way to handle actions is through onChange event. So you could attach your eventHandler on onChange of dropDown itself rather than attaching onClick on each of dropDown items. So whenever you change the selected value the onChange event will be triggered and you can easily get the selected value.