How to Auto-Expand Submenus in an Elementor Mobile Menu Dropdown (Without Breaking Accessibility)

For a recent client project, I needed to ensure submenus in an Elementor menu widget stayed open on mobile devices. At first glance, the easiest way to solve this might seem like slapping display: block!important onto the submenus elements. It’s tempting, but that approach breaks more than it fixes. ARIA attributes that guide screen readers won’t update properly, and the JavaScript that Elementor uses to manage menu behavior can get out of sync, leading to unpredictable results.

Instead, I used the tools Elementor already provides—its integration with the SmartMenus jQuery plugin—and extended that functionality in a way that’s both accessible and robust.

Why CSS Alone Doesn’t Cut It

The display property alone isn’t enough for accessible menus. When a submenu opens, attributes like aria-expanded and aria-hidden are dynamically updated. These are essential for accessibility and ensure screen readers announce whether a submenu is open or closed. Overriding CSS to force the submenu to display skips that layer entirely, leaving the menu visually open but logically broken.

Beyond accessibility, Elementor’s menu widget relies on JavaScript to manage toggles and animations. Forcing submenus open through CSS can create conflicts where JavaScript tries to hide a menu you’ve already told CSS to display.

A Script That Plays Nicely With Elementor

Instead of fighting Elementor’s built-in behavior, we can extended it. The SmartMenus plugin already manages submenus for the menu widget, so I wrote a script to hook into its API. The key was to ensure submenus stayed open when the menu was toggled on mobile but didn’t interfere with normal behavior on larger screens.

Here’s the script:

jQuery(document).ready(function () {
  const menuRoot = jQuery(".sprucely-expand-submenus .elementor-nav-menu");
  const toggleButton = jQuery(".sprucely-expand-submenus .elementor-menu-toggle");

  // Debugging setup
  // console.log('menuRoot:', menuRoot);
  // console.log('toggleButton:', toggleButton);

  if (!menuRoot.length || !toggleButton.length) {
    // console.error('Menu root or toggle button not found.');
    return;
  }

  const applySubMenuLogic = function () {
    menuRoot.each(function () {
      const currentMenu = jQuery(this);
      const smartMenuInstance = currentMenu.data("smartmenus");

      // Debugging SmartMenus initialization
      // console.log('Current Menu:', currentMenu);
      // console.log('SmartMenus instance:', smartMenuInstance);

      if (!smartMenuInstance) {
        // console.error('SmartMenus instance not found for:', currentMenu);
        return;
      }

      // Remove sub-arrow spans
      currentMenu
        .find(".menu-item-has-children > .has-submenu > .sub-arrow")
        .remove();

      currentMenu.find(".menu-item-has-children").each(function () {
        const menuItem = jQuery(this);
        const submenu = menuItem.find("ul").first();

        // Debugging submenu detection
        // console.log('Menu Item:', menuItem);
        // console.log('Submenu:', submenu);

        if (submenu.length && !submenu.is(":visible")) {
          // Expand the submenu
          smartMenuInstance.menuShow(submenu);
        }
      });
    });
  };

  toggleButton.on("click", function () {
    // Debugging toggle click event
    // console.log('Toggle button clicked. Applying submenu logic.');

    setTimeout(applySubMenuLogic, 50); // Ensure menus stay open after toggle
  });

  // Apply logic on page load
  // console.log('Applying submenu logic on page load.');
  applySubMenuLogic();

  // Reapply logic on window resize
  window.addEventListener("resize", function () {
    // Debugging resize event
    // console.log('Window resized. Reapplying submenu logic.');
    applySubMenuLogic();
  });
});

This script identifies the menu container and checks whether SmartMenus has been initialized. For this solution, I assigned a sprucely-open-submenus class to the WordPress Menu widget I wanted to target using the Advanced > CSS Classes field in Elementor. When the toggle button is clicked, it ensures all submenus remain open using SmartMenus’ menuShow method. A slight delay (setTimeout) is added to avoid timing conflicts with Elementor’s default toggle behavior.

YMMV

This solution was tested with WordPress 6.7.1, Elementor 3.25.10, and jQuery 3.7.1. While it should work with similar setups, updates to any of these components could break the functionality. Keep this in mind if you’re applying it to your own site—future maintenance may be required as Elementor or WordPress evolves.

This approach avoids the pitfalls of overriding behavior with CSS while preserving accessibility and usability. If you’re implementing this, I’d love to hear how it works for you—share your thoughts or questions in the comments below!

If you need help with customizations like this, or if you’re looking for a reliable partner to keep your site running smoothly, check out my All-in-One Hosting & Support plans for WordPress or WooCommerce. I’d love to take the stress out of managing your site, so you can focus on growing your business.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top