TransWikia.com

Magento 2 open navigation menu on click on desktop

Magento Asked by Kaushal Suthar on November 8, 2021

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?

3 Answers

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

Answered by PauGNU on November 8, 2021

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

Answered by Vince Verhoeven on November 8, 2021

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.

Answered by Mathew Lawrence on November 8, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP