Interactive dropdown menu designed to trigger different content selections upon clicking

Here is a dropdown menu solution that I have implemented: http://jsfiddle.net/ftymhs8s/

The problem I am facing is that when I click on a different line, I want to hide the currently displayed dropdown menu and show the dropdown menu of the clicked line. Additionally, I want the dropdown menu of the first line to always be displayed when people visit the website.

I believe I have described my issue correctly. Can someone please assist me with this?

// Dropdown Menu
var dropdown = document.querySelectorAll('.dropdown');
var dropdownArray = Array.prototype.slice.call(dropdown, 0);
dropdownArray.forEach(function(el) {
  var button = el.querySelector('a[data-toggle="dropdown"]'),
    menu = el.querySelector('.dropdown-menu'),
    arrow = button.querySelector('i.icon-arrow');

  button.onclick = function(event) {
    if (!menu.hasClass('show')) {
      menu.classList.add('show');
      menu.classList.remove('hide');
      arrow.classList.add('open');
      arrow.classList.remove('close');
      event.preventDefault();
    } else {
      menu.classList.remove('show');
      menu.classList.add('hide');
      arrow.classList.remove('open');
      arrow.classList.add('close');
      event.preventDefault();
    }
  };
})

Element.prototype.hasClass = function(className) {
  return this.className && new RegExp("(^|\\s)" + className + "(\\s|$)").test(this.className);
};
ul {
  list-style: none
}

.dropdown a {
  text-decoration: none;
}

.dropdown [data-toggle="dropdown"] {
  position: relative;
  display: block;
  color: black;
  padding: 10px;
}

.dropdown .dropdown-menu {
  max-height: 0;
  overflow: hidden;
}

.dropdown .dropdown-menu li {
  padding: 0;
}

.dropdown .dropdown-menu li a {
  display: block;
  padding: 10px 10px;
}

.dropdown .show {
  display: block;
  max-height: 9999px;
  margin-left: 50px;
}

.dropdown .hide {
  max-height: 0;
}
<div class="container">
  <ul>
    <li class="dropdown">
      <a href="#" data-toggle="dropdown">First Menu</a>
      <ul class="dropdown-menu">
        <li><a href="#">Home</a></li>
        <li><a href="#">About Us</a></li>
        <li><a href="#">Services</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </li>
    <li class="dropdown">
      <a href="#" data-toggle="dropdown">Second Menu</a>
      <ul class="dropdown-menu">
        <li><a href="#">Home</a></li>
        <li><a href="#">About Us</a></li>
        <li><a href="#">Services</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </li>
    <li class="dropdown">
      <a href="#" data-toggle="dropdown">Third Menu </a>
      <ul class="dropdown-menu">
        <li><a href="#">Home</a></li>
        <li><a href="#">About Us</a></li>
        <li><a href="#">Services</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </li>
  </ul>
</div>

Answer №1

When dealing with multiple collapsible elements where only one can be open at a time, the behavior resembles that of an accordion component. The concept involves closing all collapsibles initially and then opening the one selected by the user. This behavior is demonstrated in the following example using Event Delegation.

In addition, I couldn't help but notice that you created a hasClass function... or Class. However, this isn't necessary as you can simply use: node.classList.contains('class')

The demo includes detailed explanations within the code.

Check out the Demo Below:

/* Added .main class to parent <ul>
|| By adding the eventListener to the
|| parent of multiple clickable nodes
|| and using e.target property to find
|| the exact node actually clicked, we
|| have just needed the <ul> to listen
|| rather than 3 separate <li>
|| This is part of Event Delagation
*/
var main = document.querySelector('.main');

main.addEventListener('click', handleAccordion, false);

