I'm building a website where some elements in the navigation menu have a submenu. These submenus are shown when the mouse hovers over the element, but of course on mobile I cannot do that since there is no actual hover. A tap means a click, and that follows the link. I hoped on a simple solution but it doesn't work on my mobile device...
Here's a code snippet inside a loop, this is a menu item (li) that contains a submenu (ul):
$(this).find("a").on("mouseenter focusin click", function(e) {
if(submenu.css("display") != "block") {
e.preventDefault(); //should work on mobile
}
submenu.stop().css('display', 'block').animate({
top: 1.2em,
opacity: 1
}, 200);
});
Now I test this in Firefox. In normal use, you cannot actually click the menu item before the submenu comes out because display becomes block as soon as the mouse hovers over it. So in web console I type in:
$("#nav > ul > li:first-child > a").click()
This gives the expected behaviour. the submenu comes out but the link isn't followed. On mobile, the link is still followed... What gives?
Update
I just entered two alert statements. One that says the event type first thing in the handler, and one that says "preventing default" just before e.preventDefault. On my mobile browser (Dolphin browser for Android) it gives the following on a tap of the menu item:
Event is mouseenter
Preventing default
Event is click
Naturally, when the first event fired is "mouseenter", the default will not be prevented for click as that is the point where the menu shows up. In other words, I would need to make sure that the first mouseenter does show the menu but a click isn't fired... I could check the top css property for that, but I wonder if there is another way.
I got it working by adding another check to see if the submenu is at the proper position, rather than using the value of display.
$(this).find("a").on("mouseenter focusin click", function(e) {
if(e.type == "click" && submenu.css("top") != "1.2em") {
e.preventDefault();
}
submenu.stop().css('display', 'block').animate({
top: 1.2em,
opacity: 1
}, 200);
});
Related
I have an off canvas navigation menu (classname that is enabled via hover-over using jQuery on my Wordpress site. It is, as it should be, not visible on page load.
For the mobile version, I want the same nav menu to be activated by clicking on a menu icon (that I've given two classes, in this order: mobile-nav-toggle show-nav-mobile). The toggle method seems to only work for a vertical toggle. My solution to replicating the same animation on click rather than hover, is by using the toggleClass method on the icon to toggle between a classname that opens the menu nav (show-nav-mobile) and closes it (hide-nav-mobile) Using the below code:
jQuery(".show-nav-mobile").click(function(){
jQuery(".offcanvasmainnav").animate({left:'0px' }, 250);
});
jQuery(".mobile-nav-toggle").click(function(){
jQuery(".mobile-nav-toggle").toggleClass("show-nav-mobile hide-nav-mobile");
});
jQuery(".hide-nav-mobile").click(function(){
jQuery(".offcanvasmainnav").animate({left:'-640px' }, 250);
});
That doesn't seem to do the job though. The jQuery opens the offcanvasmain div just fine, but doesn't close it again.
What is wrong with my approach?
I assume your element initially looks somewhat like this:
<nav class="mobile-nav-toggle hide-nav-mobile">...</nav>
This means that
a) Both these click handlers will always run when clicking, no matter if the element still has the class hide-nav-mobile:
jQuery(".mobile-nav-toggle").click(function(){
jQuery(".mobile-nav-toggle").toggleClass("show-nav-mobile hide-nav-mobile");
});
jQuery(".hide-nav-mobile").click(function(){
jQuery(".offcanvasmainnav").animate({left:'-640px' }, 250);
});
jQuery finds the element at the moment you define the click handler; it doesn't recheck if the element still has this class when clicking later.
b) This never attaches a click handler:
jQuery(".show-nav-mobile").click(function(){
jQuery(".offcanvasmainnav").animate({left:'0px' }, 250);
});
because at the time of calling jQuery(".show-nav-mobile") it cannot find any element with that class.
To fix it, do this all in a single click handler:
jQuery(".mobile-nav-toggle").on('click', function(){
const that = jQuery(this);
that.toggleClass("show-nav-mobile hide-nav-mobile");
jQuery(".offcanvasmainnav").animate({left: that.hasClass('show-nav-mobile') ? '0px' : '-640px' }, 250);
});
So I'm building a small "UberMenu" feature for my main menu, and it works almost as I want, except that I want the UberMenu container to hide if the mouse is not over either
1) the UberMenu button, or
2) the UberMenu container
Right now, if you hover over the UberMenu button, the container shows up, and if you enter the UberMenu container, and then proceed to hover over another menu item, it hides.
This is what I want, however if I hover the UberMenu button, and then immediately hover over another menu item, the container remains open.
I've tried over a dozen different snippets, but I haven't found a solution.
I guess I need some if / else statement added to the code?
Can someone give me some guidance here? Much appreciated! :-)
This is the code I used:
//When mouse hovers UberMenu button, Show Container
$(".uber-menu-test").hover(function(e) {
e.preventDefault(); //To prevent default anchor tag behaviour
e.stopPropagation(); //To prevent parent container click event from firing
$(".uber-menu-frame").show(800);
});
// If mouse hovers either the content area or the navbararea, hide UberMenu container again
$(".block-type-content, .navbararea").mouseenter(function() {
$(".uber-menu-frame").hide(800);
});
I found a solution on my own by adding a timer, I'm not sure how solid this piece of code is, but it does the trick as far as I can tell!
For anyone interested:
$(".uber-menu-test").hover(function(e) {
e.preventDefault(); //To prevent default anchor tag behaviour
e.stopPropagation(); //To prevent parent container click event from firing
$(".uber-menu-frame").show(800);
});
var myTimer = false;
$(".uber-menu-test ,.uber-menu-frame").hover(function(){
//mouse enter
clearTimeout(myTimer);
},function(){
//mouse leav
myTimer = setTimeout(function(){
$(".uber-menu-frame").fadeOut(800);
},100)
});
Hope it helps,
$(".uber-menu-test").mouseenter(function(){
$(".uber-menu-frame").show(800);
}).mouseleave(function(){
$(".uber-menu-frame").hide(800);
});
Ok... So I have this drop down menu working as I'd like... however I'm trying to figure out how to revert the function back to it's original state after a menu item is clicked.
So first when you trigger the function it does & works great the following:
It swaps out .menu_hide and .lockscreen for .menu_show and .lockscreen_on.
// show and hide mobile menu
$('#triggerMobileMenu').click(function(e) {
e.preventDefault();
// Toggle all 4 classes off or on
$('#mobileMenu').toggleClass('menu_hide menu_show');
$('#mobileScreen').toggleClass('lockscreen_off lockscreen_on');
But now I'm trying to add another piece that says once a menu item is clicked, close the menu and swap the classes back to their original state from .menu_show and .lockscreen_on, to .menu_hide and .lockscreen_off.
$('#mobileMenu ul li a').on('click',function(){
$('#mobileMenu').toggleClass('menu_show menu_hide')({ autoCloseOnClick: true });
$('#mobileScreen').toggleClass('lockscreen_on lockscreen_off')({ autoCloseOnClick: true });
});
});
I should also note that on the same page a scroll to id# may be happening vs just simply taking you to the new url/page. Either case will happen though.
I think that you're making this too complicated. Use the same event handler for both a#triggerMobileMenu and ul#mobileMenu li a since you're having them do the same thing (toggle the visibility of the menu and another element).
$('a#triggerMobileMenu, ul#mobileMenu li a').on('click', function(evt){
evt.preventDefault();
$('#mobileMenu').toggleClass('menu_hide menu_show');
$('#mobileScreen').toggleClass('lockscreen_off lockscreen_on');
});
If you need to know which element was clicked in the event handler, evt.target is available:
if( $(evt.target).is($('a#triggerMobileMenu')) ) {
// do stuff
}
See http://jsfiddle.net/Mph6t/3/
I think it is working as intended. I had to fix some id names that may have been switched in the translation to jsfiddle. Here's a working one as far as I can tell. This leaves the somename2 div still showing. I assume that is going to be blank and just for locking the screen right?
I also changed the link to a new tab for testing purposes. FYI.
Relevant changes are:
$('#somename1 ul li a').on('click',function(){
$('#somename1').toggleClass('menu_show menu_hide')({ autoCloseOnClick: true });
$('#somename2').toggleClass('lockscreen_on lockscreen_off')({ autoCloseOnClick: true });
});
I have created a responsive menu that breaks at 480px and below. I have it to where the following reacts if:
The user clicks on a the "Menu" link.
The menu slideToggles out.
The sub-menus slideToggle out onClick as well.
But you cannot click on any of the pages.
Does this have something to do with the return false?
You may view the example here: http://www.stlredtails.com/construction/
You may need to resize the browser at or belw 480px to see the responsive navigation in action.
Here is the jQuery for the navigation:
jQuery(".navigation ul").hide();
jQuery("#navigation").click(
function() {
jQuery(this).siblings("ul").slideToggle(150, "swing");
return false;
}
);
jQuery(".navigation > ul > li").click(
function() {
jQuery(this).children("ul").slideToggle(150, "swing");
return false;
}
);
Thank you all!
The problem comes from the return false. When you're clicking the links, you're also clicking the ancestors li and ul. Before the link "activates" the functions bound to the ancestors click event execute. Since they return false the default browser behavior (navigating away) is prevented.
There are better solutions for this kind of menu behavior, but using what you already you have (and assuming you won't be changing your html) you can simply add the following to your javascript:
$('.navigation').on('click', 'a:only-child', function(e) {
e.stopPropagation();
});
This searches for all links in the navigation menu that are the only child of their parents (which in this case, happen to be the links you want to continue working as links) and prevents the ancestors click events from executing - the return false shouldn't happen anymore.
I have this menu http://jsbin.com/useqa4/3
The hover I think works correct, but what I want is the normal: when the user's cursor isn't on the "Solution" item or on the submenu then I want the div #submenuSolutions to return in "display:none".
How can I achieve this?
If you read the jQuery api more carefuly you will see that the hover function can take handle two events http://api.jquery.com/hover/
$("document").ready(function() {
$("#menuSolutions a").hover(function () {
$("#menuSolutions").addClass("menuHover");
$("#submenuSolutions").show("3000");
},function() {
$("#menuSolutions").removeClass("menuHover");
$("#submenuSolutions").hide("3000")});
});
This will work only if your menu is a suckerfish menu.
See Demo
Just added this code to hide it back when mouse leaves it:
$("#submenuSolutions").mouseleave(function(){
$(this).hide();
});
Since submenuSolutions is the id of your panel, you can use the mouseleave event which triggers when mouse leaves the area of element specified.