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?

nikin
1,1529 silver badges19 bronze badges
asked Jul 18, 2018 at 6:51
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.

answered Jan 18, 2019 at 10:57
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 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);
 }
 },
answered Jul 21, 2020 at 13:11
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;
});
answered Oct 17, 2019 at 7:47
1
  • This solution is no longer working on 2.4.4. Any other ideas? 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;
};

});

answered Feb 27, 2023 at 18:30
1
  • please add details on your answer Commented Aug 4, 2023 at 8:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.