How to get the parent LI index and the sub LI index - javascript

Fiddle: http://jsfiddle.net/fyrx459k/
Script:
$(function() {
$('.ul1 li').click( function(e) {
e.preventDefault();
var liIndex = $(this).index('li');
console.log(liIndex);
);
});
HTML:
<ul class="ul1" id="ul1">
<li>Test</li>
<li>Test2</li>
<li>Test3
<ul class="ul2">
<li>Test3 - 1</li>
<li>Test3 - 2</li>
</ul>
</li>
<li>Test4
<ul class="ul2">
<li>Test4 - 1</li>
<li>Test4 - 2</li>
</ul>
</li>
</ul>
How can I modify the script to have the following:
When a parent LI (Test#) is clicked the console will display the index - for example, clicking on Test4 will output a 3 (fourth item)
When clicking on a sub LI (Test# - #) it will display the parent index and the child index - for example, clicking on Test3 - 1 will output 2.0 (2 being the parent LI and 0 being the first child sub LI)

This is one way of doing it:
$(function () {
$('.ul1 li').click(function (e) {
e.preventDefault();
e.stopPropagation();
var i = $(this).parentsUntil('#ul1').add(this).map(function () {
return this.tagName === 'LI' ? $(this).index() : null;
}).get().join('.');
});
});

A pretty basic solution using $(this).parents("li")
$(function() {
$('.ul1 li').click(function(e) {
e.preventDefault();
e.stopPropagation();
var liIndex = "";
var $parents = $(this).parents("li");
if ($parents.length > 0)
liIndex += ($parents.index() + 1) + ".";
liIndex += ($(this).index() + 1);
alert(liIndex);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="ul1" id="ul1">
<li>Test
</li>
<li>Test2
</li>
<li>Test3
<ul class="ul2">
<li>Test3 - 1
</li>
<li>Test3 - 2
</li>
</ul>
</li>
<li>Test4
<ul class="ul2">
<li>Test4 - 1
</li>
<li>Test4 - 2
</li>
</ul>
</li>
</ul>

I love html5 tags, so check if this solution can be useful. Otherwise you can do the same thing with the ID or whatever you prefer.
function checkIndex(element) {
deepCheck(element, '');
}
function deepCheck(element, index) {
if($(element).data('index') && $(element).is('li')) {
var newIndex = '';
if(index.length === 0) {
newIndex = 'Element indexes: ' + $(element).data('index')
} else {
newIndex = index + ' - ' + $(element).data('index');
}
deepCheck($(element).parent(), newIndex);
} else if($(element).data('root')) {
alert(index);
} else {
deepCheck($(element).parent(), index)
}
}
$(document).ready(function() {
$('a').click(function(e) {
e.preventDefault();
checkIndex(this);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul data-root="true" class="ul1" id="ul1">
<li data-index="1">Test</li>
<li data-index="2">Test2</li>
<li data-index="3">Test3
<ul class="ul2">
<li data-index="1">Test3 - 1</li>
<li data-index="2">Test3 - 2</li>
</ul>
</li>
<li data-index="4">Test4
<ul class="ul2">
<li data-index="1">Test4 - 1</li>
<li data-index="2">Test4 - 2</li>
</ul>
</li>
</ul>

Use parents('li') to check if the clicked li has a li ancestor, .index() to determine the index of a li, .preventDefault() to stop the click from following the link and .stopPropagation to stop event from bubbling. This should do it:
$('#ul1 li').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var arr = [];
arr.push( $(this).index() );
!$(this).parents('li').length ||
arr.push( $(this).parents('li').index() );
console.log( arr.reverse().join('.') );
});
$(function() {
$('#ul1 li').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var arr = [];
arr.push( $(this).index() );
!$(this).parents('li').length ||
arr.push( $(this).parents('li').index() );
console.log( arr.reverse().join('.') );
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="ul1" id="ul1">
<li>Test</li>
<li>Test2</li>
<li>Test3
<ul class="ul2">
<li>Test3 - 1</li>
<li>Test3 - 2</li>
</ul>
</li>
<li>Test4
<ul class="ul2">
<li>Test4 - 1</li>
<li>Test4 - 2</li>
</ul>
</li>
</ul>

I would suggest returning false. It removes the need to use both preventDefault and stopPropagation, and also to store the event in e.
Further, some more structured selections would be helpful here. There are only two cases to handle, where a parent element classed ul2 exits and where not. In the case it does, there needs to be two queries to determine the parents index and the current index with regards to the ul2 element. In the case where it does not, then only the relative ul1 index needs to be determined.
I included some extra elements to show the need for a more targeted approach.
$(function(){
$('.ul1 li').click(function() {
var ul2 = $(this).closest('.ul2'),
location = ul2.length > 0 ?
ul2.parent().index('.ul1 > li')+"."+ul2.find('li').index(this) :
$(this).index('.ul1 > li');
log(location);
return false;
});
});
function log(msg){ $("#result").text(msg); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="ul1" id="ul1">
<li>Test</li>
<li>Test2</li>
<li>Test3
<ul class="ul2">
<li>Test3 - 1</li>
<li>Test3 - 2</li>
</ul>
</li>
<li>Test4
<ul class="ul2">
<li>Test4 - 1</li>
<li>Test4 - 2</li>
</ul>
</li>
</ul>
<div id="result"></div>

Related

How do I add within the li markup?

I'm searching how to add a block inside a li markup.
What I have :
<ul>
<li>
<button onclick="expand()">+</button>
Parent 1
</li>
</ul>
What I want after clicking on the button.
<ul>
<li>
<button onclick="expand()">+</button>
Parent 1
<ul>
<li> Child 1</li>
<li> Child 2</li>
</ul>
</li>
</ul>
My expand() function create the children ul with document.createElement() but I don't know how to insert it in the Parent 1 li.
Either insert or toggle or both
$(function() {
$(".expand1").on("click",function(e) {
if ($(this).siblings("ul").length === 0) { // only need to create once
$(this).parent().append(`<ul class="extra">
<li> Child 1</li>
<li> Child 2</li>
</ul>`)
}
$(this).siblings("ul").toggle(); // show or hide
$(this).text($(this).siblings("ul").is(":visible") ? "-":"+"); // change + / -
})
$(".expand2").on("click",function(e) {
$(this).siblings("ul").toggle()
$(this).text($(this).siblings("ul").is(":visible") ? "-":"+")
})
})
.extra { display:none }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li>
<button type="button" class="expand1">+</button>
Parent 1
</li>
</ul>
<ul>
<li>
<button type="button" class="expand2">+</button>
Parent 1
<ul class="extra">
<li> Child 1</li>
<li> Child 2</li>
</ul>
</li>
</ul>
HTML code
<ul>
<li>
<button onclick="expand(event)">+</button>
Parent 1
</li>
</ul>
Javascript Code
function expand(event) {
var el = event.target.parentElement;
let lastChild = event.target.parentElement.lastChild;
let BeenHere = lastChild.tagName == "UL";
let childLI = document.createElement("li");
let childText = document.createTextNode(`Child ${BeenHere ? lastChild.childElementCount + 1 : 1} `);
childLI.appendChild(childText);
if(BeenHere){
lastChild.appendChild(childLI);
}
else{
let childUL = document.createElement("ul");
el.insertAdjacentElement('beforeend', childUL);
childUL.appendChild(childLI);
}
}

add each class for each parent li in navbar

my html structure is like this
<ul>
<li>items1</li>
<li>items2
<ul>
<li>items2.1</li>
</ul>
</li>
<li>items3
<ul>
<li>items3.1</li>
</ul>
</li>
<li>items4</li>
</ul>
I want to add classes for each parent li like below, using JavaScript is there any possibility to do this?
<ul>
<li class="a">items1</li>
<li class="b">items2
<ul>
<li>items2.1</li>
</ul>
</li>
<li class="c">items3
<ul>
<li>items3.1</li>
</ul>
</li>
<li class="d">items4</li>
</ul>
Using not() filter and addClass(function)
$('li').not('li li').addClass(function(i) {
return String.fromCharCode(i + 97)
})
.a {color:red}
.b {color:orange}
.c {color:green}
.d {color:blue}
li li {color:black}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li>items1</li>
<li>items2
<ul>
<li>items2.1</li>
</ul>
</li>
<li>items3
<ul>
<li>items3.1</li>
</ul>
</li>
<li>items4</li>
</ul>
var letters = "abcd";
$("ul").first().children().each(function(index, el)
{
$(el).addClass(letters[index]);
});
Assuming that there could be n number of list items, we can use: String.fromCharCode(97 + index) to get the letter (up to z).
$("ul").first().children("li").each((index, item) => {
const letterFromIndex = String.fromCharCode(97 + index);
let $item = $(item);
$item.addClass(letterFromIndex);
});
If we want add different names for the li u can use this code also
var names = ["ab", "bc", "cd", "de"];
$("ul").first().children().each(function(index, el)
{
$(el).addClass(names[index]);
});

Determine if a related element is above or below

i'm having an unordered list with several elements and i'm using jquery sortable for moving items around - here's my markup:
<ul>
<li id=1>item 1</li>
<li id=2>item 2</li>
<li id=3>item 3</li>
<li rel=2>insert</li>
<li id=4>item 4</li>
</ul>
the "insert" element is related to the LI having ID 2 - my question:
what's the best practice for determing if the related item is above or below the "insert" element? (in this case: above)
if($(this).prevAll("[id=2]").length > 0)
{
//element is above
}
if($(this).nextAll("[id=2]").length > 0)
{
//element is below
}
You can use nextAll and prevAll functions of jquery for this. Here $(this) refers to insert element.
You can use the index function to get the place of the element and compare it to other elements:
inserted = $('li[rel=2]');
$('ul li').click(function() {
if (inserted.index() > $(this).index()) {
console.log('Clicked element is above');
} else if (inserted.index() < $(this).index()) {
console.log('Clicked element is below');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li id=1>item 1</li>
<li id=2>item 2</li>
<li id=3>item 3</li>
<li rel=2>insert</li>
<li id=4>item 4</li>
</ul>

How can I add class on active li with JavaScript code

<ul class="nav nav-tabs">
<li onclick="this.className='active'">Home</li>
<li onclick="this.className='active'">Menu 1</li>
<li onclick="this.className='active'">Menu 2</li>
<li onclick="this.className='active'">Menu 3</li>
</ul>
How can I add active class on li tag with JavaScript. Here I am try to do it. It is working but not properly. I am doing for tabs here.
As per the comments, I guess you are expecting this:
var a = document.querySelectorAll(".nav li a");
for (var i = 0, length = a.length; i < length; i++) {
a[i].onclick = function() {
var b = document.querySelector(".nav li.active");
if (b) b.classList.remove("active");
this.parentNode.classList.add('active');
};
}
.active {
background-color: #0f9;
}
<ul class="nav nav-tabs">
<li>Home
</li>
<li>Menu 1
</li>
<li>Menu 2
</li>
<li>Menu 3
</li>
</ul>
if you have jquery
<ul class="nav nav-tabs">
<li>Home</li>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
</ul>
<style>
.active {background-color: #0f9;}
</style>
<script type="text/javascript">
$("ul.nav.nav-tabs").on('click', 'li', function(){
$(this).siblings().removeClass('active');
$(this).addClass('active');
});
</script>
Vanilla JavaScript (ES6 where I tested, might work on ES5)
const elm = document.querySelector('ul');
elm.addEventListener('click', (el) => {
const elActive = elm.querySelector('.active');
if (elActive) {
elActive.removeAttribute('class');
}
el.target.setAttribute('class', 'active');
});

For loop closure

I am hoping someone could help me out here in explaining to me what it is with my code that is not outputting what I am expecting. I have played with this and still can't figure out why the inside closure is not outputting anything.
for (var i = 1, len = $('.items').length + 1; i < len; i = i + 1) {
var j = (function(i) {
for (j = 0; j < i.length; j = j + 1) {
$('nav').find('ul').addClass('tier' + j + '-items');
}
})(i);
}
Here is the HTML
<nav>
<ul class="items">
<li class="item">Top level 1</li>
<li class="item">Top level 1
<ul>
<li class="item">level 2</li>
<li class="item">level 2</li>
<li class="item">level 2
<ul>
<li class="item">level 3</li>
<li class="item">level 3</li>
<li class="item">level 3</li>
</ul>
</li>
</ul>
</li>
<li class="item">Top level 1</li>
</ul>
</nav>
EDIT
I updated hoping this would work but no luck:
for (var i = 0, len = $('.items').length; i < len; i = i + 1) {
(function(j) {
for (var j = 0; j < i; j = j + 1) {
$('nav').find('ul').addClass('tier' + j + '-items');
}
})(i);
}
EDIT 2:
Sorry it was not clear what I was trying to do. Spencer was able to understand after a couple times and I appreciate the help. Thank you for your help.
That's because i.length is undefined. i is a number, it dose not have a .length property to it. That will make it so the inner loop never runs.
Edit:
I'm going to assume that what you want is to add the class 'tierN-items' where N is the depth of the list as a tree. So your output would look like so:
<nav>
<ul class="items tier1-items">
<li class="item">Top level 1</li>
<li class="item">Top level 1
<ul class= "tier2-items">
<li class="item">level 2</li>
<li class="item">level 2</li>
<li class="item">level 2
<ul class= "tier3-items">
<li class="item">level 3</li>
<li class="item">level 3</li>
<li class="item">level 3</li>
</ul>
</li>
</ul>
</li>
<li class="item">Top level 1</li>
</ul>
</nav>
For that what you seem to do isn't going to work because you are finding all ul in the nav regardless of the value of j. You are going to need a different approach. My solution would be modifying the selector as a string variable. Basically we find if the first ul exist, then we find if there is a ul > li > ul, then ul > li > ul > li > ul, and so on until there isn't such an element. In code that would look like so:
var level = 1;
var tierSearch = "nav > ul";
while($(tierSearch).length)
{
$(tierSearch).addClass('tier' + level + '-items');
level++;
tierSearch += "> li > ul";
}
Fiddle Example
Is this what you want
$(function(){
$('.items ul').each(function(k,v){
$(this).addClass('tier' + k + '-items');
$(this).prepend('<strong>Tier' + k + '-items</strong>'); // just to highlight
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav class="items">
<ul >
<li class="item">Top level 1</li>
<li class="item">Top level 1
<ul>
<li class="item">level 2</li>
<li class="item">level 2</li>
<li class="item">level 2
<ul>
<li class="item">level 3</li>
<li class="item">level 3</li>
<li class="item">level 3</li>
</ul>
</li>
</ul>
</li>
<li class="item">Top level 1</li>
</ul>
</nav>

Categories