javascripthtmljquerycss-selectorspseudo-class

How to find the following HTML Element


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?


Solution

  • Regardless of the already above commented approach of mine ...

    A real generic one with no jQuery needed, just the closest and the querySelector 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 ...

    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>