Skip to main content
added 433 characters in body
Source Link
mplungjan
  • 180.4k
  • 29
  • 184
  • 247

I am guessing the HTML

You need to delegate and no need for jQuery I use the :scope selector for the first time

I have updated the HTML and the JS to match your HTML.

I have also tried to tighten some of the selectors to match WP

Please note that if you already have a WP theme with JS that wants to toggle your menu, then my script will likely interfere and perhaps your question is one for SuperUser or the WP tag

document.addEventListener('DOMContentLoaded', function () {
  const navSelectornav = document.querySelector('.main-nav';nav') || document;
  const selector = 'li.menu-item-has-children > ul.sub-menu';

  // Open /Hide closeall submenus on hoverinitially
  documentnav.querySelectorAll(navSelectorselector).forEach(function +(ul) '{
    ul.hidden = true;
  });

  // Hover open/close 
  nav.querySelectorAll('li.menu-item-has-children').forEach(function (li) {
 
    const sub = li.querySelector(':scope > ul'ul.sub-menu');
    if (!sub) return;

    // Hide submenu initially
    sub.hidden = true;

    li.addEventListener('mouseenter', function () {
      sub.hidden = false;
    });

    li.addEventListener('mouseleave', function () {
      sub.hidden = true;
    });
  });

  // Click to toggle submenus (touch devices + nested menus)
  documentnav.addEventListener('click', function (e) {
    const linka = e.target.closest(
      navSelector + ' 'li.menu-item-has-children > a'
    );
    if (!linka) return;

    const sub = linka.nextElementSibling;
    if (!sub || sub.tagName !== 'UL' || !sub.classList.contains('sub-menu')) return;
 
    // Toggle visibility
    sub.hidden = !sub.hidden;

    // PreventToggle linkonly, navigationdo whennot togglingnavigate on first tap
    e.preventDefault();

    // Prevent bubbling to any "click outside" handlers
    e.stopPropagation();
  });

  // Optional: close all menus when clickingclick outside thecloses navigationall
  document.addEventListener('click', function (e) {
    const nav = document.querySelector(navSelector);
    if (!nav || nav.contains(e.target)) return;

    nav.querySelectorAll('.menu-item-has-children > ul'selector).forEach(function (ul) {
      ul.hidden = true;
    });
  });
});
<nav class="main-nav" aria-label="Hoofdmenu">
  <ul class="menu">
    <li class="menu-item menu-item-has-children">
      <a href="/how-we-support/">How we support</a>
  
     <ul class="sub<!-menu">
- WordPress-rendered submenu -->
     <li class="menu-item"><a<ul href="/how-we-support/optionclass="sub-1/">Option 1</a></li>
menu">
        <li class="menu-item menu-item-hastype-children">post_type menu-item-object-page menu-item-2060">
          <a href="/how-we-support/optionhref="#">Cross-2/">Optionfunctional 2<training &amp; tools</a>
          <ul class="sub-menu"></li>
            <li class="menu-item"><aitem href="/howmenu-weitem-support/optiontype-2/subpost_type menu-1/">Subitem-object-page 1</a><menu-item-2061">
          <a href="#">ESG Programme management support</li>a>
        </li>
        <li class="menu-item"><aitem href="/howmenu-weitem-support/optiontype-2/subpost_type menu-2/">Subitem-object-page 2</a></li>menu-item-2062">
          <<a href="#">Industry collaboration &amp; pre-competitive initiatives</ul>a>
        </li>
 
        <li class="menu-item"><aitem href="/howmenu-weitem-support/optiontype-3/">Optionpost_type 3<menu-item-object-page menu-item-2063">
          <a href="#">Multi-stakeholder intiatives</a><a>
        </li>
      </ul>
    </li>

    <li class="menu-item"><a href="/about/">About</a></li>
    <li class="menu-item"><a href="/contact/">Contact</a></li>
  </ul>
</nav>

I am guessing the HTML

You need to delegate and no need for jQuery I use the :scope selector for the first time

document.addEventListener('DOMContentLoaded', function() {
  const navSelector = '.main-nav';

  // Open / close submenus on hover
  document.querySelectorAll(navSelector + ' .menu-item-has-children').forEach(function(li) {
 
    const sub = li.querySelector(':scope > ul');
    if (!sub) return;

    // Hide submenu initially
    sub.hidden = true;

    li.addEventListener('mouseenter', function() {
      sub.hidden = false;
    });

    li.addEventListener('mouseleave', function() {
      sub.hidden = true;
    });
  });

  // Click to toggle submenus (touch devices + nested menus)
  document.addEventListener('click', function(e) {
    const link = e.target.closest(
      navSelector + ' .menu-item-has-children > a'
    );
    if (!link) return;

    const sub = link.nextElementSibling;
    if (!sub || sub.tagName !== 'UL') return;
 
    // Toggle visibility
    sub.hidden = !sub.hidden;

    // Prevent link navigation when toggling
    e.preventDefault();

    // Prevent bubbling to any "click outside" handlers
    e.stopPropagation();
  });

  // Optional: close all menus when clicking outside the navigation
  document.addEventListener('click', function(e) {
    const nav = document.querySelector(navSelector);
    if (!nav || nav.contains(e.target)) return;

    nav.querySelectorAll('.menu-item-has-children > ul').forEach(function(ul) {
      ul.hidden = true;
    });
  });
});
<nav class="main-nav" aria-label="Hoofdmenu">
  <ul class="menu">
    <li class="menu-item menu-item-has-children">
      <a href="/how-we-support/">How we support</a>
      <ul class="sub-menu">
        <li class="menu-item"><a href="/how-we-support/option-1/">Option 1</a></li>

        <li class="menu-item menu-item-has-children">
          <a href="/how-we-support/option-2/">Option 2</a>
          <ul class="sub-menu">
            <li class="menu-item"><a href="/how-we-support/option-2/sub-1/">Sub 1</a></li>
            <li class="menu-item"><a href="/how-we-support/option-2/sub-2/">Sub 2</a></li>
          </ul>
        </li>
 
        <li class="menu-item"><a href="/how-we-support/option-3/">Option 3</a></li>
      </ul>
    </li>

    <li class="menu-item"><a href="/about/">About</a></li>
    <li class="menu-item"><a href="/contact/">Contact</a></li>
  </ul>
</nav>

You need to delegate and no need for jQuery I use the :scope selector for the first time

I have updated the HTML and the JS to match your HTML.

I have also tried to tighten some of the selectors to match WP

Please note that if you already have a WP theme with JS that wants to toggle your menu, then my script will likely interfere and perhaps your question is one for SuperUser or the WP tag

document.addEventListener('DOMContentLoaded', function () {
  const nav = document.querySelector('.main-nav') || document;
  const selector = 'li.menu-item-has-children > ul.sub-menu';

  // Hide all submenus initially
  nav.querySelectorAll(selector).forEach(function (ul) {
    ul.hidden = true;
  });

  // Hover open/close 
  nav.querySelectorAll('li.menu-item-has-children').forEach(function (li) {
    const sub = li.querySelector(':scope > ul.sub-menu');
    if (!sub) return;

    li.addEventListener('mouseenter', function () {
      sub.hidden = false;
    });

    li.addEventListener('mouseleave', function () {
      sub.hidden = true;
    });
  });

  // Click toggle (touch + nested)
  nav.addEventListener('click', function (e) {
    const a = e.target.closest('li.menu-item-has-children > a');
    if (!a) return;

    const sub = a.nextElementSibling;
    if (!sub || sub.tagName !== 'UL' || !sub.classList.contains('sub-menu')) return;

    sub.hidden = !sub.hidden;

    // Toggle only, do not navigate on first tap
    e.preventDefault();
    e.stopPropagation();
  });

  // Optional: click outside closes all
  document.addEventListener('click', function (e) {
    if (nav.contains(e.target)) return;

    nav.querySelectorAll(selector).forEach(function (ul) {
      ul.hidden = true;
    });
  });
});
<nav class="main-nav" aria-label="Hoofdmenu">
  <ul class="menu">
    <li class="menu-item menu-item-has-children">
      <a href="/how-we-support/">How we support</a>
 
      <!-- WordPress-rendered submenu -->
      <ul class="sub-menu">
        <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2060">
          <a href="#">Cross-functional training &amp; tools</a>
        </li>
        <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2061">
          <a href="#">ESG Programme management support</a>
        </li>
        <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2062">
          <a href="#">Industry collaboration &amp; pre-competitive initiatives</a>
        </li>
        <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2063">
          <a href="#">Multi-stakeholder intiatives</a>
        </li>
      </ul>
    </li>

    <li class="menu-item"><a href="/about/">About</a></li>
    <li class="menu-item"><a href="/contact/">Contact</a></li>
  </ul>
</nav>
Source Link
mplungjan
  • 180.4k
  • 29
  • 184
  • 247

I am guessing the HTML

You need to delegate and no need for jQuery I use the :scope selector for the first time

:scope means "this element" so ':scope > ul' selects ONLY the direct child submenu of this <li>

document.addEventListener('DOMContentLoaded', function() {
  const navSelector = '.main-nav';

  // Open / close submenus on hover
  document.querySelectorAll(navSelector + ' .menu-item-has-children').forEach(function(li) {

    const sub = li.querySelector(':scope > ul');
    if (!sub) return;

    // Hide submenu initially
    sub.hidden = true;

    li.addEventListener('mouseenter', function() {
      sub.hidden = false;
    });

    li.addEventListener('mouseleave', function() {
      sub.hidden = true;
    });
  });

  // Click to toggle submenus (touch devices + nested menus)
  document.addEventListener('click', function(e) {
    const link = e.target.closest(
      navSelector + ' .menu-item-has-children > a'
    );
    if (!link) return;

    const sub = link.nextElementSibling;
    if (!sub || sub.tagName !== 'UL') return;

    // Toggle visibility
    sub.hidden = !sub.hidden;

    // Prevent link navigation when toggling
    e.preventDefault();

    // Prevent bubbling to any "click outside" handlers
    e.stopPropagation();
  });

  // Optional: close all menus when clicking outside the navigation
  document.addEventListener('click', function(e) {
    const nav = document.querySelector(navSelector);
    if (!nav || nav.contains(e.target)) return;

    nav.querySelectorAll('.menu-item-has-children > ul').forEach(function(ul) {
      ul.hidden = true;
    });
  });
});
<nav class="main-nav" aria-label="Hoofdmenu">
  <ul class="menu">
    <li class="menu-item menu-item-has-children">
      <a href="/how-we-support/">How we support</a>
      <ul class="sub-menu">
        <li class="menu-item"><a href="/how-we-support/option-1/">Option 1</a></li>

        <li class="menu-item menu-item-has-children">
          <a href="/how-we-support/option-2/">Option 2</a>
          <ul class="sub-menu">
            <li class="menu-item"><a href="/how-we-support/option-2/sub-1/">Sub 1</a></li>
            <li class="menu-item"><a href="/how-we-support/option-2/sub-2/">Sub 2</a></li>
          </ul>
        </li>

        <li class="menu-item"><a href="/how-we-support/option-3/">Option 3</a></li>
      </ul>
    </li>

    <li class="menu-item"><a href="/about/">About</a></li>
    <li class="menu-item"><a href="/contact/">Contact</a></li>
  </ul>
</nav>