function handleAccordion(e) {

  /* Extract all .dropdown-menu elements into a NodeList
  || and then convert it to an array
  */
  var dropdownArray = Array.from(document.querySelectorAll('.dropdown-menu'));

  /* Gather all links inside the .dropdown-menus into
  || a NodeList and then convert it to an array
  */
  var linkArray = Array.from(document.querySelectorAll('a + .dropdown-menu a'));

  /* If the clicked node (e.target) is NOT the
  || node listening for the event (e.currentTarget
  || ul.main), then...
  */
  if (e.target !== e.currentTarget) {

    // Assign e.target to tgr variable
    var tgr = e.target;

    /* If tgr has data-toggle attribute...
    || Find tgr's next sibling (.dropdown-menu)
    || Iterate through dropdownArray with a
    || for...of loop
    || Remove .show and add .hide on
    || each .dropdown-menu in dropdownArray
    || Then add .show and remove .hide
    || on target
    || Finally stop the click event from
    || bubbling up, thereby preventing anything
    || else from being triggered.
    */
    if (tgr.hasAttribute('data-toggle')) {

      // Prevent <a> from jumping
      e.preventDefault();
      var target = tgr.nextElementSibling;
      for (let dropdown of dropdownArray) {
        dropdown.classList.remove('show');
        dropdown.classList.add('hide');
      }
      target.classList.add('show');
      target.classList.remove('hide');
    } else {
      return;
    }
    e.stopPropagation();
  }
}
html,
body,
.container {
  height: 100%;
  width: 100%;
}

.main,
section,
article {
  margin-bottom: 100vh;
}

ul {
  list-style: none
}

.dropdown a {
  text-decoration: none;
}

.dropdown [data-toggle="dropdown"] {
  position: relative;
  display: block;
  color: black;
  padding: 10px;
}

.dropdown .dropdown-menu {
  max-height: 0;
  overflow: hidden;
}

.dropdown .dropdown-menu li {
  padding: 0;
}

.dropdown .dropdown-menu li a {
  display: block;
  padding: 10px 10px;
}

.dropdown .show {
  display: block;
  max-height: 9999px;
  margin-left: 50px;
}

.dropdown .hide {
  max-height: 0;
}
<div id='home' class="container">
  <ul class='main'>
    <li class="dropdown">
      <a href="#" data-toggle="dropdown">First Menu</a>
      <ul class="dropdown-menu">
        <li><a href="#home">Home</a></li>
        <li><a href="#about">About Us</a></li>
        <li><a href="#services">Services</a></li>
        <li><a href="#contact">Contact</a></li>
      </ul>
    </li>
    <li class="dropdown">
      <a href="#" data-toggle="dropdown">Second Menu</a>
      <ul class="dropdown-menu">
        <li><a href="#1">Section I</a></li>
        <li><a href="#2">Section II</a></li>
        <li><a href="#3">Section III</a></li>
        <li><a href="#4">Section IV</a></li>
      </ul>
    </li>
    <li class="dropdown">
      <a href="#" data-toggle="dropdown">Third Menu</a>
      <ul class="dropdown-menu">
        <li><a href="https://example.com">Example</a></li>
        <li><a href="https://example.com">Example</a></li>
        <li><a href="https://example.com">Example</a></li>
        <li><a href="https://example.com">Example</a></li>
      </ul>
    </li>
  </ul>
  <article id='about'>
    <h2>About</h2>
  </article>

  <article id='services'>
    <h2>Services</h2>
  </article>

  <article id='contact'>
    <h2>Contact</h2>
  </article>

  <hr>

  <section id='1'>
    <h2>Section I</h2>
  </section>


  <section id='2'>
    <h2>Section II</h2>
  </section>


  <section id='3'>
    <h2>Section III</h2>
  </section>


  <section id='4'>
    <h2>Section IV</h2>
  </section>

</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Expanding the filtering capabilities in AngularJS with additional selection options

I'm working with a repeater and implementing a filter to query the items, like this: ng-repeat="item in items | filter:query" Now, I want to incorporate a select option as an additional filter. Any ideas on how to integrate this with the existing fi ...

Tips for creating multiple functions within a single JavaScript function

Is there a way to combine these two similar functions into one in order to compress the JavaScript code? They both serve the same purpose but target different CSS classes. The goal is to highlight different images when hovering over specific list items - ...

Encountering a 404 error while attempting to establish a connection between Express and React

My attempt to make a simple API request for bitcoin values is encountering some issues. When I enter in my Chrome browser, I receive a "Cannot Get /" message with a 404 error in the dev tools stating "GET 404 (Not Found)". However, when I visit , I succ ...

Changing the color of a single child object in Threejs

Hey there, I'm facing an issue with changing the color of a specific element in my 3D Model (imported using the GLTFLoader). I've included some images showcasing the model's structure. The element I'm trying to target is the one highli ...

Changing p tags to br tags on tinyMCE

