Add hover class to 2nd level menu links - javascript

I have a vertical mega menu. When you hover over a link that has children it shows another vertical menu to the right and so on. When you hover away from those associated submenus, they are hidden and the menu is reset.
I'm adding a class to the parent link so that the hover state stays active, this works fine. However, my issue is with the 2nd level links that have a sub menu. I'm trying to do exactly the same but with these links but whatever I try doesn't appear to work.
This is what I want to achieve, notice how the 2nd Level Page link has a pink background when the 3rd level menu is open:
This is the JS I'm using and accompanying JSFiddle to show you the stage I'm at currently: http://jsfiddle.net/mz9abf43/2/
$( ".menu li.menu-item-has-children a, .menu li.menu-item-has-children > .drop" ).mouseover(function() {
$('.menu li.menu-item-has-children a').addClass('go');
$('.menu li.menu-item-has-children > .drop a').removeClass('go');
});
$( ".menu li.menu-item-has-children > .drop a" ).mouseover(function() {
$('.menu li.menu-item-has-children > .drop a').addClass('go');
});
$( ".menu li.menu-item-has-children > *" ).mouseout(function() {
$('.menu li.menu-item-has-children a').removeClass('go');
});
UPDATE
I am not committed to the JS solution I've been tinkering with so have no issues with a CSS solution. I just want a solution that makes it work.

The basic solution, which you can complement with any other style you need
/* Hide all child lists */
li > ul {
display: none;
}
/* Hovering list item will cause the child menu to be displayed */
li:hover > ul {
display: block;
}
/* Default style for the menu item */
li {
color: black;
}
/* Hover style for each menu item, add more if you have more levels */
ul > li:hover,
ul > li > ul > li:hover,
ul > li > ul > li > ul > li:hover {
color: red;
}
<ul>
<li>
Item 1
<ul>
<li>
Item 1 a
</li>
<li>
Item 1 b
<ul>
<li>
Item 1 b I
</li>
<li>
Item 1 b II
</li>
<li>
Item 1 b III
</li>
</ul>
</li>
<li>
Item 1 c
</li>
</ul>
</li>
<li>
Item 1
</li>
<li>
Item 1
</li>
</ul>

Simple solution is in css shown below,
What I have done:
Remove all the JS code you have added for this hovering functionality.
Add the class highlight to all the elements which needs to be highlighted(background-color:pink) on hover.
CSS CODE:
.highlight:hover {
background: pink;
}
//comment below css styles
/*.menu li a:hover,
.menu li a.go { background: pink; }*/
Live Demo # JSFiddle

Related

All sub-menus are showing up on click

