Skip to content

Magento 2.4.4-p2 Open menu subcategories only on click

I’m trying to make Magento menu open categories only on click and disable hover function. It opens subcategories but once you open a subcategory it doesn’t collapse and you can’t expand a different subcategory

I added in app/design/frontend/Vendor/theme/require-config.js the following code

var config = {
    config: {
        mixins: {
            'mage/menu': {
                'Magento_Theme/js/menu-mixin': true
            }
        }
    },
};

Then, in app/design/frontend/Vendor/theme/Magento_Theme/web/js I’ve added the following script:

define([
        'jquery',
    ],
    function ($) {
        return function () {
            $.widget('mage.custommenu', $.mage.menu, {
                _toggleDesktopMode: function () {
                    var html, subMenus;

                    $(this.element).off('click mousedown mouseenter mouseleave');
                    this._on({

                        /**
                         * Prevent focus from sticking to links inside menu after clicking
                         * them (focus should always stay on UL during navigation).
                         */
                        'mousedown .ui-menu-item > a': function (event) {
                            event.preventDefault();
                        },

                        /**
                         * Prevent focus from sticking to links inside menu after clicking
                         * them (focus should always stay on UL during navigation).
                         */
                        'click .ui-state-disabled > a': function (event) {
                            event.preventDefault();
                        },

                        /**
                         * @param {jQuer.Event} event
                         */
                        'click .ui-menu-item:has(a)': function (event) {
                            var target = $(event.target).closest('.ui-menu-item');

                            if (!this.mouseHandled && target.not('.ui-state-disabled').length) {
                                this.select(event);

                                // Only set the mouseHandled flag if the event will bubble, see #9469.
                                if (!event.isPropagationStopped()) {
                                    this.mouseHandled = true;
                                }

                                // Open submenu on click
                                if (target.has('.ui-menu').length) {
                                    this.expand(event);
                                } else if ($(this.document[0].activeElement).closest('.ui-menu').length) {
                                    // Redirect focus to the menu
                                    this.element.trigger('focus', [true]);

                                    // If the active item is on the top level, let it stay active.
                                    // Otherwise, blur the active item since it is no longer visible.
                                    if (this.active && this.active.parents('.ui-menu').length === 1) { //eslint-disable-line
                                        clearTimeout(this.timer);
                                    }
                                }
                            }

                            subMenus = this.element.find('.level-top');
                            $.each(subMenus, $.proxy(function (index, item) {
                                var category = $(item).find('> a span').not('.ui-menu-icon').text(),
                                    categoryUrl = $(item).find('> a').attr('href'),
                                    menu = $(item).find('> .ui-menu');

                                this.categoryLink = $('<a>')
                                    .attr('href', categoryUrl)
                                    .text($.mage.__('%1').replace('%1', category));

                                this.categoryParent = $('<li>')
                                    .addClass('ui-menu-item all-category')
                                    .html(this.categoryLink);

                                if (menu.find('.all-category').length === 0) {
                                    menu.prepend(this.categoryParent);
                                }

                            }, this));
                        },

                        /**
                         * @param {jQuery.Event} event
                         */
                        'click .ui-menu-item.parent': function (event) {
                            var target = $(event.target).closest('.ui-menu-item');

                            if ($(target).hasClass('parent')) {
                                event.preventDefault();
                            }
                            var target = $(event.currentTarget),
                                submenu = this.options.menus,
                                ulElement,
                                ulElementWidth,
                                width,
                                targetPageX,
                                rightBound;

                            if (target.has(submenu)) {
                                ulElement = target.find(submenu);
                                ulElementWidth = ulElement.outerWidth(true);
                                width = target.outerWidth() * 2;
                                targetPageX = target.offset().left;
                                rightBound = $(window).width();

                                if (ulElementWidth + width + targetPageX > rightBound) {
                                    ulElement.addClass('submenu-reverse');
                                }

                                if (targetPageX - ulElementWidth < 0) {
                                    ulElement.removeClass('submenu-reverse');
                                }
                            }

                            // Remove ui-state-active class from siblings of the newly focused menu item
                            // to avoid a jump caused by adjacent elements both having a class with a border
                            target.siblings().children('.ui-state-active').removeClass('ui-state-active');
                            this.focus(event, target);
                        },

                        /**
                         * @param {jQuery.Event} event
                         */
                        'click .ui-menu-item': function (event) {
                            var target = $(event.target).closest('.ui-menu-item');

                            if ($(target).hasClass('level0')) {
                                return;
                            }
                            var target = $(event.currentTarget),
                                submenu = this.options.menus,
                                ulElement,
                                ulElementWidth,
                                width,
                                targetPageX,
                                rightBound;

                            if (target.has(submenu)) {
                                ulElement = target.find(submenu);
                                ulElementWidth = ulElement.outerWidth(true);
                                width = target.outerWidth() * 2;
                                targetPageX = target.offset().left;
                                rightBound = $(window).width();

                                if (ulElementWidth + width + targetPageX > rightBound) {
                                    ulElement.addClass('submenu-reverse');
                                }

                                if (targetPageX - ulElementWidth < 0) {
                                    ulElement.removeClass('submenu-reverse');
                                }
                            }

                            // Remove ui-state-active class from siblings of the newly focused menu item
                            // to avoid a jump caused by adjacent elements both having a class with a border
                            target.siblings().children('.ui-state-active').removeClass('ui-state-active');
                            this.focus(event, target);
                        },
                    });

                    html = $('html');

                    if (html.hasClass('nav-open')) {
                        html.removeClass('nav-open');
                        setTimeout(function () {
                            html.removeClass('nav-before-open');
                        }, this.options.hideDelay);
                    }
                },

                focus: function () {
                    return false;
                },
            });

            return $.mage.custommenu;
        }
    });

Finally, I’ve updated topmenu.phtml file in app/design/frontend/Vendor/theme/Magento_Theme/templates/html/topmenu.phtml

<?php

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * Top menu for store
 *
 * @var $block MagentoThemeBlockHtmlTopmenu
 */

$columnsLimit = $block->getColumnsLimit() ?: 0;
$_menuHtml = $block->getHtml('level-top', 'submenu', $columnsLimit)
?>

<nav class="navigation" data-action="navigation">
    <ul data-mage-init='{"menu":{"responsive":true, "expanded":true, "position":{"my":"left top","at":"left bottom"}}}'>
        <?= /* @noEscape */ $_menuHtml ?>
        <?= $block->getChildHtml() ?>
    </ul>
</nav>