All,
I am new to JQuery and trying to write JQuery code to create a multi level dropdown menu. The HTML looks like below:
<ul id="menu">
<li>Link 1
<ul class="submenu">
<li>Sub Link 1.1
<ul class="submenu">
<li> Sub Link 1.1.1</li>
<li> Sub Link 1.1.2</li>
</ul>
</li>
<li>Sub Link 1.2</li>
<li>Sub Link 1.3</li>
</ul>
</li>
<li>Link 2
<ul class="submenu">
<li>Sub Link 2.1
<ul class="submenu">
<li> Sub Link 2.1.1</li>
<li> Sub Link 2.1.2</li>
</ul>
</li>
<li>Sub Link 2.2</li>
</ul>
</li>
</ul>
The JQuery code I got so far is as under, but it's not opening and closing the submenus.How can I make it work?
$(document).ready(function () {
$('#ul.menu > li').hover(function () { $('ul:first', this).show(); },
function () { $('ul:first', this).hide(); }
);
$('#ul.menu li li').hover(function () {
$('ul:first', this).each(function () {
var p = $(this).parent();
$(this).css('top', p.position().top)
.css('left', p.position().left + p.width())
.show();
});},
function () { $('ul:first', this).hide(); }
);
});
Chick this out: http://jsfiddle.net/g5xSX/ , maybe it is exactly what you want
I created this fiddle, it's just a start but some of it it's working http://jsfiddle.net/mZzqu/2/
I simplified your markup (attach click handler to 'li', it's better)
<ul id="menu">
<li>Link 1
<ul class="submenu">
<li>Sub Link 1.1
<ul class="submenu">
<li id='1'> Sub Link 1.1.1</li>
<li>Sub Link 1.1.2</li>
</ul>
</li>
<li>Sub Link 1.2</li>
<li>Sub Link 1.3</li>
</ul>
</li>
<li>Link 2
<ul class="submenu">
<li>Sub Link 2.1
<ul class="submenu">
<li> Sub Link 2.1.1</li>
<li> Sub Link 2.1.2</li>
</ul>
</li>
<li>Sub Link 2.2</li>
</ul>
</li>
</ul>
jquery code
$.fn.dropdown = function (options) {
var settings = jQuery.extend({
timeout: 0.2
}, options);
var closetimer = null;
var ddmenuitem = null;
$(this).children('li').each(function(){
$(this).find('ul').hide();
});
$(this).find('li').hover(dropdown_open, dropdown_close);
document.onclick = dropdown_close;
function dropdown_open(event)
{
dropdown_canceltimer();
//dropdown_close();
ddmenuitem = $(event.target).children('ul').css('display', 'block');
}
function dropdown_close(event) {
$(event.target).parent('ul:not(#menu)').hide();
}
function dropdown_timer() {
closetimer = window.setTimeout(dropdown_close, settings.timeout * 1000);
}
function dropdown_canceltimer() {
if (closetimer) {
window.clearTimeout(closetimer);
closetimer = null;
}
}
return this;
}
Related
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>
jQuery drop-list is keeping slideup and down many times
HTML:
<nav class="main-nav">
<img src="img/logo.jpg">
<ul class="nav-list">
<li>Home Page</li>
<li>About us</li>
<li>Products
<ul class="dropdown">
<li> Product 1</li>
<li> product 2</li>
<li>product 3</li>
<li>Product 4</li>
<li>product 5</li>
<li>product 6</li>
</ul>
</li>
<li>Career</li>
<li>Contact us</li>
</ul>
</nav>
JavaScript:
$(function () {
$("li").has(".dropdown").hover(
function () {
$(this).find(".dropdown").slideDown();
},
function () {
$(this).find(".dropdown").slideUp();
}
);
});
When you are hovering in and out of the Products item multiple times in quick succession, the slideDown and slideUp animations get queued. Call stop() to cancel the current animation before doing a new slideDown or slideUp:
$("li").has(".dropdown").hover(
function () {
$(this).find(".dropdown").stop().slideDown();
},
function () {
$(this).find(".dropdown").stop().slideUp();
}
);
Actually I'm really very sorry about my question, I'm not sure how to make attention or ask question about this kind of problem.
Please see my code 1st.
<div data-model="ABC123" id="product">Select a Product</div>
<ul id="lists">
<li data-product="P1">Product 1
<ul id="sublists">
<li data-item="it1">P1 Item 1</li>
<li data-item="it2">P1 Item 2</li>
<li data-item="it3">P1 Item 3</li>
<li data-item="it4">P1 Item 4</li>
</ul>
</li>
<li data-product="P2">Product 2
<ul>
<li data-item="it1">P2 Item 1</li>
<li data-item="it2">P2 Item 2</li>
<li data-item="it3">P2 Item 3</li>
<li data-item="it4">P2 Item 4</li>
</ul>
</li>
<li data-product="P3">Product 3</li>
<li data-product="P4">Product 4</li>
</ul>
<div id="codes">
<span class="code1"></span>
<span class="code2"></span>
<span class="code3"></span>
</div>
The jquery code is:
<script>$('#product').click(function () {
var pmodel = $(this).data('model');
$('.code1').empty().append(pmodel);
$('.code2').empty();
$('.code3').empty();
});
$('#lists li').click(function () {
var dmodel = $(this).data('product');
$('.code2').empty().append(dmodel);
});
$('#lists li ul li').click(function () {
var item = $(this).data('item');
$('.code3').empty().append(item);
});</script>
You may directly view this at http://codepen.io/alshedupur/pen/YqKGqV
Everything is work fine, but my problem is, when I trigger parent list item like: Product 1 / Product 2 / Product 3
In result I want to empty .code3 span
I try to use $('.code3').empty(); on 2nd action but if I use this, then 3rd action I mean sub list click function not work.
Please see my screenshot for clearly understand what I want:
You need to empty .code3 as well.
$('#product').click(function() {
var pmodel = $(this).data('model');
$('.code1').empty().append(pmodel);
$('.code2').empty();
$('.code3').empty();
});
$('#lists > li').click(function(e) {
e.stopPropagation();
var dmodel = $(this).data('product');
$('.code2').empty().append(dmodel);
$('.code3').empty();
});
$('#lists li ul li').click(function(e) {
e.stopPropagation();
var item = $(this).data('item');
var dmodel = $(this).parents("li").data('product');
$('.code2').empty().append(dmodel);
$('.code3').empty().append(item);
});
#product:hover,
li:hover {
cursor: pointer
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-model="ABC123" id="product">Select a Product</div>
<ul id="lists">
<li data-product="P1">Product 1
<ul id="sublists">
<li data-item="it1">P1 Item 1</li>
<li data-item="it2">P1 Item 2</li>
<li data-item="it3">P1 Item 3</li>
<li data-item="it4">P1 Item 4</li>
</ul>
</li>
<li data-product="P2">Product 2
<ul>
<li data-item="it1">P2 Item 1</li>
<li data-item="it2">P2 Item 2</li>
<li data-item="it3">P2 Item 3</li>
<li data-item="it4">P2 Item 4</li>
</ul>
</li>
<li data-product="P3">Product 3</li>
<li data-product="P4">Product 4</li>
</ul>
<div id="codes">
<span class="code1"></span>
<span class="code2"></span>
<span class="code3"></span>
</div>
I got a small Javascript menu problem.
I got a ul list that looks something like this.
<ul class="mainmenu">
<li class="sub-handle">
Item 1
<ul class="submenu">
<li class="sub-li">item 1-1</li>
<li class="sub-li">item 1-2</li>
<li class="sub-li">item 1-1</li>
</ul>
</li>
<li class="sub-handle">
Item 2
<ul class="submenu">
<li class="sub-li">item 2-1</li>
<li class="sub-li">item 2-2</li>
<li class="sub-li">item 2-1</li>
</ul>
</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
I got php to add submenu-i++ and that works just fine. Now I have a Javascript drop down function that looks like this.
var sub_handle = $('.sub-handle');
var sub_nav_ul = $('nav .submenu');
var sub_nav_ul_li = $('nav .mainmenu .sub-li');
$(sub_handle).on('click', function(){
sub_nav_ul.toggleClass('showing-sub');
sub_nav_ul_li.toggleClass('smooth-anchor');
});
sub_nav_ul_li.on('click', function(){
sub_nav_ul.toggleClass('showing-sub');
});
The problem is that when I press any submenu all of my submenus open. I want Javascript somehow to count like php and see if submenu-i++ is pressed only that one expand.
I hope this is clear enough otherwise make a shout and I will try to explain futher.
Thanks in advance!
Get rid of the numbers in the classes, so all the sub-menus have ths same class. Then use DOM navigation methods to find the corresponding menu items to toggle.
var sub_handle = $('.sub-handle');
var sub_nav_ul_li = $('nav .mainmenu .sub-li');
sub_handle.on('click', function() {
$(this).find(".submenu").toggleClass('showing-sub');
$(this).find(".sub-li").toggleClass("smooth-anchor");
});
sub_nav_ul_li.on('click', function() {
$(this).parent().toggleClass('showing-sub');
});
.submenu {
display: none;
}
.submenu.showing-sub {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="mainmenu">
<li class="sub-handle">
Item 1
<ul class="submenu">
<li class="sub-li">item 1-1</li>
<li class="sub-li">item 1-2</li>
<li class="sub-li">item 1-1</li>
</ul>
</li>
<li class="sub-handle">
Item 2
<ul class="submenu">
<li class="sub-li">item 2-1</li>
<li class="sub-li">item 2-2</li>
<li class="sub-li">item 2-1</li>
</ul>
</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
<ul class="mainmenu">
<li class="sub-handle sub-handle-1">
Item 1
<ul class="submenu">
<li class="sub-li">item 1-1</li>
<li class="sub-li">item 1-2</li>
<li class="sub-li">item 1-1</li>
</ul>
</li>
<li class="sub-handle sub-handle-2">
Item 2
<ul class="submenu">
<li class="sub-li">item 2-1</li>
<li class="sub-li">item 2-2</li>
<li class="sub-li">item 2-1</li>
</ul>
</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
var sub_handle = $('.sub-handle');
var sub_nav_ul = $('nav .submenu');
var sub_nav_ul_li = $('.submenu .sub-li');
sub_handle.on('click', function(){
sub_handle.removeClass("showing-sub");
$(this).addClass('showing-sub');
sub_nav_ul_li.toggleClass('smooth-anchor');
});
sub_nav_ul_li.on('click', function(){
$(this).parent().removeClass('showing-sub');
});
Will something like this work:
Note: I had to edit some of the html.
You are toggling the class on all the subitems. What you should do is traverse from the clicked handle to get the corresponding subitems for that. Something like this would suffice:
$(sub_handle).on('click', function(){
$(this).find(sub_nav_ul).toggleClass('showing-sub');
$(this).find(sub_nav_ul_li).toggleClass('smooth-anchor');
});
sub_nav_ul_li.on('click', function(){
$(this).closest(sub_nav_ul).toggleClass('showing-sub');
});
This is in no way the best solution to this, but it should help.
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>