2

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 \Magento\Theme\Block\Html\Topmenu
 */

$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>

1 Answer 1

2

Finally got the solution by updating menu-mixin.js

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;
                                }

                                if (!this.element.is(':focus') &&
                                    $(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);
                        },
                    });

                    $('.ui-menu-item').click(function () {
                        $(this).children('ul').toggle();
                        console.log("Click menu item")
                    });
                    html = $('html');

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

                _toggleMobileMode: function () {
                    var subMenus;

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

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

                            event.preventDefault();
                            target = $(event.target).closest('.ui-menu-item');
                            target.get(0).scrollIntoView();

                            if (!target.hasClass('level-top') || !target.has('.ui-menu').length) {
                                window.location.href = target.find('> a').attr('href');
                            }
                        },

                        /**
                         * @param {jQuery.Event} event
                         */
                        'click .ui-menu-item:has(.ui-state-active)': function (event) {
                            this.collapseAll(event, true);
                        }
                    });

                    $('.ui-menu-item').click(function () {
                        $(this).children('ul').toggle();
                        console.log("Click menu item")
                    });

                    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));
                },

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

            return $.mage.custommenu;
        }
    });
1
  • I was having same problem here. Thank you! Dec 22, 2022 at 9:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.