I have 6 list items
$('.favorite-tag-group li').each(function(){
console.log("hi");
});
This code however is displaying "hi" 24 times in console.
The only thing I can think of that might be causing it to bug out is because my list items arent all in the same list.
For example, .favorite-tag-group is a div that always contains a ul. In some cases, that ul will only have 1 li. Sometimes it may have 2.
Here's a sample of what that might look like
div.favorite-tag-group
ul
li
li
li
div.favorite-tag-group
ul
li
div.favorite-tag-group
ul
li
div.favorite-tag-group
ul
li
li
All I'm trying to do is run through .each() li so that I can remove duplicates ;/
Some real html:
<div class="favorite-tag-group">
<h4>gobty</h4>
<ul class="resources led-view">
<li class="clearfix r-tutorial" data-id="22">
</li>
</ul>
</div>
<div class="favorite-tag-group">
<h4>javascript</h4>
<ul class="resources led-view">
<li class="clearfix r-tutorial" data-id="24">
</li>
</ul>
</div>
<div class="favorite-tag-group">
<h4>macvim</h4>
<ul class="resources led-view">
<li class="clearfix r-tool" data-id="21">
</li>
</ul>
</div>
here is the real function. When i paste the .each() directly into console it works, but inside this function it doesnt work:
// collapse tags functionality
$('.collapse-tags').click(function(e){
e.preventDefault();
$('.favorites-helpers, .favorite-tag-group h4').slideUp(200, function(){
var seen = {};
$('.favorite-tag-group li').each(function(){
//console.log("hi");
var currentId = $(this).data('id');
if (seen[currentId]) {
$(this).slideUp(200);
} else {
seen[currentId] = true;
}
});
});
});
As in my comment above... With a bit of further explanation.
It's because $('.favorites-helpers, .favorite-tag-group h4') will be causing multiple elements to slideUp(), and therefore the callback gets executed multiple times. Moving var seen = {} to inside the callback resets the variable as an empty object in each callback. You'll still iterate over your list items more than once (as seen by multiple console.log()s), but you'll slide the same duplicate li's up each time this way.
You asked: "The one thing im still confused about is, why would it not be able to see the scope of seen if it were outside the callback? wouldnt variable scope say that it could see it because its outside the function?"
Yes, you are right - the callback could see seen, but seen was never emptied/reset, and therefore after the second iteration of your callback, all of your li's would have had .slideUp() called on them.
Consider this: because it either slides the duplicate up, or adds the id to seen, on the second callback, .each() runs again, but it's already full of all of your list items ids.
Hope this is clear enough, if not just comment below and I'll try and come up with some examples.
Here you are, sir...
$(document).ready(function(){
$("div.favorite-tag-group>ul").children("li").each(function(index,element){
//code here
//refer to element as $(element)
//to get the id of the element use: $(element).attr("id");
});
});
Demo: http://jsfiddle.net/9uz8k/11/
Link:
http://api.jquery.com/each
Related
I'm simply creating a nav menu and have a basic unordered list with a "sub-menu" within. Here's the Jquery. The reason for the "active" class and if statement is that I want the slider to remain open if choosing another item on the list, so that it doesn't close and open again each iteration.
The problem part here for me in the code is here.
$('.slider').html( $(this).find('ul') );
The main issue is that on('click',), is only updating the first time it's clicked. On a second click on another item in the menu, it doesn't update itself with the new selection and therefore doesn't show the new sub-menu list? Console log shows that it is undefined. But it should be like the first time?
I don't understand why this is happening or the reasons behind it! Logic tells me the same thing that happens the first time, should keep happening, and therefore work?! Help appreciated before I make a hole in the wall with me forehead!
$(document).ready(function() {
$('ul > li > ul').hide();
$('.slider').hide();
// Menu
$('.menuNav > ul > li').on('click', function() {
// if active
if ( $(this).hasClass('active') ) {
$(this).parents().children().removeClass('active');
$('.slider').hide('slide', 200);
}
// if unactive
else {
$(this).addClass('active');
$('.slider').show('slide', 200);
$(this).siblings().removeClass('active');
// Show related list
$('.slider').html( $(this).find('ul') );
}
});
});
HTML
<div class="menuNav">
<ul>
<li><span>item_1</span>
<div class="sub_position">
<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>
</div>
</li>
<li><span>item_2</span>
<div class="sub_position">
<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>
</div>
</li>
.... etc
</ul>
</div>
Your issue likely arises from the fact that you are actually moving the entire submenu into .slider, instead of cloning it. This means that the submenu will be removed from the original menu the first time you click it, and clicking on it again will mean that the nested submenu selector $(this).find('ul') will return an empty set.
To circumvent this issue, I suggest that you do the following:
Append the outerHTML of the submenu to slider, i.e. $(this).find('ul')[0].outerHTML
If you want to hide the submenu in the menu when this is done, simply hide/unhide it when appropriate.
You can always use $(this).find('ul').html() for the same effect, but remember that <li> elements must be immediate children of <ul>. Therefore, we need to clone the outerHTML as well (i.e. copy the <ul>) to ensure that you have a valid HTML being injected into your slider.
$(document).ready(function() {
$('ul > li > ul').hide();
$('.slider').hide();
// Menu
$('.menuNav > ul > li').on('click', function() {
// if active
if ($(this).hasClass('active')) {
$(this).parents().children().removeClass('active');
$('.slider').hide('slide', 200);
}
// if unactive
else {
$(this).addClass('active');
$('.slider').show('slide', 200);
$(this).siblings().removeClass('active');
// Show related list (changed: now we use HTML's native outerHTML object
$('.slider').html($(this).find('ul')[0].outerHTML);
}
});
});
.slider {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="slider"></div>
<div class="menuNav">
<ul>
<li><span>item_1</span>
<div class="sub_position">
<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>
</div>
</li>
<li><span>item_2</span>
<div class="sub_position">
<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>
</div>
</li>
</ul>
</div>
You have to fix this line
$('.slider').html( $(this).find('ul') );
I am not sure what you are trying to achieve here, but you cannot treat a DOM object as HTML code. The reason your code works once is that this line is does not fire the first time (your first IF statement fires). The second time ELSE kicks in and this evil line messes things up.
Solution 1: Comment out or remove this line
//$('.slider').html( $(this).find('ul') );
Solution 2: Figure out what you want to do with this line and do it the proper way.
Friends, I am working on JavaScript for collapse/Expand <UL> list.
here is my Code. I am wanted to work on it, in Nth Level, i can show Child, but its not hiding Children.
I hope you guys will help me..
Thanks in Advance...
This will do the trick:
event.stopPropagation();
Docs.
If you debug your code you'll see that the event is being called for each parent ul. Check this out:
$("#ExpList ul li:has(ul)").click(function(event) {
event.stopPropagation();
$(this).find('> ul')
.toggleClass("hiddenChild")
.toggleClass("displayChild");
});
And the HTML:
<div id='ExpList'>
<ul>
<li>Platform-1
<ul class='hiddenChild'>
<li>Child-1
<ul class='hiddenChild'>
<li>P-C-C-1</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
Fiddle.
HTML considerations:
I don't know if you can use a div wrapping the whole list, but I think it would make more sense doing it;
You're using the id ExpList for all ul. This is not how we use an id. Instead, for selecting many elements use its own tag or a common class;
I removed the onClick = openChild() which were present in all ul. It was throwin an error in your fiddle.
JavaScript considerations:
You don't have to mix a selector with a find this way $("#ExpList").find('li:has(ul)'). You can just use it on the same selector, as I did $("#ExpList ul li:has(ul)");
You don't need to search for all ul children($(this).children('ul')) since you'll have just one, I used this instead, which looks for just one: $(this).find('> ul');
As said before, the event.stopPropagation() does the trick. You don't need to trigger click event on all parent ul.
In CSS, I just changed #ExpList to this #ExpList ul to work in the new structure. I hope it helps.
I have a list of links, one has the class active.
On my next button click id like to remove the class from the current element and add it to the next only I cant seem to get it to add?
Ive made a fiddle to hopefully explain my problem, any help would be great, thanks
http://jsfiddle.net/h6D4k/
$('.next').click(function(){
$('ul.pagination').find('a.active').removeClass('active');
$('ul.pagination').find('a.active').next('a').addClass('active');
return false;
});
One of the jQuery most usable conveniencies is that its methods are (usually) chainable - in other words, they return the very object they are called from. So you can simply write this:
$('ul.pagination').find('a.active').removeClass('active').closest('li')
.next('li').find('a').addClass('active');
... as it's <li> elements that should be 'nexted', not <a> ones. But in fact, you shouldn't probably discard 'active' altogether if it's the last element in question:
var $a = $('ul.pagination').find('a.active'),
$li = $a.closest('li'),
$nextLi = $li.next('li');
if ($nextLi.length) {
$a.removeClass('active');
$nextLi.find('a').addClass('active');
}
This is actually what you want based on your html structure in you fiddle. http://jsfiddle.net/h6D4k/1/
$('ul.pagination').find('a.active').removeClass('active').parent()
.next().find('a').addClass('active');
Because once you've done this...
$('ul.pagination').find('a.active').removeClass('active');
There is no more a.active - the active classname has been removed from that element. So repeating the same selector...
$('ul.pagination').find('a.active')//...
... will select nothing.
Chain it all together instead.
$('ul.pagination').find('a.active').removeClass('active').next('a').addClass('active');
You have a second problem. According to the jQuery API for next(), it will:
Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector.
You're not trying to get the following sibling:
<ul class="pagination">
<li><a class="one active" href="#">X</a></li>
<li><a class="two" href="#">X</a></li>
<li><a class="three" href="#">X</a></li>
</ul>
Next
Prev
You're trying to get the next <a> in the whole document. That's more challenging - and I'm not sure how to do it.
I would write it this way, preventing the action from doing anything on the last li as well.
http://jsfiddle.net/h6D4k/6/
$('.next').click(function(e){
e.preventDefault();
if ($("ul.pagination a.active").parent().is(":last-child")) return;
$('ul.pagination a.active').removeClass('active').parent().next().find("a").addClass('active');
});
You have two errors in your code:
Once removed, the active class can't be found anymore
your a tags are nested in li tags so next() doesn't work as you expect
To simplify things, you could attach the active class to the li tags.
Live demo: http://jsfiddle.net/h6D4k/7/
Code:
$('.next').click(function(){
$('ul.pagination').find('li.active').removeClass('active')
.next().addClass('active');
return false;
});
I am trying to append a list element dynamically to already existing list.
I've a list in the form,
<ul id="test">
<li class="testField">YYAAHOOOOOO<li>
</ul>
I am trying to add an extra item to this list using jQuery append or after...
What I did was:
$(".testField").after("<li class='testField'>TEST MESSENGER</li>");
Used after function as append did not work, the after function works fine first time as there is only one element with the class name testField, but as the list grows, the after function will add the list items to all elements present,
To make it clear, on first trial I will get an output:
<ul id="test">
<li class="testField">YYAAHOOOOOO<li>
<li class='testField'>TEST MESSENGER</li>
</ul>
If I try the to add another element now for example <li class='testField'>GOOGLE</li>, the output will be like:
<ul id="test">
<li class="testField">YYAAHOOOOOO<li>
<li class='testField'>GOOGLE</li>
<li class='testField'>TEST MESSENGER</li>
<li class='testField'>GOOGLE</li>
</ul>
I thought about using ids, but I am trying to have an option to remove elements from the list too... So if I try to append to an undefined id, it will return error. Is there anyway to find the first element inside a list and append the element to that first one?
Try:
$(".testField:first").after("<li class='testField'>TEST MESSENGER</li>");
this will make sure that you are appending after the first element only
Alternatively, you could do this without jQuery:
var li = document.createElement('li'); // Create a List item.
li.setAttribute("class", "testfield"); // Set the li's class
li.addChild(document.createTextNode("Your Text Here!")); // Set the li's text
document.getElementById("test").addChild(li); // Append the li to the list
It's slightly more code, yes, but it's pretty much what jQuery does under the hood. (And faster, too)
If you want to add the new ul after the first li already in the list, replace the last line with:
var ul = document.getElementById("test");
if(ul.children.length > 1){ // If the list has 2 or more children (li's)
ul.insertBefore(li, ul.children[1]); // Insert the new item before the second item in the list.
}else{
document.getElementById("test").addChild(li);
}
Now, why am I posting a answer that requires more code?
Anything that can be done in jQuery can be done in native JS. I think it's good to have multiple different answers available on SO, especially if they use different techniques to do the same thing. That way, users can chose for themselves: short'n easy (jQuery), or if they don't want to use the library, native code.
I am trying to remove the li's with conditon under Ul in multiple div's.
<div>
<ul>
<li class="sel">.....</li>
<li class="sel">.....</li>
............
<li>.....</li>
<li>.....</li>
...........
<!-- I have some 600 li's -->
</ul>
</div>
I have 200 li's with class='sel'. Now I need to remove the remaining 400 li's.
I am trying to remove in two ways, like,
$("ul", this).each(function(){
$("li", this).each(function(){
$(this).remove();
//Also tried with -- $(this).empty().remove();
});
});
other way like,
$("ul", this).each(function(){
$("li[class!=sel]", this).remove(); // Also tried with 'not'
});
Now the problem is, When I am trying to execute these ways In IE getting Script overloaded error.
Please help me out on other ways to remove unwanted li's.
Note: I don't want to keep the un-wanted li's to hide() state.
Thanks in advance...
If you're using the Attribute Not Equal Selector, you don't need to wrap it with .each() - simply call it like this:
$('li[class!="sel"]').remove();
The selector ('li[class!="sel"]') is grabbing all <li> elements that don't have class sel and then calling the remove() method on that entire set of elements.
Demo
Edit
If you only want to target the <ul> which contains the <li class="sel"></li> elements, you can set the context like this:
// Find the first <li> with class="sel" and get it's parent <ul>
var $ul = $('li.sel:first').parent('ul');
// Use $ul as the context for the removal of the <li> elements
$('li[class!="sel"]', $ul).remove();
For what it's worth, I ran into a similar problem just a couple of weeks ago, only in IE8, even calling .remove() on a single item selected by id. The problem only occurred when the element contained a great deal of content.
What I did was to call .html('') to clear the element just before calling .remove(). This reduced the time dramatically (sub-second).
Mine was obviously a different situation (one element vs. many, selected by id vs. contextual selectors, not sure what your li content is, etc.), but it might be worth a shot.
$("ul", this).each(function(){
$("li[class!=sel]", this).html('').remove();
});
Try detaching the ul, remove the li's, then reattach the ul.
$("ul", this).detach().find("li").not(".sel").remove().end().end().appendTo(this);
This prevents the browser from redrawing until all li's that need to be removed are removed.