Search code examples
javascripthtmlcssdrop-down-menudropdown

Is there a reason why e.target and an htmlelement cannot be compared?


I am trying out working with dropdown menus, and I was thinking about making a template for the dropdowns so that I can easily recreate them without any additional Javascript. However, in one of my if loops, an event.target and another element are always returning that they are different when they aren't. It doesn't make any sense, since they are the same element (proved by the console) and should be returning true. Here is the code:

const _$$ = (q) => document.getElementsByClassName(q);
let dropdowns = _$$('dropdown');
if (dropdowns[0]) {
  for (let dropdown of dropdowns) {
    let title = dropdown.children[0],
      options;
    title.setAttribute('tabindex', 0);
    title.addEventListener('focus', function() {
      options = Array.from(dropdown.children).slice(1, dropdown.children.length);
      for (let option of options) {
        option.style.display = 'block';
        option.setAttribute('tabindex', options.indexOf(option) + 1);
        option.style.top = `${title.offsetHeight+options.indexOf(option)*title.offsetHeight}px`;
        option.style.padding = window.getComputedStyle(title, null).getPropertyValue('padding');
      }
      document.addEventListener('click', function(e) {
        console.log(e.target);
        console.log(title);
        if (e.target !== dropdown || e.target !== title) {
          for (let option of options) {
            if (e.target !== option) {
              option.style.display = 'none';
            }
          }
        }
      }, false);
    }, false);
  }
}
.dropdown-title {
  padding: 5px 8px;
  color: blue;
  background: white;
  width: fit-content;
}

.dropdown {
  display: flex;
  flex-direction: column;
}

.dropdown:hover {
  cursor: pointer;
}

.dropdown-title {
  outline: none;
}

.dropdown-option {
  display: none;
  position: absolute;
  user-select: none;
}
<div class="dropdown --top-dropdown">
  <div class="dropdown-title" tabindex="0">Dropdown</div>
  <div class="dropdown-option" tabindex="1" style="display: none; top: 31px; padding: 5px 8px;">Dropdown Option</div>
  <div class="dropdown-option" tabindex="2" style="display: none; top: 62px; padding: 5px 8px;">Dropdown Option</div>
</div>

As you can see, the e.target and option are the same element. However, in the if loop where it says if (e.target !== dropdown || e.target !== option) {...} it is always saying they are different elements and removing the dropdown options.


Solution

  • The condition e.target !== dropdown || e.target !== title should be changed to e.target !== dropdown && e.target !== title. The reason for that is when e.target equals title, it necessarily doesn't equal dropdown and vice versa (when e.target is dropdown, and when the condition is or (||) the expression is always true.

    const _$$ = (q) => document.getElementsByClassName(q);
    let dropdowns = _$$('dropdown');
    if (dropdowns[0]) {
      for (let dropdown of dropdowns) {
        let title = dropdown.children[0],
          options;
        title.setAttribute('tabindex', 0);
        title.addEventListener('focus', function() {
          options = Array.from(dropdown.children).slice(1, dropdown.children.length);
          for (let option of options) {
            option.style.display = 'block';
            option.setAttribute('tabindex', options.indexOf(option) + 1);
            option.style.top = `${title.offsetHeight+options.indexOf(option)*title.offsetHeight}px`;
            option.style.padding = window.getComputedStyle(title, null).getPropertyValue('padding');
          }
          document.addEventListener('click', function(e) {
            if (e.target !== dropdown && e.target !== title) {
              for (let option of options) {
                if (e.target !== option) {
                  option.style.display = 'none';
                }
              }
            }
          }, false);
        }, false);
      }
    }
    .dropdown-title {
      padding: 5px 8px;
      color: blue;
      background: white;
      width: fit-content;
    }
    
    .dropdown {
      display: flex;
      flex-direction: column;
    }
    
    .dropdown:hover {
      cursor: pointer;
    }
    
    .dropdown-title {
      outline: none;
    }
    
    .dropdown-option {
      display: none;
      position: absolute;
      user-select: none;
    }
    <div class="dropdown --top-dropdown">
      <div class="dropdown-title" tabindex="0">Dropdown</div>
      <div class="dropdown-option" tabindex="1" style="display: none; top: 31px; padding: 5px 8px;">Dropdown Option</div>
      <div class="dropdown-option" tabindex="2" style="display: none; top: 62px; padding: 5px 8px;">Dropdown Option</div>
    </div>