0

I'm trying to close dropdown menu by clicking on the body or whatever. To do this I'm using document.addEventListener but it doesn't work.

By doing several tests I managed to get the code to work by adding a div that simulates a "body". So instead of document.addEventListener I wrote document.getElementById ("container_test"). AddEventListener.

the problem is that when there is no getElement it doesn't work. I can't understand what I'm wrong, can someone help me?

Here are two examples:

Not Working Code

var usermenu = document.querySelector(".user_menu_button");

function userMenu() {
  var x = document.getElementById("mts_menu");
  if (x.classList.toggle("show")) {
    usermenu.innerHTML = '<i class="icn_button fa-solid fa-xmark"></i><span class="txt_button">Account</span>';
  } else {
    usermenu.innerHTML = '<i class="icn_button fa-solid fa-bars"></i><span class="txt_button">Account</span>';
  }
}

const closing = document.querySelector("#mts_menu");

document.addEventListener("click", (evt) => {
  if (!evt.target.closest("#mts_menu")) closing.classList.remove("show");
});
/*Button Toggle Menu*/

.user_menu_button {
  display: flex;
  align-content: center;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 32px;
  background: #3D4350!important;
  border-radius: 4px;
  padding: 12px;
  font-size: 14px!important;
  line-height: 2;
}

.icn_button {
  margin: 0;
}

.icn_button:before,
.icn_button:after {
  margin: 0;
}

.txt_button {
  margin-left: 10px;
  color: #fff;
  font-size: 14px;
  font-weight: 400;
}


/*Items menu*/

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

.user_menu.header {
  padding: 15px 15px;
}


/*Menu header info*/

.display.name {
  font-size: 14px;
  font-weight: 500;
  color: #303238;
  line-height: 1.5;
}

.display.mail {
  font-size: 13px;
  color: #1E70EB;
  line-height: 1.5;
}

hr.divider-menu {
  border-top: 1px solid #e0e0e0;
}


/*Text Link css*/

.mnu_margin {
  margin: 7px 0;
}

.user_menu.item>a {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  padding: 8px 15px;
  color: #212629;
}

.user_menu.item:hover>a {
  color: #fff;
  background: #1E70EB;
  transition: all 0.2s;
}

.user_menu.item>a .link_text {
  font-size: 14px;
  color: #212629;
}

.user_menu.item:hover>a .link_text {
  color: #fff;
}


/*Icon Items Menu*/

.icn_menu:before,
.icon_menu:after {
  margin: 0px;
  padding: 0px;
  font-size: 16px
}

.icn_menu {
  margin-right: 10px;
  display: flex !important;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
}


/* User Menu For header website */

.mts_menu_container {
  display: flex;
  flex-direction: column;
  align-content: flex-end;
  align-items: flex-end;
}

.dropdown_box {
  position: absolute;
  margin-top: 20px;
}

.mts_dropdown_content {
  background-color: #fff;
  min-width: 160px;
  width: 240px;
  border-radius: 6px;
  overflow-x: hidden;
  overflow-y: hidden;
  box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px;
  z-index: 999;
  position: relative;
  visibility: hidden;
  opacity: 0;
  top: 50px;
  height: 0;
  transition: visibility 0.2s, max-height 0.2s, opacity 0.2s, top 0.2s, height 0.2s;
}

.mts_dropdown_content.show {
  height: 100%;
  visibility: visible;
  opacity: 1;
  top: 0;
  transition: visibility 0.2s, max-height 0.2s, opacity 0.2s, top 0.2s, height 0.2s;
}
<button onclick="userMenu()" class="user_menu_button">
     <i class="icn_button fa-solid fa-bars"></i>
     <span class="txt_button">Account</span>
</button>

