Change background list item on click in dynamic nested List - javascript

I have nested lists draw in run time dynamic from database in away like this :
<div class="list"><ul>
<li>
listA
<ul>
<li>Alist1</li>
<li>Alist2</li>
<li>Alist3</li>
</ul>
</li>
<li>
listB
<ul>
<li>BList1</li>
<li>BList2</li>
<li>BList3</li>
</ul>
</li>
i want to change the back ground of list item when clicked but it change style of the all nested list by the following method :
var $li = $('#list li').click(function () {
$li.removeClass('selected');
$(this).addClass('selected');
});
using this style :
li.selected {
background-color: aqua;}
I know that i should use the direct descendant operator (>) to force change to parent only but my problem that list is drawn dynamically and I can't limit its levels and nested list.
is there away to always force only clicked item to be changed only ?

1- You can't use #list while your list have a class list not id list with classes you need to use dot not #
2- You need to use > like $('.list > ul > li')
$(document).ready(function(){
$('ul > li').on('click' , function(e){
e.stopPropagation();
//$('li > ul').hide();
$(this).find(' > ul').slideDown();
$(this).parent('ul').find('li').removeClass('selected');
$(this).addClass('selected');
});
});
ul{
background : #fff;
}
li > ul{
display : none;
}
li.selected{
background : red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list">
<ul>
<li>
listA
<ul>
<li>Alist1
<ul>
<li>Alist1-1</li>
<li>Alist1-2</li>
<li>Alist1-3</li>
</ul>
</li>
<li>Alist2</li>
<li>Alist3</li>
</ul>
</li>
<li>
listB
<ul>
<li>BList1</li>
<li>BList2</li>
<li>BList3</li>
</ul>
</li>
</ul>
</div>
-- It'll be better to work with <a> see the next example
$(document).ready(function(){
$('a').on('click' , function(e){
e.preventDefault();
var GetLi = $(this).closest('li');
var GetBigUL = $(this).closest('ul');
var GetNextUL = $(this).next('ul');
GetBigUL.find('a').next('ul').not(GetNextUL).slideUp();
GetNextUL.slideDown();
GetBigUL.find('li').removeClass('selected');
GetLi.addClass('selected');
});
});
ul{
background : #fff;
}
li > ul{
display : none;
}
li.selected{
background : red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list">
<ul>
<li>
<a>listA</a>
<ul>
<li>
<a>Alist1</a>
<ul>
<li><a>Alist1-1</a></li>
<li><a>Alist1-2</a></li>
<li><a>Alist1-3</a></li>
</ul>
</li>
<li><a>Alist2</a></li>
<li><a>Alist3</a></li>
</ul>
</li>
<li>
<a>listB</a>
<ul>
<li><a>BList1</a></li>
<li><a>BList2</a></li>
<li><a>BList3</a></li>
</ul>
</li>
</ul>
</div>

Related

Child element effecting open/close toggle

I have created code for an open/close toggle filter by targeting the first li of the filter. For some reason, the children li's that are nested inside this are also closing the toggle when selected to filter the feed.
Please would someone be able to advise how to only target the parent li without the children li affecting the toggle from opening and closing?
Here is a link to the page with the filter. Please test the toggle and select a filter to see the issue that I am currently facing.
https://snapstaging.co.uk/coolkitnew/vans/
It may be worth noting that I don't want to target by class because I have multiple of these elements all with different classes.
let filterBlock = document.querySelectorAll('.searchandfilter li ul')
let filterLi = document.querySelectorAll('.searchandfilter ul li')
let filterLiOpen = [];
filterLi.forEach((tag, index) => {
tag.addEventListener('click', () => {
filterLi[index].classList.toggle('active')
if (!filterLiOpen[index]) {
filterLiOpen[index] = true;
} else if (filterLiOpen[index]) {
filterLiOpen[index] = false;
}
console.log(filterLiOpen);
})
})
.active { color: #C00; }
<div class="searchandfilter">
<ul>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0"><label class="sf-label-checkbox">Large Van<span class="sf-count">(20)</span></label></li>
</ul>
</li>
</ul>
</div>
The issue is because you select all the li elements in the DOM. To target only those which are children of the top level ul, not its descendants, use the child operator in your selector: >.
Also note that you can simplify the logic which toggles the boolean value you store in your array. Here's a working example:
let filterLi = document.querySelectorAll('.searchandfilter > ul > li')
let filterLiOpen = [];
filterLi.forEach((tag, index) => {
tag.addEventListener('click', e => {
e.currentTarget.classList.toggle('active');
filterLiOpen[index] = !filterLiOpen[index];
console.log(filterLiOpen);
})
})
.active { color: #C00; }
<div class="searchandfilter">
<ul>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0">
<label class="sf-label-checkbox">
Large Van
<span class="sf-count">(20)</span>
</label>
</li>
</ul>
</li>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0">
<label class="sf-label-checkbox">
Medium Van
<span class="sf-count">(10)</span>
</label>
</li>
</ul>
</li>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0">
<label class="sf-label-checkbox">
Small Van
<span class="sf-count">(5)</span>
</label>
</li>
</ul>
</li>
</ul>
</div>

How to target all nested list items beyond a specific level using jquery?

I have a list
<ul>
<li>
<ul>
<li>
<ul>
<li> <-- start targeting list items here including children -->
<ul>
<li> <-- included -->
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
I want to target all list items starting with the second or third nested list including all children thereafter using jquery
var selector = $('ul > li > ul > li > ul li');
The third li has three ul parents. You need to filter li's using .filter() and in it function check length of ul's parent of every li to filtering nested li's.
$("ul:first li").filter(function(){
return $(this).parents("ul").length > 2 ? true : false;
}).css("color", "red");
$("ul:first li").filter(function(){
return $(this).parents("ul").length > 2 ? true : false;
}).css("color", "red");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>First
<ul>
<li>Second
<ul>
<li>Third
<ul>
<li>Fourth</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

how to highlight currently clicked h3 and li elements jquery

I have a accordian-style nav here. So every time an H3 is clicked, the list items drop down.
1.I want to color the H3 green if it's clicked, so users know what's currently clicked.
2.I also want to color whatever item/dog is clicked ( the < a > elements).
So the currently clicked Heading and subheading should both be highlighted.
<div id="accordian">
<ul class="sidebar-nav" id=menu>
<li class="active">
<h3>ITEMS</h3>
<ul>
<li> item1</li>
<li> item2</li>
<li> item3</li>
</ul>
</li>
<li>
<h3>dogs</h3>
<ul>
<li> dog1</li>
<li> dog2</li>
<li> dog3</li>
</ul>
</li>
</ul>
</div>
THis is what I tried so far for 1, it doesn't work. Also, not sure how to do 2.
.sidebar-nav li.active{
color:green;
}
$('ul.sidebar-nav li h3').on('click', function(){
$('ul.sidebar-nav li').removeClass('active');
$(this).addClass('active');
});
You can do something like this:
$('ul.sidebar-nav li h3').on('click', function(){
$("ul.sidebar-nav li ul li, ul.sidebar-nav li h3").removeClass("active");
$(this).addClass("active");
});
$('ul.sidebar-nav li ul li').on('click', function(){
if($(this).parents("li").find("h3").hasClass("active")){
$(this).addClass("active");
}
});
.active{
color:green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="accordian">
<ul class="sidebar-nav" id=menu>
<li>
<h3>ITEMS</h3>
<ul>
<li> item1</li>
<li> item2</li>
<li> item3</li>
</ul>
</li>
<li>
<h3>dogs</h3>
<ul>
<li> dog1</li>
<li> dog2</li>
<li> dog3</li>
</ul>
</li>
</ul>
</div>
The idea is to use the clicked element to find the h3 and li that need to be highlighted. Also, in CSS, make the selector more generic so it will apply to both the h3 and li elements.
You are not targeting the H3 tag with your active class. In your code, $(this) is referring the the li. You will need to target the parent H3 of clicked li that has the listener.
EDIT
In the code example see my comments to the lines I added. I target all h3 tags and remove the active class to clear everything out, then I target just the parent h3 tag of the clicked li.
Also update your style to have a less specific class for any element with class active:
.active {
color:green;
}
$('ul.sidebar-nav li').on('click', function(){
$('ul.sidebar-nav li').removeClass('active');
// removes all active classes on h3 tags
$('#accordian').find('h3').removeClass('active');
$(this).addClass('active');
// add active to parent h3 as well
$(this).parents('h3').addClass('active');
});
// If you want them to to highlight independently you need 2 listeners
$('#accordion').find('.sidebar-nav h3').on('click', function() {
// removes all active classes on h3 tags
$('#accordian').find('h3').removeClass('active');
// add active to parent h3 as well
$(this).addClass('active');
});
$('#accordion').find('.sidebar-nav a').on('click', function() {
$('#accordion').find('.sidebar-nav a').removeClass('active');
$(this).addClass('active');
});

how to make this slide menu work

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

jQuery sliding menu function

hi i have a unordered list menu, trying to make the sub-items slide down and up on clicking at the main items, i wrote a jQuery code that works but when click at open menu it close it and open again, but i was hoping it will just close it.
html
<div class="menuNav">
<ul>
<li><span>item_1</span>
<ul>
<li>sub-item_1-1</li>
<li>sub-item_1-2</li>
<li>sub-item_1-3</li>
<li>sub-item_1-4</li>
</ul>
</li>
<li><span>item_2</span>
<ul>
<li>sub-item_2-1</li>
<li>sub-item_2-2</li>
<li>sub-item_2-3</li>
<li>sub-item_2-4</li>
</ul>
</li>
<li><span>item_3</span>
<ul>
<li>sub-item_3-1</li>
<li>sub-item_3-2</li>
<li>sub-item_3-3</li>
<li>sub-item_3-4</li>
</ul>
</li>
</ul>
</div>
jQuery
$(document).ready(function() {
$('.menuNav ul li').click(function(){
$(this).parent().find('ul').slideUp("fast");
$(this).parent().find("li").removeClass('menuactive');
$(this).find('ul').slideDown("slow");
$(this).addClass('menuactive');
});
$('.menuNav ul .menuactive').click(function(){
$(this).parent().find('ul').slideUp("fast");
});
});
If the li has a class menuactive, call the slideUp() function and remove the class menuactive else call the slideUp() function on all uls, remove the class menuactive from all lis, call slideDown() on the one that was clicked and add the class menuactive to the one that was clicked.
$(document).ready(function() {
$('ul > li > ul').hide();
$('.menuNav > ul > li').click(function() {
if ($(this).hasClass('menuactive')) {
$(this).find('ul').slideUp('fast');
$(this).removeClass('menuactive');
} else {
$(this).siblings().find('ul').slideUp('fast');
$(this).siblings().removeClass('menuactive');
$(this).find('ul').slideDown('fast');
$(this).addClass('menuactive');
}
});
});
.menuactive {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="menuNav">
<ul>
<li><span>item_1</span>
<ul>
<li>sub-item_1-1</li>
<li>sub-item_1-2</li>
<li>sub-item_1-3</li>
<li>sub-item_1-4</li>
</ul>
</li>
<li><span>item_2</span>
<ul>
<li>sub-item_2-1</li>
<li>sub-item_2-2</li>
<li>sub-item_2-3</li>
<li>sub-item_2-4</li>
</ul>
</li>
<li><span>item_3</span>
<ul>
<li>sub-item_3-1</li>
<li>sub-item_3-2</li>
<li>sub-item_3-3</li>
<li>sub-item_3-4</li>
</ul>
</li>
</ul>
</div>

Categories