Add classes to two level navigation with javascript - javascript

I have a simple navigation with two levels. The li-elements of the first level shall get class="n11", that of the second class="n12".
This will write class="n11" to every li-element.
var firstNavi = document.getElementsByClassName("nav1-1");
for(var i = 0; i < firstNavi.length; i++) {
var firstLi = firstNavi[i].querySelectorAll("li");
for(var i = 0; i < firstLi.length; i++) {
firstLi[i].classList.add("n11");
}
}
<ul class="nav1-1">
<li >1.1.</li>
<li >1.2
<ul class="nav1-2">
<li>1.2.1</li>
<li>1.2.2</li>
</ul>
</li>
<li>1.3
<ul class="nav1-2">
<li>1.3.1</li>
<li>1.3.2</li>
</ul>
<li>1.4</li>
</ul>
How to achieve that is written class="n12" to the second li-elements and class="n11" only to the first level entries? Thanks for any help.

You can just retrieve the first level <li> elements as well as the second level <li> elements by using the child combinator > on the first level parent <ul> element and the second level <ul> parent element and now you can just loop through each list element and add the class name accordingly like this:
const firstLevel = document.querySelectorAll('.nav1-1 > li');
const secondLevel = document.querySelectorAll('.nav1-2 > li');
for (var i = 0; i < firstLevel.length; i++) {
firstLevel[i].classList.add('n11');
}
for (var i = 0; i < secondLevel.length; i++) {
secondLevel[i].classList.add('n12');
}
<ul class="nav1-1">
<li >1.1.</li>
<li >1.2
<ul class="nav1-2">
<li>1.2.1</li>
<li>1.2.2</li>
</ul>
</li>
<li>1.3
<ul class="nav1-2">
<li>1.3.1</li>
<li>1.3.2</li>
</ul>
<li>1.4</li>
</ul>
If you do not care about IE 11 compatibility or you are using a JavaScript compiler like Babel, you can further shorten and simplify the above JavaScript by using the forEach() method and arrow functions like this:
const firstLevel = document.querySelectorAll('.nav1-1 > li');
const secondLevel = document.querySelectorAll('.nav1-2 > li');
firstLevel.forEach(e => e.classList.add('n11'));
secondLevel.forEach(e => e.classList.add('n12'));
<ul class="nav1-1">
<li >1.1.</li>
<li >1.2
<ul class="nav1-2">
<li>1.2.1</li>
<li>1.2.2</li>
</ul>
</li>
<li>1.3
<ul class="nav1-2">
<li>1.3.1</li>
<li>1.3.2</li>
</ul>
<li>1.4</li>
</ul>

You can use the ">" query selector to find li elements that have a parent of .nav1-1, then add class n11 to each. Rinse and repeat for the li elements that have a parent of .nav1-2.
Here it is in code.
'use strict';
const li1s = document.querySelectorAll('.nav1-1 > li');
for (const li of li1s)
li.classList.add('n11');
const li2s = document.querySelectorAll('.nav1-2 > li');
for (const li of li2s)
li.classList.add('n12');
<ul class="nav1-1">
<li >1.1.</li>
<li >1.2
<ul class="nav1-2">
<li>1.2.1</li>
<li>1.2.2</li>
</ul>
</li>
<li>1.3
<ul class="nav1-2">
<li>1.3.1</li>
<li>1.3.2</li>
</ul>
<li>1.4</li>
</ul>

Related

I cannot get access to all desired elements using querySelectorAll