I want to only show the sub-menu that is the child of the clicked li and button when it is clicked. Currently the click and show and hide are working but the code below shows both the sub-menus on click, I want only the child sub-menu of the li button to show on click.
<ul id="menu-main-menu" class="nav-menu">
<li class="menu-item">Menu link
<button aria-expanded="false" class="dropdown-toggle"></button>
<ul class="sub-menu">
<li class="menu-item">link1</li>
<li class="menu-item">link1</li>
</ul>
</li>
<li class="menu-item">Menu link 2
<button aria-expanded="false" class="dropdown-toggle"></button>
<ul class="sub-menu">
<li class="menu-item">link1</li>
<li class="menu-item">link1</li>
</ul>
</li>
</ul>
<div class="site-content"></div>
jQuery:
jQuery(document).ready(function ($) {
$("#menu-main-menu").on('click', 'button', function (event) {
$('ul.sub-menu').appendTo('.site-content');
if($('ul.sub-menu:visible').length)
$('ul.sub-menu').hide();
else
$('ul.sub-menu').show();
});
});
CSS:
#menu-main-menu ul.sub-menu {
display: none;
}
ul.sub-menu {
display: none;
position: absolute;
z-index: 200000;
top: 0;
left: 1.5%;
right: 1.5%;
margin: 0 auto 0 auto;
padding: 20px;
list-style: none;
}
ul.sub-menu li {
width: 24%;
display: inline-block;
padding: 8px 9px;
text-align: center;
}
ul.sub-menu .toggled-on {
display: block;
}
.site-content {
display: block;
position: relative;
}
Solution: So the solution here was to not use appendTo(), as I had to put the element back where it came from when toggled off. The solution was to merely toggle the menu item using correct position: absolute CSS for the .sub-menu and $()on('click' to toggle it.
jQuery('#menu-main-menu').on('click', 'button', function(event) {
if($(this).closest("li.menu-item").children("ul.sub-menu").length > 0)
{
$(this).closest("li.menu-item").children("ul.sub-menu").slideToggle('fast');
return false;
}
});
See it working here: http://jsfiddle.net/abdqt6d9/
The problem is that you are writing incorrect selectors for your jquery:
$('ul.sub-menu')
That means it will grab all matching elements within the page.
What you need to do is grab the corresponding li. Within your click(), the $(this) becomes the button that is clicked. Using .parent() will give you the li element. From there, search for your corresponding sub-menus within the li_element:
var $li_element = $(this).parent()
var $sub_menu = $li_element.find(".sub-menu")
if ($li_element.find(".sub-menu:visible").length > 0) {
$sub_menu.hide()
} else {
$sub_menu.show()
}
The other problem is that perhaps your styling for your sub-menu is above the buttons. so once you show it, you can no longer press the button. So you need to restyle your sub-menus.
$("ul.sub-menu") will apply to all the sub-menus, so you need to change it to only look for the sub-menu within the buttons parent. You can do this using .closest (or just .parent()) and then .find
//closest("li") will find the closest parent that is an li
//find(".sub-menu") will find the sub-menu within
$(this).closest("li").find(".sub-menu").show();
If you your button is always going to be before the sub-menu you can slim it down to just .next(".sub-menu")
$(this).next(".sub-menu").show();

I'm trying to select a nested ul, tried with css but having trouble

I have a cms rendering the menus for a website I am designing and I need to select the children of the parent menu items. Here is how the code is generated:
<div class="menu">
<ul>
<li></li>
<li><a href="" class="CurrentButton" ... />text</a>
<div>
<ul></ul>
</div>
</li>
</ul>
</div>
I have tried this CSS to try to select it but it's been unsuccessful as of yet aside from the display :none;
.menu ul li div {
display: none;
}
.menu > ul > li > a.CurrentButton div {
display: block;
}
can anyone see what I'm doing wrong? Would a jquery function be easier? I'm fairly new to jquery so I'm not sure how to go about writing a function for it.
I am trying to select the div within the li when the anchor within that li has the class CurrentButton, if the anchor within the li doesn't have the class then I want it hidden
Both of the examples you give rely on finding the .menu element, but none exists in your code. it does now.
a.CurrentButton div selects any divs inside of any a.CurrentButtons. However, your divs are not inside the as. Try this:
.menu ul > li > a {
//selects all the as, but non of the divs
}
.menu ul > li > * {
//selects anything inside a 'li', both 'a's and 'div's
}
To select divs that follow a.CurrentButtons, use this:
.menu ul li > a.CurrentButton + div {
//any 'div's that are directly after 'a.CurrentButton'
}
If you really need to be specific, use the adjacent elements operator ( + )
.menu > ul > li > a.CurrentButton + div {
Otherwise, you are targeting a div that is the descendent of CurrentButton and that doesn't exist.
If you don't need to be so specific, use the same selector as before:
.menu > ul > li > div {
Assuming the <ul> above is a child of an element with the class .menu, the <div> above is not a child of a.CurrentButton, so you should select it like this:
.menu > ul > li > div {
display: block;
}
Just so you know > only selects direct children of an element.
Try this:
In your HTML div is a sibling of a.CurrentButton. So, you should use + sign.
.menu ul li div {
display: none;
}
.menu > ul > li > a.CurrentButton + div {
display: block;
}

Change opacity of other tab when I hover current tab

I create a dropdown menu. I need when I hover to a tab, the opacity of other tabs in menu are change except the current tab I hover.
Example: when I hover to Home Tab, state of Home tab and list item is not changed (yellow color, opacity=1) but other tabs (Tutorial, Article, Inspiration) are changed (grey color, opacity=0.5)
<code>http://jsfiddle.net/dennisho/6fX42/2/</code>
There is no sibling selector that will select all siblings to help select the other menu elements but you can use the :not selector
nav > ul:hover li:not(:hover) {
opacity:0.5;
}
JSFiddle Demo
You could do something like this.
nav ul li {
background-color: yellow;
}
nav ul li:first-of-type {
border-top-left-radius:25px;
border-bottom-left-radius:25px;
}
nav ul li:last-of-type {
border-top-right-radius:25px;
border-bottom-right-radius:25px;
}
nav > ul li:hover{
opacity:1;
}
nav > ul li:not(:hover){
opacity:0.5;
}
But please include relevant code in your question so it is helpful for other people, too.

JS Menu keep menu state

I have this HTML Code:
<div id="nav">
<li>Dashboard</li>
<li><a>Contacts</a>
<ul>
<li><strong>Companies</strong></li>
<li>Add Company</li>
<li>View Company</li>
</ul>
</li>
</div>
and this JS:
<script>
$(document).ready(function () {
$('#nav > li > a').click(function(e){
if ($(this).attr('class') != 'active'){
$('#nav li ul').slideUp();
$(this).next().slideToggle();
$('#nav li a').removeClass('active');
$(this).addClass('active');
}
});
});
</script>
for my vertical menu but i cant work out how to keep the menu state when the page changes.
for example, if the is expanded, how can i keep it expanded if the page changes?
here is the CSS to:
#nav {
float: left;
margin-left:5px;
margin-top:-20px;
top:0;
left:0;
width: 100%;
list-style:none;
}
#nav li a {
display: block;
padding: 8px 10px;
margin-bottom:0;
background: #666666;
border-top: 1px solid #EEEEEE;
border-bottom: 1px solid #EEEEEE;
text-decoration: none;
color: #EEEEEE;
width:155px;
}
#nav li a:hover, #nav li a.active {
background: #F36F25;
color: #FFFFFF;
cursor:pointer;
}
#nav li ul {
display: none;
list-style:none;
}
#nav li ul li {
margin-top:0;
margin-right:0;
margin-bottom:0;
margin-left:-40px;
}
#nav li ul li a {
background: #EEEEEE;
color:#666666;
border:1px solid #EEEEEE;
}
#nav li ul li a:hover {
background: #EEEEEE;
color:#f36f25;
border:1px solid #f36f25;
}
I would suggest using sessionStorage in this scenario. It's a great tool in this case, and it is widely supported, but see http://caniuse.com/namevalue-storage to see if its suitable for your needs. What you can do is use sessionStorage to keep track (client-side) of your currently expanded menu so you can expand the correct menu on a page reload. This answer is not 100% correct in the sense that you can't just plug it in directly into your code (I would have had to guess at several things) but it should give you a fairly idea of where to go. Note that in the code below, I changed link hrefs to point to JSFiddle because that is where I made a working example, but hopefully this will get you on the right track to implement it in your own pages.
One of the main things necessary to change is to give main menu <a> tags an ID (below, they are menuDashboard and menuContacts). These would have to be consistent across your different pages, and also the scripts below would have to be included in all the pages where you want to keep the menu state. Then the basic premise is that on menu click, we store the currently expanded menu <a> ID into sessionStorage so we can access that after a page reload. Then, on page load, we look at sessionStorage to see what was previously selected by retrieving the key "activeMenuItemID", and if we find that is not undefined, we expand that menu item.
Working example: http://jsfiddle.net/VBLS8/2/show/
Note, because of how JSFiddle is built, the previous link is a link directly to JSFiddle Results iframe is. Otherwise, when clicking the links JSFiddle just breaks. The actual JSFiddle is here: http://jsfiddle.net/VBLS8/2/.
<br/>
<div id="nav">
<li>
<a id="menuDashboard">Dashboard</a>
<ul>
<li><strong>Sub Category</strong></li>
<li>Sample 1</li>
<li>Sample 2</li>
</ul>
</li>
<li>
<a id="menuContacts">Contacts</a>
<ul>
<li><strong>Companies</strong></li>
<li>Add Company</li>
<li>View Company</li>
</ul>
JavaScript:
$(document).ready(function(){
//Loop through nav items, compare to expanded item ID from sessionStorage so we can expand whichever item was previously expanded
if(sessionStorage.getItem("activeMenuItemID") != undefined){
$("#nav > li > a").each(function(){
if ($(this).attr("id") == sessionStorage.getItem("activeMenuItemID")){
expandMenuItem(this);
}
});
}
$('#nav > li > a').click(function(elem){
expandMenuItem(this);
});
});
function expandMenuItem(elem){
if ($(elem).attr('class') != 'active'){
$('#nav li ul').slideUp();
$('#nav > li > a').removeClass("active");
$(elem).addClass("active");
$(elem).next().slideToggle();
sessionStorage.setItem("activeMenuItemID", $(elem).attr("id"));
}
}
When the page changes, the click handler gets bound, but there is no statement handling the initial state of the menu.
So...
$(document).ready(function() {
//original click handler
//$('#nav a').click
//but also this piece of code, that will display all the lists having an .active link inside
$('#nav ul').has('a.active').show();
});
Regards and good luck!
A quick but a little dirty solution to keep track of your currently active page is to compare the src attribute of your target frame with the href attribute of your links.
Edit: The following fiddle might help you a bit: fiddle

