I have a menu with submenu with a structure like this:
<ul>
<li>
<a class="navigation-item">Menu Item 1</a>
<a class="submenu-toggle"><a/>
<ul class="submenu">
<li>
<a class="navigation-item">Submenu Item 1</a>
</li>
</ul>
</li>
</ul>
I want to add a class to submenu-toggle if Submenu Item 1 is selected. How can I find the submenu-toggle in this case?
closest('li')
won' work, this will only find the li after the submenu. closest('li > .submenu-toggle')
doesn't work either because the .submenu-toggle
has to be an ancestor of the submenu item. If I could select the correct li, I could then use find('.submenu-toggle')
. But how do I select the correct li?
Regardless of the already above commented approach of mine ...
A real generic one with no jQuery needed, just the
closest
and thequerySelector
methods together with css-selectors ...yourInnerElementReference.closest('li:has(.submenu-toggle)').querySelector('.submenu-toggle')
. Have a look at the functional :has() CSS pseudo-class
... there is a big chance that the OP does not need any JavaScript based solution at all in order to style the toggle-element. This of cause does not imply that JS is entirely obsolete in order to achieve all of the required menu-behaviors, but I would give even that one a real good try.
.menu {
display: inline-block;
li {
margin: 4px 0;
}
> li {
position: relative;
margin: 12px 0;
> .toggle {
position: absolute;
top: 0;
right: 0;
}
}
}
.submenu:has(a:active, a:focus, a:target) {
+ .toggle {
color: green;
}
}
<ul class="menu">
<li>
<a href="#">Menu Item 1</a>
<ul class="submenu">
<li>
<a href="#">Submenu Item 1.1</a>
</li>
<li>
<a href="#">Submenu Item 1.2</a>
</li>
</ul>
<div tabindex="0" class="toggle">toggle</div>
</li>
<li>
<a href="#">Menu Item 2</a>
<ul class="submenu">
<li>
<a href="#">Submenu Item 2.1</a>
</li>
<li>
<a href="#">Submenu Item 2.2</a>
</li>
</ul>
<div tabindex="0" class="toggle">toggle</div>
</li>
</ul>
And whereas the above example-code shows an HTML/CSS-only approach which achieves what the OP did ask for, the next provided example code can be a solution for what the OP actually might really need in terms of ...
the behavior which one would expect from a nested menu where the submenu structure can be toggled in between its expanded and collapsed state,
a semantically correct HTML structure and its related CSS rules
as well as a minimal JS footprint.
function handleNavigationClick(evt) {
const toggleElement = evt.target.closest('.expand-toggle');
if (toggleElement) {
toggleElement.classList.toggle('indicate-expanded');
}
}
document
.querySelector('nav')
.addEventListener('click', handleNavigationClick);
li {
margin: 4px 0;
}
menu {
margin: 0;
padding: 0;
list-style: none;
}
menu:not(.submenu) {
display: inline-block;
> li {
position: relative;
margin: 12px 0;
}
}
.submenu {
display: none;
}
[type="button"].expand-toggle {
overflow: hidden;
position: relative;
width: 1em;
border: none;
background: none;
text-indent: 1em;
text-wrap: nowrap;
&:after {
z-index: -1;
position: absolute;
left: -.77em;
top: .07em;
content: ">";
}
&.indicate-expanded {
&:after {
top: .04em;
content: "v";
}
~ .submenu {
display: block;
margin-left: 2em;
}
}
}
<nav>
<menu>
<li>
<button type="button" class="expand-toggle" title="toggle submenu display">
toggle submenu display
</button>
<a href="#">Menu Item 1</a>
<menu class="submenu">
<li>
<a href="#">Submenu Item 1.1</a>
</li>
<li>
<a href="#">Submenu Item 1.2</a>
</li>
</menu>
</li>
<li>
<button type="button" class="expand-toggle" title="toggle submenu display">
toggle submenu display
</button>
<a href="#">Menu Item 2</a>
<menu class="submenu">
<li>
<a href="#">Submenu Item 2.1</a>
</li>
<li>
<a href="#">Submenu Item 2.2</a>
</li>
</menu>
</li>
</menu>
</nav>