I am writing my own jQuery navigation submenu script. When you hover over a link in the horizontal nav that has a ul tag, it makes that ul appear. I have a bit of code that adds an arrow to the links in the horizontal nav if it has a submenu. My problem is that it also adds the arrows to the links in the submenu. This is not a big deal functionally, but it does look bad.
The odd part is that if I use $(this).find('> a') it screws up the appearance of the submenu. The submenu appears when I hover over the top-level link, but then disappears right away when the mouse leaves that link. So I can basically see the entire submenu when the mouse is hovered over the top level link. When the mouse leaves the top level link, the submenu disappears and I can't click on the submenu links. What am I doing wrong?
Here is a JSFiddle. Change $(this).find('a') to $(this).find('> a') and you'll see what I mean. Thanks for your time!
$(document).ready(function(){
$('nav ul li:has(ul)').each(function(){
var listItem = $(this);
$(this).find('> a').each(function(){
var aTag = $(this);
aTag.append('<img src="{img_url}/caret.png" width="8" height="8">');
aTag.on('mouseover', function(){
listItem.find('ul').each(function(){
$(this).css('display', 'block');
});
})
.on('mouseout', function(){
listItem.find('ul').each(function(){
$(this).css('display', 'none');
});
});
});
});
});
you have to pull out the first a tag from your loop through .each
$(document).ready(function () {
$('nav ul li:has(ul)').each(function () {
var listItem = $(this);
// first a tag as new var
var aTagFirst = listItem.children('a');
aTagFirst.append('<img src="{img_url}/caret.png" width="8" height="8">');
$(this).find('a').each(function () {
var aTag = $(this);
aTag.on('mouseover', function () {
listItem.find('ul').each(function () {
$(this).css('display', 'block');
});
})
.on('mouseout', function () {
listItem.find('ul').each(function () {
$(this).css('display', 'none');
});
});
});
});
});
DEMO
I guess you can do it by CSS, I have removed events mouseover, mouseout and added this styles:
li:hover ul {
display: block;
}
li:hover a {
background: #66cc00;
}
li:hover li a {
background: #333;
}
DEMO
$(document).ready(function () {
$('nav ul li:has(ul)').each(function () {
var listItem = $(this);
$(this).find('> a,>ul').each(function () {
var aTag = $(this);
aTag.append('<img src="{img_url}/caret.png" width="8" height="8">');
aTag.on('mouseover', function () {
listItem.find('ul').each(function () {
$(this).css('display', 'block');
});
})
.on('mouseout', function () {
listItem.find('ul').each(function () {
$(this).css('display', 'none');
});
});
});
});
});
Here only update is:
$(this).find('> a,>ul')
Css:
only update is one:
nav > ul > li > a {
display: block;
margin: 0px;
border-bottom: 0;
color: #333;
height: 52px;
padding: 0px 25px 0px 25px;
font-size: 1.25em;
line-height:55px;
}
Update in padding and add line-height.
Demo: http://jsfiddle.net/fsrf5jw3/5/
You need to add hover callbacks to your li element, not a element, so the code becomes:
var listItem = $(this);
listItem.find('> a').each(function(){
var aTag = $(this);
aTag.append('<img src="{img_url}/caret.png" width="8" height="8">');
});
listItem
.on('mouseover', function(){
listItem.find('ul').each(function(){
$(this).css('display', 'block');
});
})
.on('mouseout', function(){
listItem.find('ul').each(function(){
$(this).css('display', 'none');
});
});
Also, as marsh answer goes, it is more proper performance-wise to do such things with css, not javascript.
Related
I have been working on navigation bar and the strangest issue is occurring.
Please use the JSFiddle link to see what I mean.
To duplicate the error:
Run the code when the desktop view is active i.e. when the navigation links are in a line.
Then resize the screen till the "click me" is displayed.
Then press it.
Now run the code while you see the "click me" and press it again.
JS information
jQuery(document).ready(function($) {
// UserCP
$('.rotate').on('click', function() {
$(this).toggleClass("down");
});
$('.nav-start').on('click', function() {
$("#nav2").removeClass("hidden");
$('#nav2 li a').stop().slideToggle('100');
return false;
});
$(document).ready(function() {
$('#nav2 li a').stop().slideToggle('100');
});
$('body').on('click', function() {
$('#nav2 li a').stop().slideUp('100');
});
$("#nav2 li a").click(function(e) {
e.stopPropagation();
});
$(document).click(function(event) {
if (!$(event.target).closest('#nav2 li a').length) {
if ($('#nav2 li a').is(":visible")) {
$('html, body').on('click', function() {
$('#nav2 li a').stop().slideUp('100');
});
};
};
});
});
FIXED - UPDATED JSFiddle! Thanks #Louys Patrice Bessette #Titus #Rick
You are using two click events on this "Click me" li...
(One on .navstart and one on .rotate)
It may not be an issue, but this make the code harder to read.
Then, when you slideToggle(), if you want the submenu to slide down, it has to be hidden.
Because, since you remove the hidden class (probably usefull on load), the submenu is visible.
A Toggle hides it.
I simplified your script to this.
Have a look at this updated Fiddle.
$(document).ready(function() {
// Show submenu on "Click me"
$('.nav-start').on('click', function() {
$('.rotate').toggleClass("down");
$("#nav2").removeClass("hidden");
var subNav = $('#nav2 li a');
if(subNav.css("display")=="block"){
subNav.stop().slideUp('100');
}else{
subNav.stop().slideDown('100');
}
return false;
});
$("#nav2 li a").click(function(e) {
e.stopPropagation();
});
// Hide submenu on document click
$(document).click(function(event) {
if (!$(event.target).closest('#nav2 li a').length && $('#nav2 li a').is(":visible")) {
$('#nav2 li a').stop().slideUp('100');
};
});
});
When I hover on .menu li all of a take this class.
I want to hover on .menu li so that just his child a takes the class.
$(document).ready(function() {
// Menu is hover
$('.menu li').hover(function(){
$(this).addClass('liHoverMnue');
$('.menu li a').addClass('aHoverMnue');
}, function() {
$(this).removeClass('liHoverMnue');
$('.menu li a').removeClass('aHoverMnue');
});
});
Use the this keyword again
$(document).ready(function() {
// Menu is hover
$('.menu li').hover(function(){
$(this).addClass('liHoverMnue');
$(this).find('a').addClass('aHoverMnue');
}, function() {
$(this).removeClass('liHoverMnue');
$(this).find('a').removeClass('aHoverMnue');
});
});
You could also chain it
$(this).addClass('liHoverMnue').find('a').addClass('aHoverMnue');
JSFiddle
http://jsfiddle.net/5rkrq4bw/strong text
JQuery Code
// Side Menu Starts
$('.SideNav .Menu a.MenuDrop').click(function(event){
event.preventDefault();
if(!$(this).hasClass('Active')) {
if(!$(this).parent().parent().hasClass('Active') && $(this).next().hasClass('sub-menu')) {
$(this).next().slideToggle();
$(this).addClass('Active');
} else {
$('.SideNav .Menu li ul').slideUp();
$(this).next().slideToggle();
$('.SideNav .Menu a.MenuDrop').removeClass('Active');
$(this).addClass('Active');
}
}
});
//Side Menu Ends
The Problem
Trying to integrate multiple tiers I am finding the problem of only being able to have one open at a time and checking to see if any others are open to close them.
What should happen
Demo
Category
Sub-Cat
Link
Link
Link
Sub-Cat
Link
Link
Link
Category
Sub-Cat
Link
Link
Link
Sub-Cat
Link
Link
Link
Explanation
Only one 'Category' to be expanded at a time
Only one 'Sub-Cat' inside to be expanded at a time
Adding / removing of 'Active' class.
Resolved: http://jsfiddle.net/wo4sj4pt/
JQuery Code:
(function(jQuery){
jQuery.fn.extend({
accordion: function() {
return this.each(function() {
var $ul = $(this);
if($ul.data('accordiated'))
return false;
$.each($ul.find('ul'), function(){
$(this).data('accordiated', true);
$(this).hide();
});
$.each($ul.find('a'), function(){
$(this).click(function(e){
activate(this);
return void(0);
});
});
var active = $('.Active');
if(active){
activate(active, 'toggle');
$(active).parents().show();
}
function activate(el,effect){
if (!effect) {
$(el)
.toggleClass('active')
.parent('li')
.siblings()
.find('a')
.removeClass('active')
.parent('li')
.children('ul, div')
.slideUp('fast');
}
$(el)
.siblings('ul, div')[(effect || 'slideToggle')]((!effect)?'fast':null);
}
});
}
});
})(jQuery);
Usage:
Link to JQuery and the above script
Make a multi-level list
Give your list a class/ID name such as '.SideNav'
Tell the script this is your accordion $('.SideNav').accordion();
http://jsfiddle.net/sabithpocker/5rkrq4bw/2/
$('.SideNav .Menu a.MenuDrop').click(function(event){
event.preventDefault();
console.log(this);
var subMenuToExpand = $('ul.sub-menu', $(this).parent());
var otherVisibleSubMenu = $('ul.sub-menu:visible', $(this).parents('.Menu'));
otherVisibleSubMenu.hide();
subMenuToExpand.show();
});
$('.SideNav .Menu a.MenuDrop').click(function(event){
$('ul.sub-menu:visible', $(this).parents('.Menu')).slideUp(50);
$('ul.sub-menu', $(this).parent()).slideDown();
});
How can I do so that even "toggle_cart" is clickable in the same way as "clickerHeader"
but retains its hover effect (see arrow)?
please see http://jsfiddle.net/realitylab/STE48/3
$('.eventMenu > ul').toggleClass('no-js js');
$('.eventMenu .js ul').hide();
$(document).on("click", function(e) {
var $elem = $(e.target);
if ($elem.hasClass('clickerHeader')) {
$('.eventMenu .js ul').not($elem.next('ul')).hide();
$elem.next("ul").slideToggle();
} else if (!$($elem).parents('.contentHolderHeader').length) {
//} else {
$('.eventMenu .js ul').hide();
}
});
Just wrap both elements in a div ..
http://jsfiddle.net/STE48/5/
.
In the CSS add:
.eventMenu:hover .no-js .contentHolderHeader {
display: block;
}
Also add a display: none to div.eventMenu .contentHolderHeader.
Replace the JS with:
$('.eventMenu > ul').toggleClass('no-js js');
$(".toggle_cart").click(function(e){
$(".contentHolderHeader").slideToggle();
e.stopPropagation();
});
$(".eventMenu").click(function(e){
e.stopPropagation();
});
$(document).click(function(){
$(".contentHolderHeader").slideUp();
});
Remove the inner ul in the HTML.
Tested with/without JS: http://jsfiddle.net/vuF9n/2/
A minimal change to your existing code is to add the following two lines after the first line of your click function:
if ($elem.hasClass('toggle_cart'))
$elem = $elem.next();
In other words, if the span with the arrow is clicked, pretend that actually the anchor element was clicked. In context:
$(document).on("click", function(e) {
var $elem = $(e.target);
if ($elem.hasClass('toggle_cart'))
$elem = $elem.next();
if ($elem.hasClass('clickerHeader')) {
// etc.
Demo: http://jsfiddle.net/STE48/6/
This is some JavaScript I have for a simple navigation bar but I have issues with the drop down disappearing before you can click on them so I want to add a delay after the mouse leaves the bar before they hide.
How would I do that?
<script type="text/javascript">
$(document).ready(function () {
// Navigation bar drop-down
$("nav ul li").hover(function () {
$(this).addClass("active");
$(this).find("ul").show().animate({ opacity: 1 }, 400);
}, function () {
// Delay on hiding should go here
$(this).find("ul").hide().animate({ opacity: 0 }, 200);
$(this).removeClass("active");
});
$('nav ul li ul li:first-child').prepend('<li class="arrow"></li>');
$('nav ul li:first-child').addClass('first');
$('nav ul li:last-child').addClass('last');
$('nav ul li ul').parent().append('<span class="dropdown"></span>').addClass('drop');
});
</script>
Thanks in advance to anyone who can help
P.S. This is probably really obvious but I know very little about JavaScript. :L
I have a simple navigation bar
Don't use JavaScript then. This can and should be done with CSS. CSS transitions and selectors allow to define exactly what you want.
See also Delay :Hover in CSS3? and the excellent example from there.
Don't use a huge function such as delay(). Just use setTimeout().
var that = this
setTimeout(function() {
$(that).hide() // Do your stuff, just don't forget that "this" has changed
}, 1000) // Define your delay in milliseconds here
The function inside the setTimeout will execute after the delay specified as a second argument.
You can do it like this. You use the delay() method to set up the delay and you use .stop(true) on both hover functions in case the user goes out and comes back in during the delay. The .stop(true) will clear any queued animations. I also switched the code to fadeIn() and fadeOut() because those automatically do the show() and hide() as needed.
<script type="text/javascript">
$(document).ready(function () {
// Navigation bar drop-down
$("nav ul li").hover(function () {
$(this).addClass("active");
$(this).find("ul").stop(true).fadeIn(400);
}, function () {
// Delay on hiding should go here
var self = $(this);
self.find("ul").stop(true).delay(1500).fadeOut(400, function() {
self.removeClass("active");
});
});
$('nav ul li ul li:first-child').prepend('<li class="arrow"></li>');
$('nav ul li:first-child').addClass('first');
$('nav ul li:last-child').addClass('last');
$('nav ul li ul').parent().append('<span class="dropdown"></span>').addClass('drop');
});
</script>
I think you can do something like this:
<script type="text/javascript">
$(document).ready(function () {
// Navigation bar drop-down
$("nav ul li").hover(function () {
$(this).addClass("active");
$(this).find("ul").show().animate({ opacity: 1 }, 400);
}, function () {
// Delay on hiding should go here
$(this).find("ul").hide().delay(1000).animate({ opacity: 0 }, 200, function() {
$(this).removeClass("active");
});
});
$('nav ul li ul li:first-child').prepend('<li class="arrow"></li>');
$('nav ul li:first-child').addClass('first');
$('nav ul li:last-child').addClass('last');
$('nav ul li ul').parent().append('<span class="dropdown"></span>').addClass('drop');
});
</script>
You could use delay().
<script type="text/javascript">
$(document).ready(function () {
// Navigation bar drop-down
$("nav ul li").hover(function () {
$(this).addClass("active");
$(this).find("ul").show().animate({ opacity: 1 }, 400);
}, function () {
// Delay on hiding should go here
$(this).find("ul").delay(5000).fadeOut();
$(this).removeClass("active");
});
$('nav ul li ul li:first-child').prepend('<li class="arrow"></li>');
$('nav ul li:first-child').addClass('first');
$('nav ul li:last-child').addClass('last');
$('nav ul li ul').parent().append('<span class="dropdown"></span>').addClass('drop');
});
</script>
Very interesting. Nothing hides, until you mouseout.
FIDDLE