Why does jQuery fail to match ":not(#menu *)"

I've homerolled a Javascript based nav bar (I want it to be click, not hover for mobile friendliness). I've tried to make the HTML, CSS, and Javascript as simple as possible.
I put a listener on the body to close the menu bars if you click anywhere else on the website. I'm trying to filter the listener so that it does not fire if you click on any part of the menu bar. This is to prevent the dropdowns from rolling back up when you click on an element in the dropdown.
I expect the below matcher to ONLY match items that are descendants of the top level menu element. Any advice on why this isn't working would be much appreciated.
EDIT: I understand that you can use an arbitrary function to evaluate the bubble coming up and decide on whether or not you should act on it, but I'm more interested in why the below .on() selector is not working the way I expect.
$("body").on("click", ":not(#menu *)", function (e) {
$("#menu a").next().slideUp(DROP_DOWN_TIME);
});
http://jsfiddle.net/auKzt/16/
HTML
<body>
<div>HEADER</div>
<ul id="menu" class="cf">
<li>FooafsdasiuhfauhfuahsdfFooaf sdasiuhfauhfuahsdfFooafsdasiuhfauhfuahsdf
<ul>
<li>Subitem asdasdasd 1 sadad
</li>
<li>Subitem 2</li>
<li>Subitem 3</li>
</ul>
</li>
<li>Bar
<ul>
<li>Subitem 1</li>
<li>Subitem 2</li>
<li>Subitem 3</li>
</ul>
</li>
<li>Qux</li>
</ul>
<div>CONTENT</div>
<div>FOOTER</div>
</body>
JAVASCRIPT
$(document).ready(function () {
var DROP_DOWN_TIME = 200;
//Setup hidden dropdowns
$("#menu > li > a").next().toggle();
//Hide or unhide dropdowns onclick
$("#menu > li > a").click(function (e) {
$("#menu a").next().not($(e.target).next()).slideUp(DROP_DOWN_TIME);
$(e.target).next().slideToggle(DROP_DOWN_TIME);
e.stopPropagation();
});
$("body").on("click", ":not(#menu *)", function (e) {
$("#menu a").next().slideUp(DROP_DOWN_TIME);
});
});
CSS
#menu {
width: 900px;
padding: 0;
margin: 0;
}
/* Top level menu container */
#menu li {
background-color: aqua;
border: 1px solid black;
padding: 2px;
}
/* Top level menu items */
#menu > li {
display: inline-block;
float: left;
position: relative;
}
/* Submenu container */
#menu > li > ul {
position:absolute;
padding: 0;
margin: 0;
top: 100%;
left: -1px;
}
/* Submenu Items */
#menu > li > ul > li {
display: block;
float: none;
white-space: nowrap;
}
.cf:before, .cf:after {
content:" ";
/* 1 */
display: table;
/* 2 */
}
.cf:after {
clear: both;
}
It is failing because the click is being caught on the menu element. The content inside is being caught.
You need to also add the menu element
$("body").on("click", ":not(#menu, #menu *)", function (e) {
^^^^^^
Try this instead
$("body").on("click", function (e) {
if( e.taget.id !== 'menu' && !$(e.target).closest('#menu').length) {
$("#menu a").next().slideUp(DROP_DOWN_TIME);
}
});

Categories