<div class="mts_menu_container">
  <div class="dropdown_box">

    <div id="mts_menu" class="mts_dropdown_content">
      <div class="user_menu header">
        <span class="display name">Ciao [display_name]</span>
        <span class="display mail">[display_email]</span>
      </div>

      <hr class="divider-menu">

      <div class="mnu_margin">
        <div class="user_menu item">
          <a href="/account">
            <i class="icn_menu fa-regular fa-user"></i>
            <span class="link_text">Dashboard</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="ordini">
            <i class="icn_menu fa-regular fa-basket-shopping"></i>
            <span class="link_text">I miei ordini</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="libreria">
            <i class="icn_menu fa-regular fa-cloud-arrow-down"></i>
            <span class="link_text">Downloads</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="impostazioni">
            <i class="icn_menu fa-regular fa-gear"></i>
            <span class="link_text">Impostazioni</span>
          </a>
        </div>

        <div class="user_menu item last">
          <a href="wp-login.php?action=logout">
            <i class="icn_menu fa-regular fa-arrow-right-from-bracket"></i>
            <span class="link_text">Logout</span>
          </a>
        </div>
      </div>

    </div>
  </div>
</div>

Working Code

var usermenu = document.querySelector(".user_menu_button");

function userMenu() {
  var x = document.getElementById("mts_menu");
  if (x.classList.toggle("show")) {
    usermenu.innerHTML = '<i class="icn_button fa-solid fa-xmark"></i><span class="txt_button">Account</span>';
  } else {
    usermenu.innerHTML = '<i class="icn_button fa-solid fa-bars"></i><span class="txt_button">Account</span>';
  }
}

const closing = document.querySelector("#mts_menu");

document.getElementById("container_test").addEventListener("click", (evt) => {
  if (!evt.target.closest("#mts_menu")) closing.classList.remove("show");
});
#container_test {
  margin-top: 20px;
  border: 1px solid;
  padding: 20px;
}


/*Button Toggle Menu*/

.user_menu_button {
  display: flex;
  align-content: center;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 32px;
  background: #3D4350!important;
  border-radius: 4px;
  padding: 12px;
  font-size: 14px!important;
  line-height: 2;
}

.icn_button {
  margin: 0;
}

.icn_button:before,
.icn_button:after {
  margin: 0;
}

.txt_button {
  margin-left: 10px;
  color: #fff;
  font-size: 14px;
  font-weight: 400;
}


/*Items menu*/

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

.user_menu.header {
  padding: 15px 15px;
}


/*Menu header info*/

.display.name {
  font-size: 14px;
  font-weight: 500;
  color: #303238;
  line-height: 1.5;
}

.display.mail {
  font-size: 13px;
  color: #1E70EB;
  line-height: 1.5;
}

hr.divider-menu {
  border-top: 1px solid #e0e0e0;
}


/*Text Link css*/

.mnu_margin {
  margin: 7px 0;
}

.user_menu.item>a {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  padding: 8px 15px;
  color: #212629;
}

.user_menu.item:hover>a {
  color: #fff;
  background: #1E70EB;
  transition: all 0.2s;
}

.user_menu.item>a .link_text {
  font-size: 14px;
  color: #212629;
}

.user_menu.item:hover>a .link_text {
  color: #fff;
}


/*Icon Items Menu*/

.icn_menu:before,
.icon_menu:after {
  margin: 0px;
  padding: 0px;
  font-size: 16px
}

.icn_menu {
  margin-right: 10px;
  display: flex !important;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
}


/* User Menu For header website */

.mts_menu_container {
  display: flex;
  flex-direction: column;
  align-content: flex-end;
  align-items: flex-end;
}

.dropdown_box {
  position: absolute;
  margin-top: 20px;
}

.mts_dropdown_content {
  background-color: #fff;
  min-width: 160px;
  width: 240px;
  border-radius: 6px;
  overflow-x: hidden;
  overflow-y: hidden;
  box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px;
  z-index: 999;
  position: relative;
  visibility: hidden;
  opacity: 0;
  top: 50px;
  height: 0;
  transition: visibility 0.2s, max-height 0.2s, opacity 0.2s, top 0.2s, height 0.2s;
}

.mts_dropdown_content.show {
  height: 100%;
  visibility: visible;
  opacity: 1;
  top: 0;
  transition: visibility 0.2s, max-height 0.2s, opacity 0.2s, top 0.2s, height 0.2s;
}
<button onclick="userMenu()" class="user_menu_button">
     <i class="icn_button fa-solid fa-bars"></i>
     <span class="txt_button">Account</span>
