Born2DoubleUp
Born2DoubleUp

Reputation: 109

How to Close One Javascript Drop Down Menu When Opening Another

I'm not to familiar with JavaScript and I was hoping to get a little help with a problem I can't seem to fix. I currently have 2 Drop Down Menus on my website. One is a drop down menu for the navigation which is activated when clicking a hamburger menu icon. The second drop down is being used to show categories on my website. Currently when I click one drop down, I have to click it again in order to close it. If I click the second drop down without closing the first both will remain visible. What I would like to happen is two things. First I would like it so that if a user clicks anywhere outside of the div for the drop down menu it automatically closes. The second thing I would like to see happen is only have one drop down menu visible at a time. So if I click one and another drop down is open I want it to be closed. Hopefully I explained this well. Now onto the code I'm using.

The following is within my head.

<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function DropDownMenuNavigation() {
document.getElementById("b2DropDownMenuNav").classList.toggle("show");
}
function DropDownMenuCategory() {
document.getElementById("b2DropDownMenuCat").classList.toggle("show");  
}
</script>

Then I use this as the button to activate the navigation drop down menu. This is included within my body.

<div class="dropbtn" style="float: left;">
<button onclick="DropDownMenuNavigation()" class="dropbtn">&#9776; MENU</button>
</div>

and this what I use to include my category drop down menu.

<div class="dropbtn" style="float: left;">
<button onclick="DropDownMenuCategory()" class="dropbtn">CATEGORIES</button>
</div>

Now lastly is the css I use just on the off chance that helps any.

/* Dropdown Button */
.dropbtn {
background-color: #0066a2;
color: white;
padding: 1px;
font-size: 15px;
font-weight: bold;
border: none;
cursor: pointer;
}
.dropbtn a {
color: #FFFFFF;
text-decoration: none;
font-size: 15px;
font-weight: bold;
}

/* The container <div> - needed to position the dropdown content */
.dropdown {
float: left;
position: relative;
display: inline-block;
}

/* Dropdown Content (Hidden by Default) */
.dropdown-content {
display: none;
position: absolute;
background-color: #0066a2;
min-width: 260px;
max-width: 960px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}

/* Links inside the dropdown  */
.dropdown-content a {
color: #000000;
text-decoration: none;
}

/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;} 

So what would be the best method to go about doing what I'm asking? Could someone maybe lend me a hand and point me in the right direction. Thanks a lot and I appreciate any help you could lend me.

Upvotes: 2

Views: 9369

Answers (3)

Ayush Mandowara
Ayush Mandowara

Reputation: 500

To do this, you can add custom JS functions that will open dropdowns based on element ID, and when one dropdown is opened, all others will be closed. You can create a function that closes all the dropdowns. Then, in your "open" function, call the "close_all" function first.

Here's a working snippet.

// Functions for Interactive File Menu Bar 
// - Click Butoon to Open Dropdown
// - Clicking one dropdown closes all other
// - Clicking outside the file menu bar will close all the dropdown.

function open_dropdown(element_id) {
  console.log('Opening Dropdown:', element_id)
  close_all_dropdowns()
  document.getElementById(element_id).style.display = 'block';
}

// Close the dropdown if the user clicks outside of it
function close_dropdown(element) {
  console.log('I am closing dropdown:', element)
  element.style.display = 'none'
}

// Close all dropdowns.
function close_all_dropdowns() {
  var dropdowns = document.getElementsByClassName('dropdown-content')
  for (var i = 0; i < dropdowns.length; i++) {
    close_dropdown(dropdowns[i]);
  }
}

// Close all dropdowns when clicking outside.
window.onclick = function (e) {
  if (!e.target.matches('.dropbtn')) {
    close_all_dropdowns()
  }
}
/* Styles for the File Menu Bar. */
.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}


