Let's say I have this markup:
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
And I have this jQuery:
$("#wizard li").click(function () {
// alert index of li relative to ul parent
});
How can I get the index of the child li relative to it's parent, when clicking that li?
For example, when you click "Step 1", an alert with "0" should pop up.
$("#wizard li").click(function () {
console.log( $(this).index() );
});
However rather than attaching one click handler for each list item it is better (performance wise) to use delegate which would look like this:
$("#wizard").delegate('li', 'click', function () {
console.log( $(this).index() );
});
In jQuery 1.7+, you should use on. The below example binds the event to the #wizard element, working like a delegate event:
$("#wizard").on("click", "li", function() {
console.log( $(this).index() );
});
something like:
$("ul#wizard li").click(function () {
var index = $("ul#wizard li").index(this);
alert("index is: " + index)
});
There's no need to require a big library like jQuery to accomplish this, if you don't want to. To achieve this with built-in DOM manipulation, get a collection of the li siblings in an array, and on click, check the indexOf the clicked element in that array.
const lis = [...document.querySelectorAll('#wizard > li')];
lis.forEach((li) => {
li.addEventListener('click', () => {
const index = lis.indexOf(li);
console.log(index);
});
});
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
Or, with event delegation:
const lis = [...document.querySelectorAll('#wizard li')];
document.querySelector('#wizard').addEventListener('click', ({ target }) => {
// Make sure the clicked element is a <li> which is a child of wizard:
if (!target.matches('#wizard > li')) return;
const index = lis.indexOf(target);
console.log(index);
});
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
Or, if the child elements may change dynamically (like with a todo list), then you'll have to construct the array of lis on every click, rather than beforehand:
const wizard = document.querySelector('#wizard');
wizard.addEventListener('click', ({ target }) => {
// Make sure the clicked element is a <li>
if (!target.matches('li')) return;
const lis = [...wizard.children];
const index = lis.indexOf(target);
console.log(index);
});
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
Take a look at this example.
$("#wizard li").click(function () {
alert($(this).index()); // alert index of li relative to ul parent
});
Delegate and Live are easy to use but if you won't have any more li:s added dynamically you could use event delagation with normal bind/click as well. There should be some performance gain using this method since the DOM won't have to be monitored for new matching elements. Haven't got any actual numbers but it makes sense :)
$("#wizard").click(function (e) {
var source = $(e.target);
if(source.is("li")){
// alert index of li relative to ul parent
alert(source.index());
}
});
You could test it at jsFiddle: http://jsfiddle.net/jimmysv/4Sfdh/1/
Yet another way
$("#wizard li").click(function ()
{
$($(this),'#wizard"').index();
});
Demo
https://jsfiddle.net/m9xge3f5/
Related
I am trying to toggle a class on and off whenever i click on a list item. I have tried several things like using classList.toggle and wrapping the list elements in a anchor tag and then trying to add a class to the list items through that but the more things i try the more confused i get.
Here below is the snippet of my code.
var li = document.querySelectorAll("li");
li.addEventListener('click', (e) => {
addDoneClass();//
});
function addDoneClass() {
li.className = "done"
}
.done {
text-decoration: line-through;
}
<ul>
<li random="23">Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
You might look at my JavaScript code an wonder why i done it that way that is because i was very confused at this point and that was the last thing that i tried.
const elements = document.querySelectorAll("li");
elements.forEach((element) => {
// First option
element.addEventListener('click', function() {
this.classList.toggle('active');
});
// Second option
//element.addEventListener('click', function() { customHandle(this); });
// Third option
//element.addEventListener('click', (e) => customHandle(e.target.closest('li')));
});
// For the second and third options
//function customHandle(element) {
// element.classList.toggle('active');
//}
li.active {
text-decoration: line-through;
}
<ul>
<li>Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
In my opinion, it's better if you add one event listener to the ul element, than have multiple for the li elements.
You can use classList.add("class") and classList.remove("class") functions, more information. You will also need to check if the current item already has the class, for that use the classList.contains("class") function.
Here's an example (updated with ternary operator):
const ul = document.getElementById("list");
ul.addEventListener('click', (e) => {
const li = e.target;
li.classList.contains('done') ? removeDoneClass(li) : addDoneClass(li)
});
function addDoneClass(li) {
li.classList.add("done");
}
function removeDoneClass(li) {
li.classList.remove("done");
}
.done {
text-decoration: line-through;
}
<ul id="list">
<li random="23">Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
Tip: use the classList.toggle("class"), it does the same thing, but it's the cleaner way.
You Have To Use "this" Keyword To Reffer The Currently Clicked Element. You Can Achieve This Using JQuery Like This.
li.active { text-decoration : line-through; }
$("li").click(function () {
this.classList.toggle('active');
});
The problem is with scope.
function addDoneClass() {
li.className = "done"
}
In your context, li is an array of list items. Without running, I would guess that clicking on any list item was striking through all of the list items. Instead you need to pass the specific list item that you want changed.
li.addEventListener('click', (e) => {
addDoneClass(this); //this is the list item being clicked.
});
function addDoneClass(obj) {
// Now you can change to toggle if you want.
obj.className = "done"
// ie obj.className.toggle("done");
}
querySelectorAll returns a nodelist and addEventLister can only be applied to one node at a time. So to fix your code you should loop over each li in the list. And since you are using the event (evt) with addEventListener, you can use event.currentTarget to get the element clicked.
var listItems = document.querySelectorAll("li");
var listItemCount = listItems.length;
for (var i = 0; i < listItemCount; i++) {
listItems[i].addEventListener('click', (evt) => {
addDoneClass(evt);//
});
}
function addDoneClass(evt) {
evt.currentTarget.className = "done";
}
.done {
text-decoration: line-through;
}
<ul>
<li random="23">Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
I am trying to get the text value of an element that gets a class added after being clicked on. But I cannot get it. I also tried using html().
let selectLevel;
function selectOption(menu) {
$(menu).on('click', "li", function() {
$(menu).find('.activeSelection').removeClass();
$(this).addClass('activeSelection');
});
}
$(document).ready(function() {
selectOption('.levelList');
selectLevel = $('.levelList').find('.activeSelection').text();
});
The easiest way I can imagine is to trigger a custom event when you click on the list element, then bind a function to this event to update your variable.
Example below:
let selectLevel;
function selectOption(menu) {
$(menu).on('click', "li", function() {
$(menu).find('.activeSelection').removeClass();
$(this).addClass('activeSelection');
$(menu).trigger('itemChanged'); //trigger custom event
});
}
$(document).ready(function() {
selectOption('.levelList');
$('.levelList').on('itemChanged', function() { // wait for event on your menu
selectLevel = $(this).find('.activeSelection').text();
console.log(selectLevel); // just logging the current value
});
});
.activeSelection {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="levelList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Can anyone help me with this problem:
I want to trigger a click event on the next href nested inside an unordered list on an keyup-event but I can't get it running.
The HTML looks like this:
<ul>
<li>Start</li>
<li>Topic 1</li>
<li>Topic 2</li>
<li>Topic 3</li>
</ul>
and the jQuery looks like this:
$(document).keyup(function (e) {
if (e.keyCode == 40) { // down
$('#active').next("li a").click();
}
});
You are trying to select the succeeding li sibling element of the a#active element. It doesn't have an li sibling elements, but its parent element does.
You need to select the parent, and then select the sibling li element from there.
$(document).keyup(function(e) {
if (e.keyCode === 40) { // down
$('#active').parent().next().find('a').click();
}
});
You could also use the .closest() method, if the nesting varies:
$(document).keyup(function(e) {
if (e.keyCode === 40) { // down
$('#active').closest('li').next().find('a').click();
}
});
You may actually have to access the first matched DOM element in order to fire the click event as well (since .click() is a native DOM element method), so you could also try:
$(document).keyup(function(e) {
if (e.keyCode === 40) { // down
$('#active').closest('li').next().find('a')[0].click();
}
});
I have write code to make the li active on url basis .It works fine but it fails on child li.It make child li active while i want that top li should be active not child.My code is below:
<script type="text/javascript">
jQuery(function () {
setNavigation();
});
function setNavigation() {
// this portion code make li active on url basis
var pathname = window.location.pathname;
path = pathname.replace(/\/$/, "");
path = decodeURIComponent(path);
var value = jQuery(location).attr('href');
// value = value.replace('.html', ''); alert(value);
jQuery(".flexy-menu a").each(function () {
var href = jQuery(this).attr('href');
if (value === href) {
jQuery(this).closest('li').addClass('active');
}
});
// this is code for child li but only first code works
jQuery('.flexy-menu').children('li').click(function(){
jQuery(this).parent('li').addClass('active');
});
}</script>
My HTML is like this :
<ul class="flexy-menu orange">
<li style="">Home</li>
<li style="">Collection
<ul style=""> <li>My Secret Garden </li>
<li>Legend</li></ul>
</li>
<li class="active" style="">Artisans</li>
<li style="">Contact </li>
</ul>
Instead of parent use .closest():
jQuery(this).closest('li').addClass('active');
and put this in doc ready:
jQuery(function () {
setNavigation();
jQuery('.flexy-menu').find('li').click(function(){
jQuery(this).closest('li').addClass('active');
});
});
Here i changed your selector little bit with .find() instead of .children(), because .find() looks for grand child also and if you want to traverse up to the parent then use .closest() method.
I have write code to make the li active on url basis
Okay! then you can choose to do this:
$('a[href*="'+ path +'"]').parents('li').addClass('active');
This should work:
All to all you just need to do this only, no extra function required:
jQuery(function () {
var path = window.location.pathname;
$('a[href*="'+ path +'"]').parents('li').addClass('active');
jQuery('.flexy-menu').find('li').click(function(){
jQuery(this).closest('li').addClass('active');
});
});
jQuery('.flexy-menu > li').click(function(e){
jQuery(this).closest('li').addClass('active').siblings().removeClass('active');
e.preventDefault();
});
Demo:
http://jsfiddle.net/hqPQu/
how can I add a class in a drop dowwn menu on click when child of parent of parent is clicked.
This is my html:
<ul id="FirstLevel">
<li>FirstLevel</li>
<li>FirstLevel
<ul class="secondLevel">
<li>SecondLevel</li>
<ul class="LastLevel">
<li>LastLevel</li>
<li>LastLevel</li>
</ul>
<li>SecondLevel</li>
<li>SecondLevel</li>
</ul>
</li>
<li>FirstLevel</li>
<li>FirstLevel</li>
<li>FirstLevel</li>
</ul
So what I need is that; onclick on LastLevel or SecondLevel of my menu I want to add a class via jQuery on FirstLevel li and to remove that class when another sub menu is selected.
I've tried this but is not really working:
$('#firstUl').find('li').click(function(){ //removing the previous selected menu state $('#firstUl').find('li').removeClass('active'); //is this element from the second level menu? if($(this).closest('ul').hasClass('lastLevel')){ $(this).parents('li').parents('li').addClass('menuActive'); //this is a parent element }else{ $(this).addClass('menuActive'); } });
Thank you.
Give your FirstLevel <li>'s a class, it'd make it much easier to target the correct element:
<li class="first-level">FirstLevel</li>
Then attach a click handler to all child <li>'s:
$('li.first-level li').on('click', function(e) {
e.preventDefault(); // prevent the link from being followed
$(this).closest('li.first-level').toggleClass('yourClass') //
.siblings('li.first-level').removeClass('yourClass');
});
Here's a fiddle
This does all the things you want:
$("ul.secondLevel a").click(function () {
$("#FirstLevel>li").removeClass("blue");
$(this).parents("ul.secondLevel").parent().addClass("blue");
});
Check here: http://jsfiddle.net/c4dTK/
Here is the solution :
$('.secondLevel li a').click(function (e) {
$('#FirstLevel > li').removeClass('selected');
$(this).parents('.secondLevel').parent().addClass('selected');
})
As your first level list has been given an Id, you could just find the element using a selector as follows:
$(".secondLevel a").click(function () {
e.preventDefault();
$("#FirstLevel li").addClass("newClass");
});
try this
jQuery(this).parent('ul').addClass('yourClass');
Or:
jQuery(this).parents('ul').addClass('yourClass');
here goes another answer
var test= $('li li,li li li')
console.log(test)
test.click(function(){
$(this).parents('ul').closest('li').addClass('kaka');
});
jsfiddle