</button>

<div class="mts_menu_container">
  <div class="dropdown_box">

    <div id="mts_menu" class="mts_dropdown_content">
      <div class="user_menu header">
        <span class="display name">Ciao [display_name]</span>
        <span class="display mail">[display_email]</span>
      </div>

      <hr class="divider-menu">

      <div class="mnu_margin">
        <div class="user_menu item">
          <a href="/account">
            <i class="icn_menu fa-regular fa-user"></i>
            <span class="link_text">Dashboard</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="ordini">
            <i class="icn_menu fa-regular fa-basket-shopping"></i>
            <span class="link_text">I miei ordini</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="libreria">
            <i class="icn_menu fa-regular fa-cloud-arrow-down"></i>
            <span class="link_text">Downloads</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="impostazioni">
            <i class="icn_menu fa-regular fa-gear"></i>
            <span class="link_text">Impostazioni</span>
          </a>
        </div>

        <div class="user_menu item last">
          <a href="wp-login.php?action=logout">
            <i class="icn_menu fa-regular fa-arrow-right-from-bracket"></i>
            <span class="link_text">Logout</span>
          </a>
        </div>
      </div>

    </div>
  </div>
</div>

<div id="container_test">container test</div>
Barmar
  • 741,623
  • 53
  • 500
  • 612
