jquery submenu: li items still toggling - javascript

I've been trying to figure it out for a few hours now and as i'm pretty new to jQuery itself and i'm definetely still a beginner i really need some help with this. I try to create a navigation with a submenu in it. Everything is working as intended but i am missing the way i can stop the list items of the submenu from toggling.
HTML:
<nav>
<ul id="site-nav">
<li>Main Nav Item 1</li>
<li>Main Nav Item 2 (with submenu)
<ul>
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
<li>Main Nav Item 3 (with submenu)
<ul>
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
</ul>
</li>
<li>Main Nav Item 4</li>
<li>Main Nav Item 5</li>
</ul>
</nav>
JQuery (submenu part):
$(document).ready(function() {
var subnavArrow = ['<div id="subnavArrow"></div>'];
subpull = $('header nav ul li');
submenu = $('header nav ul li ul');
submenuitems = $('header nav ul li ul li');
subpull.has('ul').prepend(subnavArrow);
$('header nav ul li').has('ul').click(function() {
$(this).children('ul').toggle('slow');
});
});
If you didn't catch what i mean, i try to explain it as thoroughly as i can:
I want to open the Main Nav Item 2's subitems per click on it (Main Nav Item 2). This is working as intented and i can also close it again, but if i click on a Submenu Item of "Main Nav Item 2" it will close down the Main Nav Item 2's submenu.

You can check whether you have clicked on the anchor or div element like
$(document).ready(function() {
var subnavArrow = ['<div id="subnavArrow"></div>'];
subpull = $('header nav ul li');
submenu = $('header nav ul li ul');
submenuitems = $('header nav ul li ul li');
subpull.has('ul').prepend(subnavArrow);
$('header nav ul li').has('ul').click(function(e) {
if ($(e.target).closest('li').is(this)) {
$(this).children('ul').toggle('slow');
}
});
});
#subnavArrow:after {
content: '<<'
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header>
<nav>
<ul id="site-nav">
<li>Main Nav Item 1
</li>
<li>Main Nav Item 2 (with submenu)
<ul>
<li>Submenu Item 1
</li>
<li>Submenu Item 2
</li>
<li>Submenu Item 3
</li>
</ul>
</li>
<li>Main Nav Item 3 (with submenu)
<ul>
<li>Submenu Item 1
</li>
<li>Submenu Item 2
</li>
</ul>
</li>
<li>Main Nav Item 4
</li>
<li>Main Nav Item 5
</li>
</ul>
</nav>
</header>