So I have these three lists which I want to hide.
<ul id="wordsb" class="wordslist" class="list1">
<li class="list-group-item">list1</li>
<li class="list-group-item">dancing</li>
<li class="list-group-item">elephant</li>
</ul>
<ul id="wordsb1" class="wordslist" class="list2">
<li class="list-group-item">list2</li>
<li class="list-group-item">man</li>
<li class="list-group-item">dog</li>
</ul>
<ul id="wordsb2" class="wordslist" class="list3">
<li class="list-group-item">list3</li>
<li class="list-group-item">plane</li>
<li class="list-group-item">truck</li>
</ul>
I am using the queryselectorall in order to access the ids loop through them, applying a style to each of them:
var gameb = document.querySelectorAll("#wordsb", "#wordsb1", "#wordsb2");
for (var i = 0; i < gameb.length; i++) {
gameb[i].style.display = "none";
}
Unfortunately, only the first list (wordsb) is hidden as desired, and the remaining two elements do not seem to be impacted. When I console.log(gameb) I found that the node list only includes wordsb, and not the other two elements. enter code here
querySelectorAll only takes one parameter:
var gameb = document.querySelectorAll("#wordsb, #wordsb1, #wordsb2");
for (var i = 0; i < gameb.length; i++) {
gameb[i].style.display = "none";
}
<ul id="wordsb" class="wordslist" class="list1">
<li class="list-group-item">list1</li>
<li class="list-group-item">dancing</li>
<li class="list-group-item">elephant</li>
</ul>
<ul id="wordsb1" class="wordslist" class="list2">
<li class="list-group-item">list2</li>
<li class="list-group-item">man</li>
<li class="list-group-item">dog</li>
</ul>
<ul id="wordsb2" class="wordslist" class="list3">
<li class="list-group-item">list3</li>
<li class="list-group-item">plane</li>
<li class="list-group-item">truck</li>
</ul>
This was just a silly mistake, document.queryselectorall only takes one parameter, and I accidentally put quotation marks around each of my elements. Thereby, making multiple parameters. Thus the node list didnt reflect all three of my elements.

Is there any way to hide bullets of <li> element which is having empty div