I need assistance with using tinyMCE as an inline editor. I want it so that when a user is in edit mode and presses enter, <br /> is inserted instead of <p>. I have gone through the manual and FAQ but have not been able to achieve this successf ...

The Vue component that was added dynamically is failing to render

When I have a section of HTML with Vue components inside it, coming from a server, and then insert it on the page by clicking a button, the components are not rendering or showing up. Here is the HTML that gets inserted when the button is clicked: <sec ...

What could be causing site container not to respond to height:auto?

I have encountered an issue while developing a website using absolute height values. I am puzzled as to why the height:auto property is not working for me. Can anyone provide some insight on this? HTML Structure <div id="site-content"> <div id=" ...

What is the process for extracting data from latitude and longitude in order to generate a marker on Google Maps using a script

I have an HTML input text and want to pass coordinates to create a marker on Google maps. Check out the code here: jsfiddle.net/#&togetherjs=r3M9Kp7ff7 What is the best way to transfer this data from HTML to JavaScript? <label for="latitude">L ...

Having difficulties redirecting with the button's onclick function

I'm struggling to redirect users to another page using a button (assuming the hostname is localhost:8000) $('#getFruit').attr('onclick', window.location.host + '/getFruit/banana') <script src="https://cdnjs.cloudfl ...

What is the reason behind receiving the error message "Uncaught SyntaxError: Unexpected token o" while using $.parseJSON() and JSON.parse()

After searching extensively on both SO and Google, I have not been able to find a solution to my issue and I'm feeling stuck. The problem arises when I try to parse an array returned from a PHP page using echo json_encode(). Here is what the array loo ...

What is the reason for MongoDB throwing an error when an array of objects is treated as a string?

I'm finding myself quite puzzled by the error I'm encountering, as it appears to be very similar to me. When running a Mongoose Seed, the model structure looks like this: User Model const mongoose = require("mongoose") const Schema = mongoose. ...

What is the method to activate sequential selection through JSON response?

I just started exploring angularjs and I'm interested in selecting values step by step. But first... Here's the JSON response I received: [ { "countryname": "India", "states": [ { "statename": "Karnataka", ...

Leveraging Bootstrap grids with JavaScript

$.getJSON('http://localhost/REST_API/api/post/read.php', function(data) { var count = 0; data.forEach(obj => { if(count % 3 === 0) { $( ".monsterlist" ).append("<div class='row'>" ...

Organize the rows of the table based on two variables

Currently, I am tackling the challenge of dynamically sorting a table using javascript/jQuery. The table rows come with two crucial attributes: deadline (measured in Unix timestamp) status (either 1 or 2) The primary requirement is to sort the table by s ...

What is the method for implementing a dropdown box with select and non-select checkboxes, similar to the example shown in the image, using AngularJS?

https://i.sstatic.net/CTx8K.jpg I am seeking assistance in incorporating the image below into a dropdown menu using angularjs ...

Changing the order of rows and columns with CSS3 and DOM manipulation

I have implemented the following CSS code to alternate the background color of li elements. However, I am facing an issue where I need the CSS styling to remain consistent even if the rows have the .hidden class applied to them (with .hidden class having d ...

AngularJS - Issue with Checklist-Model not accurately reflecting changes in the model when checkboxes are toggled

Recently delving into AngularJS, I encountered a puzzling issue with the Checklist-Model directive. To replicate the problem, I followed one of their examples. Upon clicking a checkbox and invoking a function, the model reflects the update correctly on th ...

Error: The JS Exception has not been handled, as it is stating that 'undefined' is not an object when evaluating 'global.performance.now' in react-native-tvOS

I am currently working on a React-Native-tvOs app and despite following all the instructions from the react-native-tvOs GitHub page, I keep encountering an error when running the default template app. Even after running the script provided on the GitHub re ...

Receiving JSX from deeply nested dynamic imports in the latest version of NextJS 12 triggers an error message stating that [object Promise] is

There have been numerous proposed solutions for this particular issue on Stackoverflow, but none of them seem to work for my specific code. It appears that my situation is somewhat unique due to the nested dynamic imports involved: In a NextJS component, ...

What is the best way to integrate AJAX with draggable columns in a Laravel application?

I have successfully integrated draggable functionality for table columns using jQuery Sortable within my Laravel application. I am now facing the challenge of updating the database with the data from these columns through AJAX. Despite researching online ...