how to make this slide menu work - javascript

Example: https://jsfiddle.net/lmgonzalves/xj6a74jy/1/
Result: I would like to make a slideUp + slideDown menu the has multiple levels.
I'm stuck trying to get this slide menu to work and I'm not sure how about to get it to work. I've tried using "height"0px" on some css when clicked but ultimately I get back to the same problem. I can make it through the first click in making the slide menu work (meaning there is a slideUp and slideDown), but any level after that the slider just slides up and not down leaving me with no visible menu. Here is what I have:
$('.mobile-nav .navigation a').on('click',function(e){
e.preventDefault();
var t = $(this);
var active = t.closest('li.active');
active.children('ul,a, li.back').not(t.closest('ul')).slideUp();
t.next('ul').slideDown();
});
.mobile-nav .navigation {background:#eee; width:250px; position:relative;}
.mobile-nav .navigation ul {margin:0; padding:0;}
.mobile-nav .navigation a {display:block; line-height:30px;}
.mobile-nav .navigation li ul {display:none;}
<div class="mobile-nav">
<div class="navigation">
<ul>
<li class="active">
All
<ul style="display:block;">
<li>
Topic 1
<ul>
<li class="back">Back</li>
<li>
Some Topic
<ul>
<li class="back">Back</li>
<li>
Some Topic1
((( the menu keeps getting repeated here going deeper, using the format of BackTopic 1Topic 1Topic2 with varying number of li's in each ul.
So the first ul looks like this:
<div class="mobile-nav">
<div class="navigation">
<ul>
<li class="active">
All
</li>
</ul>
/* With 3 more ul's and li's in each
<ul></ul>
<ul></ul>
<ul></ul>
</div>
</div>
When I click on one of the a href tag's, the menu slides to the next level showing the ul, which is the 2nd ul. But when I click on any of the li a's within this ul, I can see the menu start to slide down, but at the same time, the entire ul slides up showing nothing. The ul that was opened now is display:none; even though the next ul is now showing block. I can't figure out how to keep the slides going as they were in the first click.
I can redo classes and such if there is a better way to make this happen.

Fiddled something for you: Fiddle
Hope this is what you need. Just changed the way of selecting the elements.
(function ($) {
"use strict";
$('.mobile-nav')
.on('click', 'a', function (e) {
var $cTarget = $(e.currentTarget),
$dropdown = $cTarget.next('ul'),
$parentUl = $cTarget.closest('ul'),
$activeElem = $parentUl.find('ul.active');
$parentUl.children('li').each(function (key, elem) {
var $elem = $(elem);
if(!$cTarget.parent('li').is($elem)) {
$elem.slideUp();
}
});
$activeElem.toggleClass('active').slideUp();
if (!$dropdown.is($activeElem)) {
$dropdown.toggleClass('active').slideDown();
}
})
.on('click', '.back', function (e) {
var $cTarget = $(e.currentTarget),
$dropdown = $cTarget.closest('ul');
$dropdown.toggleClass('active').slideUp();
$cTarget.parents('li').first().siblings().slideDown();
});})(jQuery);

So these answers are going to be pretty close to each other, but I haven't seen one that meets your "only one item can be open at a time criteria." The JQuery is a little verbose if you want to stick with slipeUp and slideDown but here's an example of the code for handling it for the top-level unordered lists:
$('.toplevel > span').click(function () {
if ($(this).parent().hasClass('activeTop')) {
$('.activeTop').removeClass('activeTop');
$(this).parent().children('ul').slideUp();
return;
}
$('.activeTop').children('ul').slideUp();
$('.activeTop').removeClass('activeTop');
$(this).parent().addClass('activeTop');
$('.activeTop').children('ul').slideDown();
});
I replaced the a tags with spans (and cleaned up the HTML a bit) so I didn't have to deal with my demo fiddle navigating away, but here's a demo implementing the behavior for both top- and second-level menu items.

Check out this fiddle, I would make your structure a little simpler like this https://jsfiddle.net/jk90pxgt/1/ and then your jQuery is only a couple of lines. You can obviously add back buttons if you would like and styling is up to you but this is just a much cleaner way to do the slide menu. Also don't use links and prevent the default, it is just extra code. Just do your click function on the LI
Here is the jQuery
$(".mobile-menu li").click(function(e){
e.stopPropagation();
$(this).children(".sub-menu").slideToggle();
});
New HTML Structure
<ul class="mobile-menu">
<li>First Item
<ul class="sub-menu">
<li>First Sub-Item
<ul class="sub-menu">
<li>First Sub-Item</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Item
<ul class="sub-menu">
<li>First Sub-Item
<ul class="sub-menu">
<li>First Sub-Item
<ul class="sub-menu">
<li>First Sub-Item</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Third Item</li>
</ul>
And CSS
.sub-menu {
display:none;
}
li {
cursor:pointer;
}

Here is how I was able to make this work:
$('.mobile-nav .navigation a').on('click',function(e){
var t = $(this), li = t.closest('li'), ul = li.closest('ul'), a = ul.siblings('a');
if(li.hasClass('back')) {
e.preventDefault();
//do back code here
var sib = ul.closest('li').siblings('li');
a = ul.parents('ul').eq(0).siblings('a');
ul.slideUp();
sib.add(a).slideDown();
} else if(t.siblings().length > 0) {
e.preventDefault();
li.siblings('li').add(a).slideUp();
t.next('ul').slideDown();
}
});

Related

jQuery dropdown toggle find active li and keep the dropdown open

I've got some trouble with a custom dropdown toggle.
The menu looks like this in HTML and I do not have the option to edit the code for that so I have to solve it with css/jQuery:
<ul>
<li>Link"</li> <-- when this is active it has class="active"
<li>Link"</li>
<li>Link"</li>
<li class=""> <-- this is always empty
<span>Title</span>
<ul>
<li class="active">Link</li> <-- this is currently active
<li>Link</li>
<li>Link</li>
</ul>
</li>
</ul>
I'm a bit afraid to ask but here is what I've got:
$('.menu li[class=""] span').click(function() {
const self = $(this).parent();
self.toggleClass("menu-open");
});
if($('.menu li[class=""]').find('li.active').length !== 0){
$(this).addClass("menu-open");
}
The first function which actually works pretty well checks if there is a li with a class="" as this indicates it has a submenu. Clicking the span in the li toggles the class menu-open which results in either having the menu open or collapsed.
Now I would like to keep the menu open when I'm on a page that's in the submenu. If that's the case the li in the sub ul is active, the parent li is not.
Unfortunately that doesn't work. I also tried different approaches such as hasClass, children but none of it worked either. I pinpointed the issue mostly to:
$(this).addClass("menu-open");
as it seems that $(this) was never found. If I change $(this) to $('body') for example and add a hide(), I clearly see that it works because I get a blank page :)
Any advise on what the issue is would be much appreciated as I nearly gave up now.
$(function() {
$('.menu li[class=""] span').click(function() {
const self = $(this).parent();
self.toggleClass("menu-open");
});
if($('.menu li[class=""]').find('li.active').length !== 0){
$(this).addClass("menu-open");
}
});
.menu{width:200px}
ul{list-style-type:none}
li{background:#ccc;margin:2px}
span{display:block}
.menu li[class=""]{
height:20px;
overflow:hidden;
cursor:pointer;
}
.menu-open{
height:auto;
overflow:visible;
cursor:pointer;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
</head>
<body>
<div class="menu">
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
<li class="">
<span>Title</span>
<ul>
<li class="active">Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</li>
<li>Link<li>
<li>Link<li>
<li class="">
<span>Title</span>
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</li>
</ul>
</div>
</body>
</html>
Try to use the each operator that JQuery provides.
It's something like this:
$('.menu li').each(function() {
if($(this).find('li.active').length !== 0) {
$(this).addClass('menu-open');
}
});
In your case, the context that is using the this expression doesn't represent the selector you tried to use in if.
in the example using each, the this expression that is inside the function represents the element being interacted represented by the selector.

jQuery or JavaScript menu drop down on click

Right now, I'm trying to build a vertical menu that will have a drop down sub menu below it.
Below is my HTML and the jQuery function I am using:
$(function() {
$('#menusomething > li').click(function(e) {
e.stopPropagation();
var $el = $('ul', this);
$('#menusomething > li > ul').not($el).slideUp();
$el.stop(true, true).slideToggle(400);
});
$('#menusomething > li > ul > li').click(function(e) {
e.stopImmediatePropagation();
});
});
<div id="navmenu">
<ul id="menusomething" style="padding-left:30px">
<li>HOME</li>
<li>ABOUT</li>
<li>CHAPTERS</li>
<ul class="submenu">
<li>Dallas</li>
<li>Los Angeles</li>
<li>New York</li>
<li>Northern California</li>
<li>Orange County</li>
<li>Phoenix</li>
<li>San Diego</li>
<li>Washington DC</li>
</ul>
<li>MEMBER SERVICES</li>
Figured out the answer for anyone who sees this. First had to move the closing li tag from chapters to the end of .submenu Then used this and now it works as wanted.
$(function() {
$('#menusomething li > .submenu').parent().click(function() {
var submenu = $(this).children('.submenu');
if ( $(submenu).is(':hidden') ) {
$(submenu).slideDown(400);
} else {
$(submenu).slideUp(400);
}
});
});
The following code does what I believe you desire: Have a <ul> element that is the nextElementSibling of the first level <li> element slide open and closed when it is clicked. As you mentioned in comments that you desired, it now starts closed due to adding style="display: none;" to the <ul>.
Note: From a user interface perspective, the <li> entries which don't have sub-menus, or are otherwise links, should not have the text enclosed in <a> tags. With the <a> tags the user will think they are clickable, when a click does nothing. This is confusing to a user. It appears that you may have some be sub-menus and some be direct links. If possible, there should be some visual difference between the two types to hint to the user as to what will happen when they click.
Along with other issues, your HTML has nothing that will match either the '#menusomething > li > ul' or the '#menusomething > li > ul > li' selectors. Specifically, you have no <UL> elements that are children of <LI> elements.
$(function() {
$('#menusomething > li').click(function(e) {
e.stopPropagation();
var nextSib = this.nextElementSibling;
if(nextSib && nextSib.nodeName === 'UL') {
//If we get here the nextSib exists and is a <UL> that immediately follows
// the <LI> which was clicked.
$(nextSib).slideToggle(400);
}
});
$('#menusomething > ul > li').click(function(e) {
console.log('Clicked on chapter: ' + this.textContent);
e.stopImmediatePropagation();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="navmenu">
<ul id="menusomething" style="padding-left:30px">
<li>HOME</li>
<li>ABOUT</li>
<li>CHAPTERS</li>
<ul class="submenu" style="display: none;">
<li>Dallas</li>
<li>Los Angeles</li>
<li>New York</li>
<li>Northern California</li>
<li>Orange County</li>
<li>Phoenix</li>
<li>San Diego</li>
<li>Washington DC</li>
</ul>
<li>MEMBER SERVICES</li>
</ul>
</div>

Event bound to LI is also triggered by clicks on child elements

I got the following structure: - nested UL
<ul class="depth-one">
<li>Category 1
<ul class="depth-two">
<li > Category 1.1</li>
<li> Category 1.2</li>
<li> Category 1.3</li>
</ul>
</li>
<li> Category 2
<ul class="depth-two">
<li>Category 2.1</li>
<li>Category 2.2</li>
<li>Category 2.3</li>
</ul>
</li>
<li>Category 3
<ul class="depth-two">
<li>Category 3.1</li>
<li>Category 3.2</li>
<li>Category 3.3</li>
</ul>
</li>
</ul>
I've applied a rule with CSS:
ul{
list-style: none;
padding:0;
margin:0;
}
.depth-one{
display:block;
}
.depth-two{
display:none;
}
which leaves only the MAIN category shown.
$(document).ready(function() {
$(".depth-one > li").click(function() {
selector = $(this).find(' > .depth-two');
if($(selector).css("display") == "none"){
selector.slideDown(800);
} else {
selector.slideUp(800);
}
});
});
this one, toggles the SUB categories when the MAIN category is being clicked.
here is a fiddle to demonstrate: http://jsfiddle.net/NB4bN/1/
Now, as you can see, when I'm clicking on the SUBCATEGORY, the whole category slides up, any idea why?
I'm trying to achieve that only when I click on the MAIN category, the subcategory will slides up, otherwise, nothing happens when I click on the sub <li> items.
Here is a fix for your problem: http://jsfiddle.net/smerny/NB4bN/2/
$(document).ready(function () {
$(".depth-one > li").click(function (e) {
if (e.currentTarget == e.target) {
selector = $(this).find(' > .depth-two');
if ($(selector).css("display") == "none") {
selector.slideDown(800);
} else {
selector.slideUp(800);
}
}
});
});
I added the check to see if currentTarget and target were the same. So you know if the click is on an element within your li or the li itself.
An easy fix is to stop the event bubbling by adding:
$('li li').click(function (e) {
e.stopPropagation();
});
jsFiddle example
.stoppropagation() prevents an event from bubbling up the DOM tree. So what the above chunk does is look for any list items that are children of other list items and whenever it registers a click on one, it stops the click event from bubbling up the DOM.

How do i .removeClass('active') for just one of my <li> elements with jQuery?

I am having some issues figure out how i can just remove a class ="active" from a just one of my lists.
I have a navigation bar:
<div class="container">
<ul class="nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
I also have a menu within Home:
<div class="container_2">
<ul>
<li class="left-main-list active">Subject 1</li>
<ul class="list-in-list">
<li>Sub subject 1</li>
<li>Sub subject 2</li>
</ul>
<li class="left-main-list>Subject 2</li>
<li class="left-main-list>Subject 3</li>
</ul>
</div>
While i browse my menu on the home page, i want to change the the active list items class to active when clicked, so i now have this jQuery code:
$(document).ready(function() {
$('li').click(function() {
$('li').removeClass('active');
$(this).addClass('active');
});
});
This works for my menu, the class change to the current one, but it also delete my navigation bars class, which i don't want. :)
I have tried something like:
$(document).ready(function() {
$('.left-main-list').click(function() {
$('.left-main-list li').removeClass('active');
$(this).addClass('active');
});
});
I've tried '.left-main-list li' & 'li.left-main-list' without any success.
Greatful for answer to this question, and i hope my question (this time) is more accurate than my previous ones. :)
/Bill
ps: Can a sub subject AND a main subject be active at the same time, and that sub subject's class of active, be removed if you for example click another sub subject, but the main item still have it's class of active?
While i browse my menu on the home page, i want to change the the
active list items class to active when clicked
You could just target the lis within the relevant div, similar to this:
$(document).ready(function() {
var $listItems = $('div.container_2 li');
$listItems.click(function() {
$listItems.removeClass('active');
$(this).addClass('active');
});
});
DEMO - target lis within .container_2 only
Can a sub subject AND a main subject be active at the same time, and
that sub subject's class of active, be removed if you for example
click another sub subject, but the main item still have it's class of
active?
Still targeting the container you could use jQuery's parent(), similar to this:
$(document).ready(function () {
$('div.container_2 li').click(function () {
var $this = $(this);
var $children = $this.parent().find('li');
$children.removeClass('active');
$this.addClass('active');
});
});
DEMO - Using parent() to allow active menu and sub-menu but not when main menu changes
I looked at the possibility of making this more dynamic to add activation of items going up the chain when switching between sub menus located within different main menu elements.
Fixing the HTML of the nested uls whereby your nested uls are inside lis instead of just inside the upper ul you can do a fully dynamic implementation.
Assume your HTML like this:
<div class="container">
<ul class="nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
<div class="container_2">
<ul>
<li class="left-main-list active">Subject 1
</li>
<li>
<ul class="list-in-list">
<li>Sub subject 1
</li>
<li>Sub subject 2
</li>
<li>
<ul class="list-in-list">
<li>Sub subject 1
</li>
<li>Sub subject 2
</li>
</ul>
</li>
</ul>
</li>
<li class="left-main-list">Subject 2
</li>
<li class="left-main-list">Subject 3
</li>
</ul>
</div>
Now, using the following script, you can also make parents of any sub menu items active when changing from a sub menu to another which is within another main menu item, similar to this:
$(document).ready(function () {
$('div.container_2 li>a').click(function () {
var $this = $(this);
var $relatedElements = $this.parents('ul').find('li');
if($this.hasClass('active')){
return;
}
$relatedElements.removeClass('active');
$this.parent('li').addClass('active');
var $parents = $this.parents('li');
$parents.each(function(){
$(this).not($this.parent()).prev().addClass('active');
});
});
});
DEMO - Chain-like activation
I think this should have all possible examples to get you started from here.
Hope this helps.
Try this:
$("li").click(function() {
$(this.parentNode).children("li").removeClass("active");
$(this).addClass("active");
});
This will affect only the siblings of the element you click on.
$('.left-main-list').click(function() {
$('.left-main-list').removeClass('active');
$(this).addClass('active');
});
I think what you're looking for is this:
$(document).ready(function() {
$('li').click(function() {
$('li.left-main-list').removeClass('active');
$(this).addClass('active');
});
});
How about
$('li').on ('click', function (){
$(this).addClass ('active').siblings ('li').removeClass ('active');
})

Jquery menu ul toggle

I have this menu:
<ul id="submenu" class="clearfix">
<li>Vedella</li>
<li>Minis de vedella</li>
<li>Vaca</li>
<li>Poltre</li>
<li>Porc Ibèric</li>
<li>Pollastre</li>
<li>Gall d´indi</li>
<li>Bou</li>
</ul>
Each of the "#submenu" li fadeIn an ul sublist and hide the other sublists.
This are the sublist:
<ul class="sublist first_sublist">
<li>Normal </li>
<li>All i Julivert</li>
<li>Formatge Roquefort</li>
<li>Ceba</li>
</ul>
<ul class="sublist second_sublist">
<li>Mini-Hamburgueses</li>
<li>Surtit Mini-Hamburgueses</li>
</ul>
<ul class="sublist third_sublist">
<li>Normal</li>
</ul>
<ul class="sublist fourth_sublist">
<li>Poltre</li>
</ul>
<ul class="sublist fifth_sublist">
<li>Porc ibèric de Gla</li>
</ul>
and this css:
.second_sublist, .third_sublist, .fourth_sublist, .fifth_sublist, .sixth_sublist{
display: none;
}
with this script:
$('#submenu li').click{
$('#submenu li').removeClass('active');
$(this).addClass('active');
$('.sublist.second_list').hide();
$('.sublist.first_list').fadeIn();
});
The problem with the script is that it will get bigger if i have five sublists, cause i will have to make every click function per "#submenu" li.
Can someone help me to make it simple?
Use class instead of id for binding event so that you do not need id for binding the click event.
$('.clearfix li').click(function(){
$('#submenu li').removeClass('active');
$(this).addClass('active');
$('.sublist.second_list').hide();
$('.sublist.first_list').fadeIn();
});
This is the approach I would take:
Update: I forgot to mention, you could also get rid of the clearfix class.
HTML
<ul id="submenu" class="clearfix">
<li>Vedella</li>
<li>Minis de vedella</li>
<li>Vaca</li>
<li>Poltre</li>
<li>Porc Ibèric</li>
<li>Pollastre</li>
<li>Gall d´indi</li>
<li>Bou</li>
</ul>
<ul class="sublist">
<li>Normal </li>
<li>All i Julivert</li>
<li>Formatge Roquefort</li>
<li>Ceba</li>
</ul>
<ul class="sublist">
<li>Mini-Hamburgueses</li>
<li>Surtit Mini-Hamburgueses</li>
</ul>
<ul class="sublist">
<li>Normal</li>
</ul>
<ul class="sublist">
<li>Poltre</li>
</ul>
<ul class="sublist">
<li>Porc ibèric de Gla</li>
</ul>
JavaScript
$('#submenu li').on('click',function(){
$this = $(this);
// move active class to current list item
$this.addClass('active').siblings().removeClass('active');
// make sure all the sublists are hidden,
// then determine the position of the list item
// in the ul, and select the corresponding sublist
// ex: selecting the 2nd list item in submenu would
// find the 2nd sublist and fadeIn
$('.sublist').hide().eq($this.index()).fadeIn();
});
Of course, this means the sublists would have to be in the same order as the submenu list items.
Check this fiddle
Use the HTML-5 data attributes to store the corresponding sublists in them..
Approaching this way you can use a single handler to show/hide the sublists on the page.
HTML
<ul id="submenu" class="clearfix">
<li>Vedella</li>
<li>Minis de vedella</li>
<li>Vaca</li>
<li>Poltre</li>
<li>Porc Ibèric</li>
<li>Pollastre</li>
<li>Gall d´indi</li>
<li>Bou</li>
</ul>
Javascript
$('#submenu li a').on('click',function() {
var $this = $(this);
var className = $this.data("class");
$('#submenu li a').removeClass('active');
$this.addClass('active');
$('.sublist').hide();
$('.'+ className).show();
});​
Use this if you have done your structure this way or if you change this way:
<ul id="submenu" class="clearfix">
<li>Vedella
<ul class="sublist first_sublist">
<li>Normal </li>
<li>All i Julivert</li>
<li>Formatge Roquefort</li>
<li>Ceba</li>
</ul>
</li>
</ul>
$('#submenu li').click(function(){
$('#submenu li').removeClass('active');
$(this).addClass('active');
$('.sublist.second_list').hide();
$('ul',this).fadeIn();
});

Categories