Don't append #ID elements. ID should be unique. Use classes instead .subnavArrow
Refer in jQuery to #site-nav as the parent
Use Event.stopPropagation() (if you'll use more nested li>ul)
Always assign your var to variables, or concatenate using ,
$(function() { // DOM ready
var $LIsub = $("#site-nav li:has(ul)");
$LIsub.children("a").append('<span class="subnavArrow"/>');
$LIsub.click(function(e) {
e.stopPropagation();
$(this).find('> ul').slideToggle('slow');
});
});
#site-nav li ul{
display:none;
}
.subnavArrow:after{
content:"\25BC"
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header>
<nav>
<ul id="site-nav">
<li>Main Nav Item 1</li>
<li>Main Nav Item 2
<ul>
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
<li>Main Nav Item 3
<ul>
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
</ul>
</li>
<li>Main Nav Item 4</li>
<li>Main Nav Item 5</li>
</ul>
</nav>
</header>

The problem with your code is that when you click on a submenu <li>, the click event propagates to the parent <li>, and triggers the function hiding the <ul>.
A solution could be to move the .has('ul') part of your click listener inside the callback :
$(document).ready(function() {
var subnavArrow = ['<div id="subnavArrow"></div>'];
subpull = $('header nav ul li');
submenu = $('header nav ul li ul');
submenuitems = $('header nav ul li ul li');
subpull.has('ul').prepend(subnavArrow);
$('header nav ul li').click(function(e) {
e.stopPropagation();
if ($(this).has('ul')) {
$(this).children('ul').toggle('slow');
}
});
});
This way, the listener also listens for the clicks on the submenus, and you can prevent the event from propagating to the parent with e.stopPropagation();
You can sere this solution on this JSFiddle : https://jsfiddle.net/9ejpLmLf/

Very simple code
=============================
<pre>
$(document).ready(function() {
var subnavArrow = ['<div id="subnavArrow"></div>'];
subpull = $('header nav ul li');
submenu = $('header nav ul li ul');
submenuitems = $('header nav ul li ul li');
subpull.has('ul').prepend(subnavArrow);
$('header nav ul li a').click(function() {
$(this).siblings('ul').toggle('slow');
});
});
</pre>

Related

Toggling individual elements of the same class in dropdown menu

I am building a dropdown menu. As is, when I open one submenu, it stays open, even if I open a second submenu. When I open the second submenu, it should close any other open menus.
const submenuLinks = document.querySelectorAll('.has-submenu > a');
submenuLinks.forEach(element => element.addEventListener('click', function(e) {
var submenu = element.nextElementSibling;
if (submenu.classList.contains('menu-hide')) {
submenu.classList.add('menu-show');
submenu.classList.remove('menu-hide');
} else {
submenu.classList.add('menu-hide');
submenu.classList.remove('menu-show');
}
}));
.menu-hide {
display: none;
}
.menu-show {
display: block;
}
<nav>
<ul class="menu">
<li>
Menu Item 1
</li>
<li class="has-submenu">
Menu Item 2 »
<ul class="menu-sub menu-hide">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
<li class="has-submenu">
Menu Item 3 »
<ul class="menu-sub menu-hide">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
</ul>
</nav>
There is no need to use two different classes to show and hide the submenus.
You can hide all submenus by default with CSS and only show the one with
.menu-show
Try this
const submenuLinks = document.querySelectorAll('.has-submenu > a');
submenuLinks.forEach(element => element.addEventListener('click', function(e) {
var submenu = element.nextElementSibling;
if (submenu.classList.contains('menu-show')) {
submenu.classList.remove('menu-show');
} else {
submenuLinks.forEach(a => a.nextElementSibling.classList.remove('menu-show'));
submenu.classList.add('menu-show');
}
}));
.menu-sub {
display: none;
}
.menu-show {
display: block;
}
<nav>
<ul class="menu">
<li>
Menu Item 1
</li>
<li class="has-submenu">
Menu Item 2 »
<ul class="menu-sub">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
<li class="has-submenu">
Menu Item 3 »
<ul class="menu-sub">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
</ul>
</nav>
before your logic that hides the current submenu, you can hide every submenu (ul with class 'menu-sub'). In this way, only the current submenu will be expanded. Here's the edited javascript code:
const submenuLinks = document.querySelectorAll('.has-submenu > a');
submenuLinks.forEach((element) =>
element.addEventListener('click', function (e) {
var allSubmenus = document.querySelectorAll('.menu-sub');
allSubmenus.forEach((submenu) => {
submenu.classList.remove(...submenu.classList);
submenu.classList.add('menu-sub');
submenu.classList.add('menu-hide');
});
var currentSubmenu = element.nextElementSibling;
if (currentSubmenu.classList.contains('menu-hide')) {
currentSubmenu.classList.add('menu-show');
currentSubmenu.classList.remove('menu-hide');
} else {
currentSubmenu.classList.add('menu-hide');
currentSubmenu.classList.remove('menu-show');
}
})
);
What I did there extra was to remove all the classes from the submenus, add back the 'menu-sub' and 'menu-hide' classes.
You must loop over all ul's to check the classes. I commented what i add to you js code.
const uls = document.querySelectorAll('.menu-sub');
uls.forEach(a => {
if (a.classList.contains('menu-show')) {
a.classList.remove('menu-show')
a.classList.add('menu-hide')
}
})
const submenuLinks = document.querySelectorAll('.has-submenu > a');
submenuLinks.forEach(element => element.addEventListener('click', function(e) {
/* start */
const uls = document.querySelectorAll('.menu-sub');
uls.forEach(a => {
if (a.classList.contains('menu-show')) {
a.classList.remove('menu-show')
a.classList.add('menu-hide')
}
})
/* end */
var submenu = element.nextElementSibling;
if (submenu.classList.contains('menu-hide')) {
submenu.classList.add('menu-show');
submenu.classList.remove('menu-hide');
} else {
submenu.classList.add('menu-hide');
submenu.classList.remove('menu-show');
}
}));
.menu-hide {
display: none;
}
.menu-show {
display: block;
}
<nav>
<ul class="menu">
<li>
Menu Item 1
</li>
<li class="has-submenu">
Menu Item 2 »
<ul class="menu-sub menu-hide">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
<li class="has-submenu">
Menu Item 3 »
<ul class="menu-sub menu-hide">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
<li>Submenu Item 3</li>
</ul>
</li>
</ul>
</nav>

DropDown list disappearing before user can the move mouse pointer to it

I have a menu with list items Home and Home 2. When I hover my mouse over "Home"/"Home 2", the dropdown list appears for the item as expected. However, when I am moving my mouse down to the dropdown list, the list disappears immediately and I cant get access to the dropdown menu.
I want the drop down list to be visible while moving my mouse down to the dropdown list preferably using jQuery. However, I cant get it to work.
The HTML Layout for the design is:
<ul class="mega">
<li class="grid">Home
<ul>
<li>DD Item 1</li>
<li>DD Item 2</li>
<li>DD Item 3</li>
</ul>
</li>
<li class="grid">Home2
<div class="dropDownClass"">
<ul>
<li>DD Item 4</li>
<li>DD Item 5</li>
<li>DD Item 6</li>
</ul>
</div>
</li> </ul>
Any help will be much appreciated.
Thanks
I would do something like this.
HTML:
<ul class="mega">
<li class="grid">Home
<ul>
<li>DD Item 1</li>
<li>DD Item 2</li>
<li>DD Item 3</li>
</ul>
</li>
<li class="grid">Home2
<ul>
<li>DD Item 4</li>
<li>DD Item 5</li>
<li>DD Item 6</li>
</ul>
</li>
CSS:
ul{
list-style-type: none;
}
.mega{
width: 200px;
}
.grid{
padding: 10px 15px;
background-color:gray;
color: white;
border: 1px solid black;
}
.grid ul{
display: none;
}
.grid:hover > ul{
display: inline;
}
Check out this fiddle to see it in action.
You need to setup your CSS to resemble this:
ul{list-style: none; padding: 3px; margin:0}
li ul{display:none;}
li.grid:hover ul{display:block}
This will hide the second ul until the li is hovered.
Add this in your document.ready function:
$('.grid1 ul').hide();
$('.grid2 ul').hide();
$('.grid1').on('mouseover', function() {
$('.grid1 ul').show();
$('.grid1').on('mouseout', function() {
$('.grid1 ul').hide();
});
});
$('.grid2').on('mouseover', function() {
$('.grid2 ul').show();
$('.grid2').on('mouseout', function() {
$('.grid2 ul').hide();
});
});
and change the value of the class attributes of the two <li> elements from both being grid to one of them being grid1 and the other being grid2.

Menu with slidable submenu container

I have menu defined with UL, LI... up to max one submenu:
ul
li: item 1
li: item 2
ul
li: item 2.1
li: item 2.2
li: item 3
ul
li: item 3.1
li: item 3.2
li: item 3.3
What I would like to achieve is to show submenu in a way that "sub menu item container" will slide to the next submenu when clicked. In example above this would mean:
when user clicks item 2, container with sub items will show
when user clicks item 3, container with somehow slide/animate/transition from item 2 to item 3, changing sub items
This transition should only happen if sub menu container is already visible... if not, it just shows up next to clicked menu item.
I am not sure how to approach this problem and would like your help on it.
Thank you!
EDIT:
This is hover enabled sample, no JS (I know onclick is not supported in CSS, but I can handle this later):
http://codepen.io/anon/pen/HDinB
This shows a bottom aligned main menu items. Each main menu item can have sub items (this is only one level deep menu, there will be no more sublevels). Submenu will contain no more than max 9 items which will fit into a box (black box you see). What I want is that subitems apear "in" that box, but when new main item with submenu items is selected this box will slide (if already visible) to the new position and kind of be wrapper for new subitems. Will this be a floating box, or real container, or something 3rd, I don't know because I don't realy know what would be best way to handle this.
I'm absolute no jQuery expert, and I presume that jQuery is okay for you.
I created you this function. When a submenu is expanded, the class expand is assigned to it.
When a link is clicked, the function will check if it has the expand class, if so, it will close the submenu. If not, all the other expand classes will be closed (if exist) and the submenu will be shown.
HTML:
<ul>
<li>item 1</li>
<li>item 2
<ul>
<li>item 2.1</li>
<li>item 2.2</li>
</ul>
</li>
<li>item 3
<ul>
<li>item 3.1</li>
<li>item 3.2</li>
<li>item 3.3</li>
</ul>
</li>
</ul>
CSS:
li ul {
display: none;
}
jQuery:
$(document).ready(function() {
$("li:has(ul) a").on('click', function(e) {
e.preventDefault();
var ul = $(this).parent().find('ul');
if( ul.hasClass('expand') ) {
ul.removeClass('expand').slideToggle()
}
else {
$('.expand').removeClass('expand').slideToggle();
ul.addClass('expand').slideToggle();
}
});
});
jsFiddle DEMO.
Hope it suits you, or at least helps you on your way.
<ul class="menu">
<li>item 1</li>
<li>item 2
<ul class="sub-menu">
<li>item 2.1</li>
<li>item 2.2</li>
</ul>
</li>
<li>item 3
<ul class="sub-menu">
<li>item 3.1</li>
<li>item 3.2</li>
<li>item 3.3</li>
</ul>
</li>
</ul>
<style type="text/css">
ul li{display:inline-block; position:relative;}
ul li ul{display:none;width:100%; position:absolute; white-space: nowrap;margin:0px;padding:0px;top:20px;}
ul li ul li{ display:block;}
</style>
<script type="text/javascript">
$(document).ready(function(){
$(".menu li").click(function(){
$(".menu li ul").slideUp();
if($(this).find("ul").css("display")=='block')
{
$(this).find("ul").slideUp();
}
else
{
$(this).find("ul").slideDown();
}
});
});
</script>

jQuery and selecting the next list items objects

I have a nav built with a list and sub nav as lists inside a parent list. The sub nav resides in the next list item to it's corresponding main nav link:
This sits in a div with an id of 'nav'
<ul>
<li>Nav main 1</li>
<li>
<ul>
<li>sub 1</li>
<li>sub 2</li>
<li>sub 3</li>
<li>sub 4</li>
</ul>
</li>
<li>Nav main 2</li>
<li>
<ul>
<li>sub 1</li>
<li>sub 2</li>
<li>sub 3</li>
<li>sub 4</li>
</ul>
</li>
</ul>
Currently I have the following jquery:
$(document).ready(function() {
$("#nav ul li a[href^='#']").each(function(){
if ($(this).next().is(':visible')) {
$(this).next().hide();
} else {
$("#nav ul li a[href^='#']").each(function(){
$(this).next().hide();
});
$(this).next().show();
}
});
I thought this would work to make all the sub menu's hide and then show the one that had been clicked on. For some reason nothing happens. I have checked the console (firebug) and there is no error shown.
Getting frustrated with it now! :-/
EDIT: Here is the answer:
$(document).ready(function() {
$("#nav ul li a[href^='#']").each(function(){
$(this).parent().next().hide();
$(this).click(function() {
if ($(this).parent().next().is(':visible')) {
$(this).parent().next().hide();
} else {
$("#nav ul li a[href^='#']").each(function(){
$(this).parent().next().hide();
});
//then reshow and label the clicked nav
$(this).parent().next().show();
}
});
});
});
HTML:
<div id="nav">
<ul>
<li>
Nav main 1
<ul>
<li>sub 1</li>
<li>sub 2</li>
<li>sub 3</li>
<li>sub 4</li>
</ul>
</li>
<li>
Nav main 2
<ul>
<li>sub 1</li>
<li>sub 2</li>
<li>sub 3</li>
<li>sub 4</li>
</ul>
</li>
</ul>
</div>
JavaScript:
var s = $('#nav ul ul').hide();
$('#nav a').click(function() {
var u = $(this).next();
u.is(':visible') ? u.hide() : ( s.hide(), u.show() );
return false;
});
Live demo: http://jsfiddle.net/Tq6LM/1/
Never mind... I have figured it out. Writing it out again must have helped. I needed to call the .parent()
Seems that i was trying to call the next a href, i needed to call the next list!
DEMO: http://so.devilmaycode.it/jquery-and-selecting-the-next-list-items-objects
your Javascript Code should be:
$(function() {
$('#main-nav li').click(function(e) {
e.preventDefault();
$('#main-nav li ul').slideUp(500);
$(this).find('ul:not(:visible)').slideDown(500);
});
});
little bit of CSS
#main-nav li ul { display:none }
your HTML should look like this:
<ul id="main-nav">
<li>Nav main 1
<ul>
<li>sub 1</li>
<li>sub 2</li>
<li>sub 3</li>
<li>sub 4</li>
</ul>
</li>
<li>Nav main 2
<ul>
<li>sub 1</li>
<li>sub 2</li>
<li>sub 3</li>
<li>sub 4</li>
</ul>
</li>
</ul>

sub nav not directing to page (jquery)

I have basic markup for a drop down nav, nested lists.
The user will click on the top nav item, which will open up the sub nav, but my sub nav links aren't working.
It is in a CMS so I must have the links for the placeholder pages there.
Markup:
<ul class="navtop">
<li>Who
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Long Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
<li>What
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Long Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
</ul>
Javascript:
$(".navtop li").click(function(){
$(this).toggleClass("show");
$(this).siblings(".show").toggleClass("show");
return false;
});
css:
#headernav .navtop li.show ul
{
display: block;
}
I tried adding a 'return true' for $(".navtop li ul li a") but it didn't work.
Suggestions?
Why are you return false to the LI click? I believe that's the problem.
If you take that out, everything should work fine.
If that doesn't work, bear in mind you're attaching the click event to every LI instead of just the top level LIs. Try this instead:
$(".navtop > li").click(function(){
$(this).toggleClass("show");
$(this).siblings(".show").toggleClass("show");
});
Turns out this worked:
$(".navtop > li > a").click(function(){
$(this).parent('li').toggleClass("show");
$(this).parent('li').siblings(".show").toggleClass("show");
return false;
});

Categories

Resources