I am dynamically generating li elements.
some of li element is having empty div.
like
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
i don't want to show 3rd li element bullet on ui, but want to show other two li bullets.
is there any way to fix this?
Thanks
Amit
You've said you're creating the elements. If so, then just leave off that li or apply a class to it when the div is empty that sets list-style: none.
If you're doing this after the fact, then without changing the structure, adding classes, etc., I can't think of a CSS solution that works based on the div being empty (rather than it always being the third item, which I'm sure isn't the question).
The JavaScript solution is simple, but I don't like using JavaScript for this.
const items = document.querySelectorAll(".mybuttons li");
for (const item of items) {
if (!item.textContent.trim()) {
item.style.listStyle = "none";
}
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
Or with ES5 only:
var items = document.querySelectorAll(".mybuttons li");
for (var n = 0; n < items.length; ++n) {
if (!items[n].textContent.trim()) {
items[n].style.listStyle = "none";
}
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
I shouldn't have used style.listStyle above, I should have done what I said in the first paragraph above and used a class:
const items = document.querySelectorAll(".mybuttons li");
for (const item of items) {
if (!item.textContent.trim()) {
item.classList.add("empty");
}
}
.empty {
list-style: none;
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
Use querySelectorAll to select all the li elements and inside a forEach loop if the text in a div is empty remove the list style from it.
var a=document.querySelectorAll('li');
a.forEach((e)=>{
if(e.childNodes[0].textContent=="")
e.style.listStyle='none';
})
<ul class="mybuttons" >
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
<li class="mybutton"><div>erere</div></li>
<li class="mybutton"><div></div></li>
<li class="mybutton"><div>rere</div></li>
</ul>
With JS:
{
const init = () => {
const btns = document.querySelectorAll(`.mybutton`);
const allBtns = Array.from(btns);
btns.forEach(btn => {
if(btn.textContent === ''){
btn.style.listStyle = 'none';
}
}
}
init();
}
This might work
just check the text in the li element and eventually remove it:
let items = document.getElementsByTagName("li");
for (var i = 0; i < items.length; ++i) {
let text = items[i].innerText;
if(!text) items[i].remove();
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
Yes. Use the list-style property and assign none to it.
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton" style="list-style: none;"><div>HHH</div></li>
</ul>

Next child LI that is wrapped in a different UL

I have the following list:
<ul>
<li class="item">One</li>
<li class="item">Two</li>
<li class="item">Three
<ul>
<li class="item">Something Original</li>
<li class="item selected">Something</li>
</ul>
</li>
<li>Four
<ul>
<li class="item">I want this selected next</li>
<li class="item">Good</li>
</ul>
</li>
</ul>
Using jQuery, how do I find the next li with the class="item" since it is wrapped in a different container. Obviously I cannot do $(".selected").next(".item") so how else can I do it?
JSFiddle: http://jsfiddle.net/q3f6v7zz/
Since the li elements are nested and you know that you want the next appearing li with a particular class, you can use .index() and do something like this
var $li = $('.item'); // <--- get the list of all lis with class .item
var index = $li.index($('.selected')); // <--- find the index of the one with .selected amongst all the lis
console.log($li.eq(index+1).html()); // <--- index+1 because you need the next appearing li after selected
If you want to move the selected class on keydown something like this should do
var $li = $('.item');
$(document).on('keydown', function(e) {
if (e.keyCode == 40) {
var index = $li.index($('.selected'));
$li.eq(index).removeClass('selected');
index = (index+1) % $li.length; // <--- to rotate the values from 0 to count of li.item elements
$li.eq(index).addClass('selected');
}
});
var $li = $('.item');
$(document).on('keydown', function(e) {
if (e.keyCode == 40) {
var index = $li.index($('.selected'));
$li.eq(index).removeClass('selected');
index = (index+1) % $li.length;
$li.eq(index).addClass('selected');
}
});
.selected {
background: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul>
<li class="item">One</li>
<li class="item">Two</li>
<li>Three
<ul>
<li class="item">Something</li>
<li class="item selected">Something Else</li>
</ul>
</li>
<li>Four
<ul>
<li class="item">I want this selected next</li>
<li class="item">Good</li>
</ul>
</li>
</ul>
You can get the index of the selected element within all lis, and then increment that index to get the next one.
$("ul").on("click", "li.item.selected", function() {
var all_li = $("li.item");
var selected_index = all_li.index(this);
var next_li = all_li.eq((selected_index + 1) % all_li.length);
$(this).removeClass("selected");
next_li.addClass("selected");
});
.item.selected {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="item">One</li>
<li class="item">Two</li>
<li class="item">Three
<ul>
<li class="item selected">Something</li>
</ul>
</li>
<li>Four
<ul>
<li class="item">I want this selected next</li>
<li class="item">Good</li>
</ul>
</li>
</ul>
I used the modulus so it will wrap around at the end.
Not sure what you are exactly looking for but you can use $(Element").parent().parent().find("li");
So in other words .parent() may be what you are looking for there is also .sibling() to find or you may want $('li').closest('ul').find('li')
which will go up the tree to find the nearest ul to the one you are looking for
https://api.jquery.com/closest/
You may also use:
Vanilla JS to do something similar to what was discussed by others with $index if it makes more sense to you:
Again this isn't as efficient but that is basically what JQuery is doing:
var myLis = document.getElementsByTagName('li');
var wantedIndex;
for(var i = 0;i<myLis.length; i++){
if(myLis[i].className === "active"){
wantedIndex = i+1; //gets the li which is next when selecting all lis
}
}

How to Get and filter Data attribute of all parents in a nested list, till a target (div) of html element?

I've a nested (UL->LI->UL-LI..) list. On any clicked node, I'm using ParentsUntil() to find all the parents till a certain ancestor element.
In each nested element's data attribute (data-weight:), there is number that represent weight.
I want to sum/aggregate total weight till the parent. These numbers (Areas) are in the data-area field of each item.
<ul class="level-1 yes" data-weight="12" data-weight-total="0">
<li class="item-i" data-weight="22" >I</li>
<li class="item-ii" data-weight="4" data-weight-total="0">II
<ul class="level-2 yes" data-weight="12">
<li class="item-a" data-weight="1.4">A</li>
<li class="item-b" data-weight="128" data-weight-total="0">B
<ul class="level-3" data-weight="63" data-weight-total="0">
<li class="item-1" data-weight="54">1</li>
<li class="item-2" data-weight="23">2</li>
<li class="item-3" data-weight="107">3</li>
</ul>
</li>
<li class="item-c" data-weight="231">C</li>
</ul>
</li>
<li class="item-iii">III</li>
</ul>
$( "li.item-2" )
.parentsUntil( $( "ul.level-1" ), ".yes" );
In the list above,
How can I get the an array/list of them items from the clicked item
to the parent item with their data-weight [key,value]? for e.g. $VarArrayCargoVessel
And as I traverse up, How can I sum/total weights (data-weight-total) of each/nested list and populate\fill-in the data-weight-total? I have zero's right now, because I dont know how to insert/write into a array value
$("li").click(function(event){
event.stopPropagation();
var list = $(this).closest("ul");
var children = list.children();
var values = [];
for(var i = 0; i < children.length; i++){
values.push(parseInt((children[i].getAttribute("data-weight") !== null) ? children[i].getAttribute("data-weight") : "0"));
}
var sum = 0;
for(var j = 0; j < values.length; j++){
sum += values[j];
}
$("#summed").html(sum);
});
li{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="level-1 yes" data-weight="12" data-weight-total="0">
<li class="item-i" data-weight="22" >I</li>
<li class="item-ii" data-weight="4" data-weight-total="0">II
<ul class="level-2 yes" data-weight="12">
<li class="item-a" data-weight="1.4">A</li>
<li class="item-b" data-weight="128" data-weight-total="0">B
<ul class="level-3" data-weight="63" data-weight-total="0">
<li class="item-1" data-weight="54">1</li>
<li class="item-2" data-weight="23">2</li>
<li class="item-3" data-weight="107">3</li>
</ul>
</li>
<li class="item-c" data-weight="231">C</li>
</ul>
</li>
<li class="item-iii">III</li>
</ul>
<div id="summed"></div>
Okay, let's get through this step by step. The first thing we do is creating a click event for each "li" item. If a li item is clicked, we execute our function ("function(event)").
The first line stops the event from propagating up. jQuery would go up to the very first "ul" item otherwise and execute the code for each "ul" element. We would receive the first list all the time.
http://api.jquery.com/event.stopPropagation/
We then get the closest "ul" element and store this in the variable "list". We don't want the list itself, that's why we store its children ("li" elements) in the variable "children".
We loop then through each children and store the "data-weight" attribute (method "getAttribute") in the array "values". Note that we only store the attribute if it's actually exists. If not, we just store "0".
https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute
We then create a variable "sum" which stores our summed up attributes. To do so we loop through the array and add the value (+= values[j]) to the sum variable.
The last step writes the sum variable in the html of the div with the id #summed (check the html section).
Further reading:
https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute
http://api.jquery.com/closest/
http://api.jquery.com/click/
http://api.jquery.com/event.stopPropagation/
http://api.jquery.com/children/
http://www.sitepoint.com/shorthand-javascript-techniques/
http://api.jquery.com/html/
Version 2:
$("li").click(function(event){
event.stopPropagation();
var list = $(this).find("ul").first();
console.log(list);
var children = list.children();
var values = [];
for(var i = 0; i < children.length; i++){
values.push(parseInt((children[i].getAttribute("data-weight") !== null) ? children[i].getAttribute("data-weight") : "0"));
}
var sum = 0;
for(var j = 0; j < values.length; j++){
sum += values[j];
}
$("#summed").html(sum);
});
li{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="level-1 yes" data-weight="12" data-weight-total="0">
<li class="item-i" data-weight="22" >I</li>
<li class="item-ii" data-weight="4" data-weight-total="0">II
<ul class="level-2 yes" data-weight="12">
<li class="item-a" data-weight="1.4">A</li>
<li class="item-b" data-weight="128" data-weight-total="0">B
<ul class="level-3" data-weight="63" data-weight-total="0">
<li class="item-1" data-weight="54">1</li>
<li class="item-2" data-weight="23">2</li>
<li class="item-3" data-weight="107">3</li>
</ul>
</li>
<li class="item-c" data-weight="231">C</li>
</ul>
</li>
<li class="item-iii">III</li>
</ul>
<div id="summed"></div>
Note the third line in the answer. I've changed it to:
var list = $(this).find("ul").first();
Version 3:
$("li").click(function(event){
event.stopPropagation();
var children = $(this).find("ul").first().children();
if(children.length === 0){
children = $(this);
}
console.log(children);
var values = [];
for(var i = 0; i < children.length; i++){
values.push(parseFloat((children[i].getAttribute("data-weight") !== null) ? children[i].getAttribute("data-weight") : "0"));
}
var sum = 0;
for(var j = 0; j < values.length; j++){
sum += values[j];
}
$("#summed").html(sum);
});
li{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="level-1 yes" data-weight="12" data-weight-total="0">
<li class="item-i" data-weight="22" >I</li>
<li class="item-ii" data-weight="4" data-weight-total="0">II
<ul class="level-2 yes" data-weight="12">
<li class="item-a" data-weight="1.4">A</li>
<li class="item-b" data-weight="128" data-weight-total="0">B
<ul class="level-3" data-weight="63" data-weight-total="0">
<li class="item-1" data-weight="54">1</li>
<li class="item-2" data-weight="23">2</li>
<li class="item-3" data-weight="107">3</li>
</ul>
</li>
<li class="item-c" data-weight="231">C</li>
</ul>
</li>
<li class="item-iii">III</li>
</ul>
<div id="summed"></div>
Look into the following jQuery methods, they are all you need:
each (link),
attr (link),
find (link) / children (link)
For example, at page load, you want to find each node that has a data-weight-total attribute, go through all its (immediate or all) children that have a data-weight attribute, get the sum of them and assign it to the node's data-weight-total property. If you want only immediate children, you would use .children(), or for all you would use .find():
$( document ).ready(function(){ // DOM ready
$('[data-weight-total]').each(function(){
var total = 0;
$(this).find('[data-weight]').each(function(){
total = total + parseInt($(this).attr('data-weight'));
});
$(this).attr('data-weight-total',total);
});
})
This would calculate and assign data-weight-total values to all nodes that have such an attribute, as soon as the document is loaded. Let me know if you need explanations for any of what's going on in the function.

Add count classes for nested list elements with jQuery

I have this HTML:
<ul class="parent">
<ul>
<li>
<ul></ul>
</li>
<li>
<ul>
<li>
<ul></ul>
</li>
</ul>
</li>
</ul>
</ul>
I need to add count classes for all nested lists in this markup, to reach this:
<ul class="parent">
<ul class="level-1">
<li>
<ul class="level-2"></ul>
</li>
<li>
<ul class="level-2">
<li>
<ul class="level-3"></ul>
</li>
</ul>
</li>
</ul>
<ul class="level-1">
<li>
<ul class="level-2"></ul>
</li>
<li>
<ul class="level-2">
<li>
<ul class="level-3"></ul>
</li>
</ul>
</li>
</ul>
</ul>
So i do this:
var parent_ul = $('.parent');
if (parent_ul.doesExist()){
var parent_ul_lists = parent_ul.find('ul');
parent_ul_lists.each(function(){
var i;
for (i = 0; i < parent_ul_lists.length; i++) {
$(this).eq(i).addClass('level-' + i);
}
})
}
But in output i have class test level-1 for all of parent list childrens. Can anybody help?
Try this
$('.parent ul').addClass(function(){
return "level-"+$(this).parents('ul').length;
});
DEMO
Try this
var parent = $('.parent').children();
next = parent;
var i = 0;
while (next.length) {
parent = next;
parent.addClass("Level_" + i);
i++;
next = next.children();
}
Demo
You need some good 'ole recursion!
Check out this JSFiddle UPDATE: corrected jsFiddle link
Here is the code:
function labelChildUL(element, count) {
element.children().each(function (index, value) {
var $me = $(value);
if ($me.is("ul")) {
$me.addClass("level-" + (count + 1));
labelChildUL($me, count + 1);
}
else
{
labelChildUL($me, count);
}
});
}
$(document).ready(function () {
var $parent = $('#parent');
labelChildUL($parent, 0);
});
I also updated your html to use id instead of class for "parent":
<ul id='parent'>
...
You can use this;
var parent_ul = $('.parent');
var parent_ul_lists = parent_ul.find('ul');
parent_ul_lists.each(function(){
$(this).addClass('level-'+ ($(this).parents().length -1)/2);
});
See working demo here: http://jsfiddle.net/huseyinbabal/qmGcC/
Note: Inspect element in output to see class assigned

Categories