Snorlax
  • 183
  • 4
  • 27
  • Does this answer your question? [Javascript removeEventListener not working inside a class](https://stackoverflow.com/questions/33859113/javascript-removeeventlistener-not-working-inside-a-class) – PlumCake2022 Jun 27 '22 at 18:07
  • What are you talking about? There are no JS classes here. – Barmar Jun 27 '22 at 18:09
  • @Barmar I mean the css class from `.mts_dropdown_content.show.` It is the `show` class that is added or removed with js toggle. I would like to remove it always with js but by clicking on the body. – Snorlax Jun 27 '22 at 18:15
  • My comment was meant for @PlumCake2022. The question they linked to is related to JavaScript classes, not CSS classes. You also don't have any calls to `removeEventListener()`. – Barmar Jun 27 '22 at 18:16
  • Uhps! Sorry. Anyway I'm trying to look around for similar posts, but on my own I can't, I'm pretty new to all of this. – Snorlax Jun 27 '22 at 18:20

2 Answers2

1

When you click on the button to open the menu, the menu is opened, then the event bubbles out to document. Since the button isn't inside the menu, the menu closes.

Yuo should use event.stopPropagation in the button code to prevent this.

var usermenu = document.querySelector(".user_menu_button");

function userMenu(event) {
  event.stopPropagation();
  var x = document.getElementById("mts_menu");
  if (x.classList.toggle("show")) {
    usermenu.innerHTML = '<i class="icn_button fa-solid fa-xmark"></i><span class="txt_button">Account</span>';
  } else {
    usermenu.innerHTML = '<i class="icn_button fa-solid fa-bars"></i><span class="txt_button">Account</span>';
  }
}

const closing = document.querySelector("#mts_menu");

document.addEventListener("click", (evt) => {
  if (!evt.target.closest("#mts_menu")) closing.classList.remove("show");
});
/*Button Toggle Menu*/

.user_menu_button {
  display: flex;
  align-content: center;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 32px;
  background: #3D4350!important;
  border-radius: 4px;
  padding: 12px;
  font-size: 14px!important;
  line-height: 2;
}

.icn_button {
  margin: 0;
}

.icn_button:before,
.icn_button:after {
  margin: 0;
}

.txt_button {
  margin-left: 10px;
  color: #fff;
  font-size: 14px;
  font-weight: 400;
}


/*Items menu*/

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

.user_menu.header {
  padding: 15px 15px;
}


/*Menu header info*/

.display.name {
  font-size: 14px;
  font-weight: 500;
  color: #303238;
  line-height: 1.5;
}

.display.mail {
  font-size: 13px;
  color: #1E70EB;
  line-height: 1.5;
}

hr.divider-menu {
  border-top: 1px solid #e0e0e0;
}


/*Text Link css*/

.mnu_margin {
  margin: 7px 0;
}

.user_menu.item>a {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  padding: 8px 15px;
  color: #212629;
}

.user_menu.item:hover>a {
  color: #fff;
  background: #1E70EB;
  transition: all 0.2s;
}

.user_menu.item>a .link_text {
  font-size: 14px;
  color: #212629;
}

.user_menu.item:hover>a .link_text {
  color: #fff;
}


/*Icon Items Menu*/

.icn_menu:before,
.icon_menu:after {
  margin: 0px;
  padding: 0px;
  font-size: 16px
}

.icn_menu {
  margin-right: 10px;
  display: flex !important;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
}


/* User Menu For header website */

.mts_menu_container {
  display: flex;
  flex-direction: column;
  align-content: flex-end;
  align-items: flex-end;
}

.dropdown_box {
  position: absolute;
  margin-top: 20px;
}

.mts_dropdown_content {
  background-color: #fff;
  min-width: 160px;
  width: 240px;
  border-radius: 6px;
  overflow-x: hidden;
  overflow-y: hidden;
  box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px;
  z-index: 999;
  position: relative;
  visibility: hidden;
  opacity: 0;
  top: 50px;
  height: 0;
  transition: visibility 0.2s, max-height 0.2s, opacity 0.2s, top 0.2s, height 0.2s;
}

.mts_dropdown_content.show {
  height: 100%;
  visibility: visible;
  opacity: 1;
  top: 0;
  transition: visibility 0.2s, max-height 0.2s, opacity 0.2s, top 0.2s, height 0.2s;
}
<button onclick="userMenu(event)" class="user_menu_button">
     <i class="icn_button fa-solid fa-bars"></i>
     <span class="txt_button">Account</span>
</button>

<div class="mts_menu_container">
  <div class="dropdown_box">

    <div id="mts_menu" class="mts_dropdown_content">
      <div class="user_menu header">
        <span class="display name">Ciao [display_name]</span>
        <span class="display mail">[display_email]</span>
      </div>

      <hr class="divider-menu">

      <div class="mnu_margin">
        <div class="user_menu item">
          <a href="/account">
            <i class="icn_menu fa-regular fa-user"></i>
            <span class="link_text">Dashboard</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="ordini">
            <i class="icn_menu fa-regular fa-basket-shopping"></i>
            <span class="link_text">I miei ordini</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="libreria">
            <i class="icn_menu fa-regular fa-cloud-arrow-down"></i>
            <span class="link_text">Downloads</span>
          </a>
        </div>

        <div class="user_menu item">
          <a href="impostazioni">
            <i class="icn_menu fa-regular fa-gear"></i>
            <span class="link_text">Impostazioni</span>
          </a>
        </div>

        <div class="user_menu item last">
          <a href="wp-login.php?action=logout">
            <i class="icn_menu fa-regular fa-arrow-right-from-bracket"></i>
            <span class="link_text">Logout</span>
          </a>
        </div>
      </div>

    </div>
  </div>
</div>
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thank you for your time. Works well. Curiosity, I also tried the solution proposed by @Ahmed ALABSI and it works too. What is the difference between `event.stopPropagation ();` and `&&! evt.target.closest ('.user_menu_button'))` ? What do they have in common? Sorry, I'm a fan trying to learn new notions. – Snorlax Jun 27 '22 at 18:28
  • 1
    They're completely different. His solution just ignores clicks inside the button, the same way that it ignores clicks inside the dropdown. My solution says that when you click inside the button, don't allow that click event to be processed by the document. – Barmar Jun 27 '22 at 18:31
1
  • In your "not working code" you need to exclude the button that show the menu too.
document.addEventListener("click", (evt) => {
      if (!evt.target.closest("#mts_menu")  && !evt.target.closest('.user_menu_button')) closing.classList.remove("show");
    });
Ahmed ALABSI
  • 21
  • 1
  • 3