.dropdown-content a {
  float: none;
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
  text-align: left;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="https://unpkg.com/98.css">
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
  <title>RetroNet</title>
</head>

<body>

  <div class="window" style="width: 100%">
    <div class="title-bar">
      <div class="title-bar-text">Welcome to RetroNet!</div>
      <div class="title-bar-controls">
        <button aria-label="Minimize"></button>
        <button aria-label="Maximize"></button>
        <button aria-label="Close"></button>
      </div>
    </div>

    <!-- Main menu -->
    <div class="window-body">
      <div class="dropdown">
        <button class="dropbtn" onclick="open_dropdown('dd_file')">File</button>
        <div class="dropdown-content" id="dd_file">
          <a href="#">Open</a>
          <a href="#">Close</a>
          <a href="#">Settings</a>
        </div>
      </div>
      <div class="dropdown">
        <button class="dropbtn" onclick="open_dropdown('dd_edit')">Edit</button>
        <div class="dropdown-content" id="dd_edit">
          <a href="#">Cut</a>
          <a href="#">Copy</a>
          <a href="#">Paste</a>
        </div>
      </div>
      <div class="dropdown">
        <button class="dropbtn" onclick="open_dropdown('dd_view')">View</button>
        <div class="dropdown-content" id="dd_view">
          <a href="#">Toggle CSS</a>
          <a href="#">Toggle Javascript</a>
        </div>
      </div>
      <div class="dropdown">
        <button class="dropbtn" onclick="open_dropdown('dd_tools')">Tools</button>
        <div class="dropdown-content" id="dd_tools">
          <a href="#">Not Decided</a>
        </div>
      </div>
      <div class="dropdown">
        <button class="dropbtn" onclick="open_dropdown('dd_favorite')">Favourties</button>
        <div class="dropdown-content" id="dd_favorite">
          <a href="#">Add New Favorite</a>
          <a href="#">Add this Page to Favorites</a>
          <a href="#">Show Favorites</a>
        </div>
      </div>
      <div class="dropdown">
        <button class="dropbtn" onclick="open_dropdown('dd_help')">Help</button>
        <div class="dropdown-content" id="dd_help">
          <a
            href="https://github.com/ayushxx7/summer-code-jam-2020/blob/master/adventurous-anteaters/README.md">README</a>
        </div>
      </div>
    </div>
  </div>

</body>

</html>

Upvotes: 1

Alexander
Alexander

Reputation: 658

The onclick attribute shouldn’t include the (). It should look like this:

<button onclick="DropDownMenuNavigation" class="dropbtn">&#9776; MENU</button>

Or—even better—don’t put the event listener inline, put it in the script.

Also, remove the “show” class from the other dropdown when the button is pressed.

See here:

document.getElementById('menudropbtn').addEventListener('click', function () {
	document.getElementById('b2DropDownMenuNav').classList.toggle('show')
  document.getElementById('b2DropDownMenuCat').classList.remove('show')
})

document.getElementById('categoriesdropbtn').addEventListener('click', function () {
	document.getElementById('b2DropDownMenuCat').classList.toggle('show')
  document.getElementById('b2DropDownMenuNav').classList.remove('show')
})
/* Dropdown Button */
.dropbtn {
  background-color: #0066a2;
  color: white;
  padding: 1px;
  font-size: 15px;
  font-weight: bold;
  border: none;
  cursor: pointer;
}

.dropbtn a {
  color: #FFFFFF;
  text-decoration: none;
  font-size: 15px;
  font-weight: bold;
}


/* The container <div> - needed to position the dropdown content */
.dropdown {
  float: left;
  position: relative;
  display: inline-block;
}


/* Dropdown Content (Hidden by Default) */
.dropdown-content {
  display: none;
  position: absolute;
  background-color: #0066a2;
  min-width: 260px;
  max-width: 960px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}


/* Links inside the dropdown  */
.dropdown-content a {
  color: #000000;
  text-decoration: none;
}


/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {
  display: block;
}
<div class="dropbtn" style="float: left;">
  <button class="dropbtn" id="menudropbtn">&#9776; MENU</button>
  <div class="dropdown">
    <div class="dropdown-content" id="b2DropDownMenuNav">
      <a>Something</a>
    </div>
  </div>
</div>

<div class="dropbtn" style="float: left;">
  <button class="dropbtn" id="categoriesdropbtn">CATEGORIES</button>
  <div class="dropdown">
    <div class="dropdown-content" id="b2DropDownMenuCat">
      <a>Something else</a>
    </div>
  </div>
</div>

Upvotes: 2

HMR
HMR

Reputation: 39270

Maybe the following code can help. You can use custom event to have module items (like menu, popup or such) communicate to each other.

If a menu button is clicked then you can dispatch a custom event. Any other item on the page may do something according to what this event is (like pausing a game when a main menu is opened).

// find menu-content in item (=menu-button) and return
//  "none" if menu-content.style.display is "block"
//  "block" if menu-content.style.display is not "block"
const toggle =
  (item) => {
    const content = 
      item.querySelector("[x-role=\"menu-content\"]");
    return content.style.display === "block"
      ? "none"
      : "block"
  }
;
// set menu-content found in item (=menu-button) to
//  none or block
const changeDisplay =
  (item,display) =>
    item.querySelector("[x-role=\"menu-content\"]")
      .style.display = display;
// when menu-button is clicked
const menuButtonClicked = 
  e => {
    //get the toggled content style
    //  if current style is block then
    //  toggled is none and vice versa
    const style = toggle(e.target);
    //hide all menus, in the for each we
    //  added an event listener for "menu-click" event
    //  the listener will hide the menu
    var evt = new Event("menu-click",{});
    document.body.dispatchEvent(evt);
    //set style of the current
    changeDisplay(e.target,style);
  }
;
//for each menu-botton role 
//  I am not using css selectors on class, class is for style, 
//  user defined properties can be used for behavior. 
//  If you mix this up then you can break style, behavior
//  or both when changing behavior or style
document.querySelectorAll("[x-role=\"menu-button\"]")
  .forEach(
    x => {
      //when clicked let menuButtonClicked handle it
      x.addEventListener(
        "click"
        ,menuButtonClicked
      );
      //listen to custom event called "menu-click"
      //  set display to none when this happens
      //  if you were to dynamically add and remove
      //  menu items then you should remove the event
      //  listeners when you remove the menu
      document.body.addEventListener(
        "menu-click"
        ,e => changeDisplay(x,"none")        
      );
    }
  )
;
.menu-button {
  cursor: pointer;
}
.menu-content {
  display:none;
}
<div class="menu-button" x-role="menu-button">
 menu1
 <div class="menu-content" x-role="menu-content">
  <ul>
    <li>one</li>
    <li>two</li>
  </ul>
 </div>
</div>
<div class="menu-button" x-role="menu-button">
 menu2
 <div class="menu-content" x-role="menu-content">
  <ul>
    <li>three</li>
    <li>four</li>
  </ul>
 </div>
</div>
<div class="menu-button" x-role="menu-button">
 menu3
 <div class="menu-content" x-role="menu-content">
  <ul>
    <li>five</li>
    <li>six</li>
  </ul>
 </div>
</div>

Upvotes: 0

Related Questions