Can't open submenu when using queryselectorall

Hi,

I have a mobile navigation with two dropdown menus. Here is the markup of the nav:

<div id="mobile-menu" class="mobile-menu container fixed">
                    <ul>
                        <li><a href="#">Home</a></li>
                        <li class="dropdown">
                            <a href="#">Articles <i class="bi bi-chevron-down"></i></a>
                            <ul class="submenu hidden">
                                <li><a href="#">Submenu item 1</a></li>
                                <li><a href="#">Submenu item 2</a></li>
                            </ul>
                        </li>
                        <li><a href="#">Contact</a></li>

                        <li class="dropdown">
                            <a href="#">My account <i class="bi bi-chevron-down"></i></a>
                            <ul class="submenu hidden">
                                <li><a href="#">Dashboard</a></li>
                                <li><a href="#">Profile</a></li>
                            </ul>
                        </li>
                    </ul>
                </div>

The dropdown menus should open/expand when clicked. Originally I grabbed the dropdown and submnenu classes like this:

const mobileDropdown = document.querySelector(".dropdown");
const mobileSubMenu = document.querySelector('.submenu');

and used an eventlistener to toggle the “hidden” class which is just a display:none

mobileDropdown.addEventListener('click', () => {
    mobileSubMenu.classList.toggle('hidden');
});

The problem with this is that this will only open the first dropdown menu and I cannot open the second.

When I try ot use querySelectorAll instead of just querySelector then i get thiserror: Uncaught typeError addEventListener is not a function

Here I read that with querySelectorAll I need to use a for or foreach loop.

but i think I’m messing it up. I tried this:

const mobileDropdown = document.querySelectorAll(".dropdown");
const mobileSubMenu = document.querySelector('.submenu');

mobileDropdown.forEach(md => md.addEventListener('click', () => {
    mobileSubMenu.classList.toggle('hidden');
}));

Now I don’t get an error, I can open the first dropdown menu, but when I try to open the second dropdown menu, the first one opens. What am I doing wrong?

So you’re telling every dropdown to toggle the class on mobileSubMenu.

mobileSubMenu is exactly 1 element.

Shortest answer I can give you with what you already know:
Inside your foreach, select the submenu underneath that element (Hint: document is not the only source for which querySelector() is valid!), and toggle that element’s classlist.

1 Like

If you add a class to the element called .dropdown instead then you can open the nested submenu with css instead.

e.g.
.hidden {
  display: none;
}
.open > .hidden {
  display: block;
}

const mobileDropdown = document.querySelectorAll(".dropdown");

mobileDropdown.forEach((md) =>
  md.addEventListener("click", () => {
    md.classList.toggle("open");
  })
);
1 Like

S’weird, i would have thought the element-specific (.hidden) would have overridden the child-inheritance (.open > hidden)…but nope.

I think you are just having a ’ Mental hiccup’ as you know this :slight_smile:

There is no inheritance going on in the rule .open > .hidden. It’s directly targeting .hidden and as it has higher specificity it wins out. I know you know this :slight_smile: