I have the following simple 3 level menu structure:
<nav class="main-nav" class="list">
<ul>
<li class="first lev1">Home</li>
<li class="lev1">Lev 1 1</li>
<li class="lev1 hasc active">
Lev 1 2
<ul>
<li class="first lev2 hasc">
Lev 2 1
<ul>
<li class="first lev3">Lev 3 1 </li>
<li class="lev3">Lev 3 2</li>
</ul>
</li>
</ul>
</li>
<li class="lev1 hasc active">Lev 1 3</li>
</ul>
</nav>
I'm trying to make the menu work so that the second level menu slides open when it has children but is an active link when it does not and then all 3rd level links are always active links.
The following jquery code works for all the required functionality for the 2nd level menu (prevents default and opens the 3rd level if there are children, but if not makes the link active) the problem is that i'm not sure how to over ride the prevent default for the third level?
$(".lev2").click(function (event) {
event.preventDefault();
if ($(this).hasClass('on')) {
$(this).children('ul').slideUp();
$(this).removeClass('on');
} else {
$(this).children('ul').slideDown();
$(this).addClass('on');
}
});
I've looked into starting at the root of the menu class 'main-nav' and then try to branch the code but as it is nested I'm not finding any logic that will work? any ideas most welcome.
The problem seems to be that you´re binding the click event to the entire li element and not just the link.
Try this instead:
$(".lev2 > a").click(function (event) {
event.preventDefault();
$(this).parent().toggleClass('on').children('ul').slideToggle();
});
It binds the event to the link and toggles your actions on the parent li and ul for the next level.
Live example at jsFiddle
This works. Just checks for a level 3 click first to tell the level 2 function whether level 3 has been clicked or not.
$(document).ready(function(){
var level2 = true;
$("li.lev3").click(function(){
level2 = false;
});
$("li.lev2").click( function(event){
if (level2){
alert ("lvl2");
event.preventDefault();
if($(this).hasClass('on')) {
$(this).children('ul').slideUp();
$(this).removeClass('on');
}else{
$(this).children('ul').slideDown();
$(this).addClass('on');
}
}
});
});
Related
I need to open a submenu, clicking on the parent element. I could do it this way
$(function(){
$('li.dropdown > a').on('click',function(event){
event.preventDefault();
$(this).toggleClass('selected');
$(this).parent().find('ul').first().toggle(300);
$(this).parent().siblings().find('ul').hide(200);
//Hide menu when clicked outside
$(this).parent().find('ul').parent().mouseleave(function(){
var thisUI = $(this);
$('html').click(function(){
thisUI.children(".dropdown-menu").hide();
thisUI.children("a").removeClass('selected');
$('html').unbind('click');
});
});
});
});
But, sometimes I have an actual link as a parent element. And I'd want to go to that link on click. And open the dropdown otherwise. Tried with a click on dropdown:before/ dropdown:after pseudoelements with no luck. And I can't manage to do it adding a span inside dropdown div.
Thank you for any help.
LE: My HTML structure looks like this
<nav id="main-nav">
<ul>
<li>Home</li>
<li class="dropdown">Main Cat
<ul class="dropdown-menu">
<li>Sub Cat1</li>
<li>Sub Cat2</li>
<li>Sub Cat3</li>
</ul>
</li>
<li>Second Cat</li>
</ul>
</nav>
Check my html code bellow. There i want if Level1 clicked it will hide all of its child ul and li of level1 then if clicked it back it will show again i mean toggle between them. Like this way if i click Level2 it will only toggle its child ul and li as like Level1. But as per my current first step i am trying to toggle on Level1 but the issue it once i click Level1 its totally getting hidden and not come back again. Anyone can tell me how can i achieve this?
here is my fiddle link
Html:
<ul>
<li class="l1">
Level1
<ul>
<li>
Level2
<ul>
<li>Level2 child</li>
<li>Level2 child</li>
<li>Level2 child</li>
</ul>
</li>
<li>Level2</li>
<li>Level2</li>
</ul>
</li>
<li class="l1">
Level1
<ul>
<li>Level2</li>
<li>Level2</li>
<li>Level2</li>
</ul>
</li>
</ul>
Jquery:
$(document).ready(function () {
$('.l1').on('click',function(){
$(this).toggle();
//alert("clicked");
});
});
I think the issue you're facing is that you're selecting the whole element rather than just it's children. Check out the following JS and let me know if that's what you're looking for
$(document).ready(function () {
$('.l1').on('click',function() {
$(this).find("ul").toggle();
});
});
In short, I have a 2 main links (Private Car and Commercial Vehicle) each with a specific class attached to their anchor tags. The same class names are used on the li tags of a second sublink ul to match them with to two top links. The idea is that each time a main link is clicked, the following happens:
The sublink ul slides up
All the li's inside are hidden
The li's with the corresponding main link class are shown
The sublink ul slides down showing only the correct li's
Unfortunately that is not the order that the functions fire in. What happens is this:
The sublink ul slides up
The sublink ul slides down
All list elements inside are hidden
The relevant list elements slide down
Any idea on how I can get the order to fire as I want it?
Here is the code
$('.insurer ul.toplinks a').click(function(e) {
e.preventDefault();
var sublinkCategory = $(this).attr('class'),
subLinksToShow = $(this).parent().parent().parent().find('li.' + sublinkCategory),
subLinksList = $(this).parent().parent().parent().find('ul.sublinks'),
allLinks = $(subLinksList).find('li');
// First time
if ($(subLinksList).is(":hidden")) {
$(subLinksToShow).slideDown();
$(subLinksList).slideDown();
// List visible but new links invisible
} else if ($(subLinksList).is(":visible") && $(subLinksToShow).is(":hidden")) {
$(subLinksList).slideUp(function() {
$(allLinks).hide(function() {
$(subLinksList).slideDown(function() {
$(subLinksToShow).slideDown();
});
});
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="toplinks">
<li>Private Car</li>
<li>Commercial Vehicle</li>
</ul>
<ul class="sublinks">
<li class="privatecar">Key Facts</li>
<li class="privatecar">Policy Wording</li>
<li class="commercialvehicle">Key Facts</li>
<li class="commercialvehicle">Policy Wording</li>
</ul>
Your code isn't working while we don't have the fully code.
This works, note the comments in the code to see what happens on the line under the comment.
$(function() {
// hide by default
$('.sublinks').hide();
$('.toplinks a').on('click', function(e) {
e.preventDefault();
// get the classname
var cl = $(this).attr('class');
// slide up
$('.sublinks').slideUp("slow", function() {
//on callback (= after slide up is done)
// show all links
$('.sublinks li').show();
// hide the ones not having the right class
$('.sublinks li:not(.'+cl+')').hide();
// slide down again
$('.sublinks').slideDown("slow");
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="toplinks">
<li>Private Car</li>
<li>Commercial Vehicle</li>
</ul>
<ul class="sublinks">
<li class="privatecar">private Key Facts</li>
<li class="privatecar">private Policy Wording</li>
<li class="commercialvehicle">commercial Key Facts</li>
<li class="commercialvehicle">commercial Policy Wording</li>
</ul>
I have a menu with 3 levels, and I would like to use a class for the first active li and a second class for all other subsequent li. When I click on a selection the level 3 to remain active the whole path (level 1, level 2, level 3). If I click on a selection on level 2 to remain active up to level 2.
I have the following:
$(document).ready(function(){
$('.sf-menu li a').each(function(index) {
if((this.pathname.trim() == window.location.pathname))
$(this).parent().addClass("selected");
var next_li = $(this).parent().next();
$('a', next_li).addClass("selected2");
});
});
I think I got it this time, It's a bit dirty but It works.
First add classes so you can identify first, second and third level <li> elements. Do it in the foreach, or whatever bucle that makes the menu (or by hand if there's no such bucle):
<ul id="navlist" >
<li id="home" class="firstLevel">
<a class="nav" href="home">Home</a>
<ul class="secondLevel">
<li class="secondLevel">
<a class="nav2" href="home">sub-Home1</a>
<ul>
<li class="thirdLevel"><a class="nav3" href="home">sub-sub-Home1></a></li>
<li class="thirdLevel"><a class="nav3" href="home">sub-sub-Home1></a></li>
</ul>
</li>
<li><a class="nav2" href="home">sub-Home2</a></li>
</ul>
</li>
<li id="about" class="firstLevel">
<a class="nav" href="about-us">About Us</a>
</li>
</ul>
Then use jQuery closest. It traverses up the DOM tree and matches the closest item, you can pass a selector (the firstLevel or secondLevel classes we just created):
$('#navlist a').on('click', function (e) {
e.preventDefault(); //prevent the link from being followed
$('#navlist a').removeClass('selected');
$('#navlist a').removeClass('selected2');
$(this).closest('.secondLevel').children('a').addClass('selected2');
$(this).closest('.firstLevel').children('a').addClass('selected2');
$(this).addClass('selected');
});
Then you add !important to the selected class (so when there's a colision like in the About Us link selected is the class that is applied). This is the dirtiest part.
Check a working fiddle: https://jsfiddle.net/4r5vg/661/
I am making a menu that has submenu. When I click on a menu item I am using prevent default because it is a tags, but on the submenu level I don't want to prevent default. I haven't been able to figure out how to make it work so it doesn't affect the top level.
<div id="block-menu-block-2">
<ul class="menu">
<li>
1
</li>
<li>
2
<ul class="menu">
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
<li>2.4</li>
</ul>
</li>
<li>
3
<ul class="menu">
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
<li>3.4</li>
</ul>
</li>
<li>
4
</li>
<li>
5
</li>
<li>
6
<ul class="menu">
<li>6.1</li>
</ul>
</li>
</ul>
</div>
Here is the jQuery
$('#block-menu-block-2 ul li').on("click", function() {
if ($(this).children().is('ul')) {
if ($(this).find('ul').is(':visible')) {
$(this).find('ul').hide("blind");
$(this).removeClass('menuItemSelected');
$(this).find('ul').removeClass('menuItemSelected');
} else {
$(this).parent().find('li ul').hide("blind");
$(this).parent().find('.menuItemSelected').removeClass('menuItemSelected');
$(this).find('ul').show("blind");
$(this).addClass('menuItemSelected');
$(this).find('ul').addClass('menuItemSelected');
};
event.preventDefault()
}
});
Here is a codepen for reference: http://codepen.io/mathiasha/pen/bVpMyo
Added some stuff. Changed some stuff. Couldn't paste into codepen from my phone so this might not work. Code first, then word wall.
$('#block-menu-block-2 ul li').each (function () {
var $this = $(this);
if ($this.find ('ul:first').length > 0) {
$this.click (function () {
if ($this.find ('ul:visible').length > 0) {
$this.removeClass ('menuItemSelected').find ('ul').removeClass('menuItemSelected').hide ('blind');
} else {
$this.parent ().find ('ul li').hide ('blind');
$this.parent ().find('.menuItemSelected').removeClass ('menuItemSelected');
$this.addClass ('menuItemSelected').find ('ul').show ('blind').addClass ('menuItemSelected');
}
});
}
});
$('#block-menu-block-2 > ul > li > a').click (function (e) {
if ($(this).find ('ul:first').length > 0)
e.preventDefault ();
});
The real answer lies in only putting the preventDefault only on the a tag and only when it is the immediate child of a li tag tjat is the immediate child of a ul tag that is the immediate child of the block-menu. See the last 3 lines.
The rest of the code below should only add the click listener to li tags with ul tags inside. Tried to use chaining to limit the number of jQuery objects created. Might have messed up what it was doing. You only really need to remove preventDefault from where it is and than use the last 3 lines.
Can you not add a class to your submenu triggers, e.g. .submenu-trigger, and then use the following jQuery:
$(document).on('click', function(e) {
if ($(e.target).hasClass('submenu-trigger')) e.preventDefault();
});
Ignoring all the other menu manipulation and putting the event on <a> tags you can simply check if the <a> has a sibling <ul> and if it does prevent default
$('#block-menu-block-2 a').click(function(e){
if( $(this).siblings('ul').length ){
e.preventDefault();
}
// menu manipulation code
});