4

By default on mouseenter event menu get open but I would like to change the event to click so for that I've override following file

lib/web/mage/menu.js

and made the following change for _toggleDesktopMode function but it's not working.

_toggleDesktopMode: function () {
            var categoryParent, html;

            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.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);
                            }
                        }
                    }
                },
                'click .ui-menu-item': function (event) {
                    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
                 */
                'mouseleave': function (event) {
                    this.collapseAll(event, true);
                },

                /**
                 * Mouse leave.
                 */
                'mouseleave .ui-menu': 'collapseAll'
            });

            categoryParent = this.element.find('.all-category');
            html = $('html');

            categoryParent.remove();

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

Any suggestion what is wrong?

1
  • can anyone help? Commented Aug 28, 2018 at 13:02

4 Answers 4

4

No need to override

lib/web/mage/menu.js

Just go to

app/design/frontent/YourVendor/YourTheme/Magento_Theme/templates/html/topmenu.phtml

Inside ul data-mage-init add

"mediaBreakpoint": "(max-width: 1824px)"

Code should look like below

<ul data-mage-init='{"menu":{"responsive":true, "expanded":true,  "mediaBreakpoint": "(max-width: 1824px)", "position":{"my":"left top","at":"left bottom"}}}'>

By doing this menu.js will execute _toggleMobileMode() and in this mode, menu will open on click, not on hover.

2
  • ur given solution is working but while click on the menu it scrolls do you have any idea how to fixed ? Commented May 16, 2019 at 11:11
  • Didn't work for me in Magento 2.3.4
    – PauGNU
    Commented Jul 21, 2020 at 13:15
2

In my case (Magento 2.3.4), the solution from @Vince Verhoeven worked partially: I wanted the top level links to open the submenu in case there was one. So I had to detect whether there was a submenu or not to control this logic.

Just after:

'click .ui-menu-item:has(a)': function (event) {

I added a condition for the code to be executed:

if ($(event.target).siblings('.submenu').length || $(event.target).parent().siblings('.submenu').length) {

The final function looks like this:

    /**
     * @private
     */
    _toggleDesktopMode: function () {
        var categoryParent, html;

        $(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) {
                if ($(event.target).siblings('.submenu').length || $(event.target).parent().siblings('.submenu').length) {
                    event.preventDefault();

                    var target = $(event.target).closest('.ui-menu-item');

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

                        // Open submenu on click
                        if (target.has('.ui-menu').length) {
                            this.expand(event);
                        } else 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);
                            }
                        }
                    }
                }
            },

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

                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
             */
            'mouseleave': function (event) {
                this.collapseAll(event, true);
            },

            /**
             * Mouse leave.
             */
        });

        categoryParent = this.element.find('.all-category');
        html = $('html');

        categoryParent.remove();

        if (html.hasClass('nav-open')) {
            html.removeClass('nav-open');
            setTimeout(function () {
                html.removeClass('nav-before-open');
            }, this.options.hideDelay);
        }
    },
2
  • Thank you....Working Well.... :) +1 Commented Mar 31, 2021 at 13:31
  • I have the same requirement in 2.4.6. But it's not working. Do you have any idea what needs to be changed? Commented Aug 15, 2023 at 5:07
1

You have to extend lib/web/mage/menu.js

on line 538 you see, you need to make click

'mouseenter .ui-menu-item':

and on line 579 you have to remove 'mouseleave .ui-menu': 'collapseAll'

example by extending menu.js

requirejs-config.js

var config = {
map: {
    '*': {
        "menu": "Magento_Theme/js/megaMenu"
    },
},

megaMenu.js

define([
    'jquery',
    'mage/menu',
],
function ($) {
    $.widget('my.megamenu', $.mage.menu, {
            _init: function () {
                this._super();
            }, /**
             * @private
             */
            _toggleDesktopMode: function () {
                var categoryParent, html;

                $(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) {
                        console.log('mine');
                        event.preventDefault();

                        var target = $(event.target).closest('.ui-menu-item');

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

                            // Open submenu on click
                            if (target.has('.ui-menu').length) {
                                this.expand(event);
                            } else 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);
                                }
                            }
                        }
                    },

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

                        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
                     */
                    'mouseleave': function (event) {
                        this.collapseAll(event, true);
                    },

                    /**
                     * Mouse leave.
                     */
                });

                categoryParent = this.element.find('.all-category');
                html = $('html');

                categoryParent.remove();

                if (html.hasClass('nav-open')) {
                    html.removeClass('nav-open');
                    setTimeout(function () {
                        html.removeClass('nav-before-open');
                    }, this.options.hideDelay);
                }
            }
        }
    );
    return $.my.megamenu;
});
1
  • This solution is no longer working on 2.4.4. Any other ideas?
    – AboElnouR
    Commented Jul 30, 2022 at 23:00
1

on 2.4.5

define(['jquery'], function ($) {
'use strict';

var menuMixin = {

    // overriden method remove hover in desktop mode
    // only open menu by clicking
    _toggleDesktopMode: function () {
        var categoryParent, html;
    // remove mousemove listener
        $(this.element).off('click mousedown mouseenter mouseleave mousemove');
       
        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);

                    // Open submenu on click
                    if (target.has('.ui-menu').length) {
                       
                        event.preventDefault();
                        
                        this.expand(event);
                    } else 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);
                        }
                    }
                }
            },

            /**
             * @param {jQuery.Event} event
             */
            'click .ui-menu-item': function (event) {
                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
             */
            'mouseleave': function (event) {
                this.collapseAll(event, true);
            },
        });

        categoryParent = this.element.find('.all-category');
        html = $('html');

        categoryParent.remove();

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

return function (targetWidget) {
    $.widget('mage.menu', targetWidget.menu, menuMixin);
    return $.mage.menu;
};

});

1
  • please add details on your answer Commented Aug 4, 2023 at 8:23

Your Answer

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

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