Toggling individual elements of the same class in dropdown menu - javascript

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>

Related

Adding class to all and one indexed items in list

I have managed to get the click function to display the specific items to add class on click.
The behavior im looking for.
Index both list item types because they will have the same amount of items.
On primary-list-item click fire 3 and 4.
Add class hide to all items but the indexed primary-list-item clicked.
Add class to the specifically indexed controls-list-item clicked.
The code submitted doesnt quite index the clicks properly.
Examples and ideas welcome & thankyou in advance.
$('.primary-list-item').click(function(e) {
e.preventDefault();
$('.primary-list-item').removeClass('hide');
$('.controls-list-item').removeClass('show');
$(this).removeClass("hide");
$(this).removeClass("show");
var index = $(this).index();
$('.links-block-list').each(function() {
$('.primary-list-item', this).not(':eq(' + index+')').addClass('hide');
$('.controls-list-item', this).eq(index).addClass('show');
})
});
.hide {
display: none !important;
}
.show {
display: flex;
}
.primary-list-item {
display: flex;
}
.controls-list-item {
display: none;
}
a {
text-decoration: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="links-block-list">
<li class="primary-list-item">List Item 1</li>
<li class="controls-list-item hide">List Item 1</li>
<li class="primary-list-item">List Item 2</li>
<li class="controls-list-item hide">List Item 2</li>
<li class="primary-list-item">List Item 3</li>
<li class="controls-list-item hide">List Item 3</li>
<li class="primary-list-item">List Item 4</li>
<li class="controls-list-item hide">List Item 4</li>
<li class="primary-list-item">List Item 5</li>
<li class="controls-list-item hide">List Item 5</li>
</ul>
Honestly, having a class hide and show gets confusing. "Show" should just be the absence of hide (or vice versa as you prefer).
Getting rid of that complexity makes the code much easier. You were of course missing the index variable which is just $(this).index() for the clicked item.
$('.primary-list-item').click(function(e) {
e.preventDefault();
var index = $('.links-block-list > .primary-list-item').index(this); // index of clicked item
$('.primary-list-item').not(this).addClass("hide"); // hide all other primary items
$('.controls-list-item').eq(index).removeClass('hide'); // unhide the same index from control list as clicked primary
});
$('#reset').click(function(){
$('.primary-list-item').removeClass("hide");
$('.controls-list-item').addClass("hide");
});
.hide {
display: none !important;
}
.primary-list-item {
display: flex;
}
a {
text-decoration: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="links-block-list">
<ul class="links-block-list">
<li class="primary-list-item">List Item 1</li>
<li class="controls-list-item hide">List Item 1</li>
<li class="primary-list-item">List Item 2</li>
<li class="controls-list-item hide">List Item 2</li>
<li class="primary-list-item">List Item 3</li>
<li class="controls-list-item hide">List Item 3</li>
<li class="primary-list-item">List Item 4</li>
<li class="controls-list-item hide">List Item 4</li>
<li class="primary-list-item">List Item 5</li>
<li class="controls-list-item hide">List Item 5</li>
</ul>
</ul>
<button id="reset">reset</button>

jquery onclick parent remove child action

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>

how to show hide element previce element and show one nested ul

I create this nested list to show and hide items but I want ask how can I show one list and hide other for example if user click on second subject will hide all open items
HTML
<ul>
<li class="subject">List item 1 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
<li class="subject">List item 2 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
<li class="subject">List item 3 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
</ul>
javascript
$(function(){
// $("ul li").children().slideDown("slow");
$(".subject").click(function(){
$(this).find("#item").slideToggle("slow");
});
})
CSS
#item
{
display: none;
}
$(".subject").click(function(){
$(this).find("#item").slideToggle("slow");
$(this).siblings().children('ul').slideUp();
});
DEMO
or
$(".subject").click(function () {
$(this).find("#item").slideToggle("slow")
.end().siblings().children('ul').slideUp();
});
DEMO
I corrected your markup. Your IDs should always be unique; otherwise use classes.
$(function(){
$(".subject").click(function(){
$('ul.item').not( $(this).find('ul.item') ).slideUp("slow");
$(this).find('ul.item').slideDown('slow');
});
});
WORKING JSFIDDLE DEMO

Jquery code for multi-level dropdown navigation menu

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;
}

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>

Categories

Resources