Javascript NodeList.forEach() not updating all found items - javascript

I don't understand why this function only seems to update the last item in the nodelist. I want the function to add the indicator element to all li elements that have a child ul element. The function finds all the elements, but only the last one seems to get updated.
var ulElementsWithChildren = document.querySelectorAll('ul.test>li ul');
var indicator = document.createElement('span');
indicator.innerHTML = '+';
ulElementsWithChildren.forEach(function(item) {
item.parentElement.appendChild(indicator);
});
html
<ul class="test">
<li>Item 1</li>
<li>Item 2
<ul>
<li>Child Item</li>
</ul>
</li>
<li>Item 3</li>
<li>Item 4
<ul>
<li>Child Item</li>
</ul>
</li>
<li>Item 5
<ul>
<li>Child Item</li>
</ul>
</li>
</ul>
JS Fiddle

You create one <span>.
You then append it in lots of different places.
Since an element can only appear in one place, you move it each time until you get to the end of the loop.
You need to create a new span each time you go around the loop.

Move your indicator declaration inside of your foreach and it will work.
example:
ulElementsWithChildren.forEach(function(item) {
var indicator = document.createElement('span');
indicator.innerHTML = '+';
item.parentElement.appendChild(indicator);
});
You're only creating a single span element.

Related

Reverse order of list items with JS not working correctly

I have an unordered list that I have reversed with javascript. My script works but the list is duplicated. The page renders with the original list plus the output of twhat my js is doing. How can I make it only render once? Should I rewrite with jQuery and use .detach() ? Below is my code:
var navList = $('ul.menu');
var navListItems = list.children('li');
navlist.append(navListItems.get().reverse());
Just typos in variable names :)
var navList = $('ul.menu');
var navListItems = navList.children('li');
navList.append(navListItems.get().reverse());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul class="menu">
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
You're appending the items to a list that already has items. I suggest clearing the list after you've stored an array of the items. Then, you can start to append each item again. See snippet below.
var navMenu = document.getElementById("menu");
var navList = Array.from(navMenu.children);
navList.reverse().forEach( function(listItem){
navMenu.append(listItem);
});
<div>
<p>List</p>
<ul id="menu">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
Edit: as per freedomn-m's comment, the append function will "move" existing items instead of adding onto the list. Removed the unnecessary navMenu.innerHTML = ""; line.

How can i remove the text decoration from a list item wrapped in an <a> tag, using a query selector?

I want to remove the textDecoration from a special item using a javascript ( preferably a query selector. I don't know what I'm doing wrong though. Here's the code. CodePen
<h1>Thanks for the Help!</h1>
<h1> My problem set </h1>
<ul>
<li id="highlight">List Item 1</li>
<li class="bolded">List Item 2</li>
<li class="bolded">List Item 3</li>
</ul>
var sLi = document.querySelector("ul a.special");
for (var i = 0; i <= sLi.length; i++){
sLi[i].style.textDecoration = "none";
}
There is no need to loop over your query result since querySelector() only returns one item.
Had you been using querySelectorAll(), you would want the loop as that returns a node list.
var sLi = document.querySelector("ul a.special");
sLi.style.textDecoration = "none";
<h1>Thanks for the Help!</h1>
<h1> My problem set </h1>
<ul>
<li id="highlight">List Item 1</li>
<li class="bolded">List Item 2</li>
<li class="bolded">List Item 3</li>
</ul>
Also (FYI), the li should contain the a and not the other way around.

Find if there is any repeat content in a list and remove repeated instances

Basically what I'm trying to do is identify if there is any duplicate content from a list of items that all share the same class, and then remove any duplicates.
Here's my attempt so far but has been unsuccessful:
var listItemContent = $(".featured-listing").textContent;
if (listItemContent === listItemContent) {
$('.featured-listing').hide();
}
This isn't really working, and I'm not surprised as js is not my strong suit and even reading it it looks like it would take the content of an instance of ".featured-listing", compare it to itself and then hide it, resulting in all instances of that class being hidden. The problem is I'm not really sure how to:
Check if DIFFERENT instances of that class have matching text content
Remove any duplicated content
Try this snippet:
var listItemContents = [];
$(".featured-listing li").each(function() {
var text = $(this).text();
if (listItemContents.indexOf(text) == -1) {
listItemContents.push(text);
}
else {
$(this).remove();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="featured-listing">
<li>Text 1</li>
<li>Text 2</li>
<li>Text 3</li>
<li>Text 1</li>
<li>Text 4</li>
<li>Text 2</li>
</ul>
It just loop through all items checking their content by an array. If the text isn't found in the array, it adds the item. If it is found, it removes the list item.
I'm not sure if that html is like yours since you didn't added it in your post. If you do, we can have a clearer idea of what would works better for you.
Get all the featured-listings. Define a main object which holds all the texts and then delete an element if there is the same text:
var elements = {};
$('.featured-listsing').each(function () {
var currentText = $(this).text();
if ( elements[currentText] )
$(this).remove();
else
elements[currentText] = true;
});
You can filter your jQuery list object taking those that are repeated and hide them. Check the next fiddle, the technique is to check those items that have a previous sibling with the same text content and create a new jQuery list with them:
let items = $(".featured-listing li");
let repes = items.filter((ind, itm) => $(itm).prevAll(`:contains(${itm.innerText})`).length);
repes.remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="featured-listing">
<li>1 - Content 01</li>
<li>2 - Content 02</li>
<li>1 - Content 01</li>
<li>3 - Content 03</li>
<li>4 - Content 04</li>
<li>2 - Content 02</li>
<li>5 - Content 05</li>
<li>6 - Content 06</li>
<li>2 - Content 02</li>
<li>7 - Content 07</li>
</ul>

Trying to parse tag element

I am trying to parse a web page with JavaScript targeting list <li> without class.
<ul id="cartItems">
<li class="heading">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
Use querySelectorAll to get all li elements without a class.
var liWithoutClass = document.querySelectorAll('#cartItems > li:not([class])');
console.log(liWithoutClass);
document.write(liWithoutClass[0].textContent); // Output the first li
document.write('<br />' + liWithoutClass[3].textContent); // Output the last li
<ul id="cartItems">
<li class="heading">Heading</li>
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element 4</li>
</ul>
var elems = document.querySelector(".heading");
console.log(elems);
//Now we strip the class out like this:
elems.setAttribute(class, "none");
//Now we display the updated values below
console.log(elems);
// This won't work in Stack's editor, so you'll have to validate it against the page you want.
//Now, just grab the entire element by id from the page as below:
var updatedContent = document.getElementById("cartItems");
console.log(updatedContent);
//Again, stack's editor is limited, but overall this should work fine on your page.
<ul id="cartItems">
<li class="heading">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>

Selecting the .index() of a deeply nested element

I want to select the tags onlu inside a by the index shown in the link names. What is the best method of doing this?
<div class="index-get">
<div class="column">
Category Title
<ul>
<li>Index 0</li>
<li>Index 1</li>
<li>Index 2</li>
<li>Index 3</li>
<li>Index 4</li>
</ul>
</div>
<div class="column">
<ul>
<li>Index 5</li>
<li>Index 6</li>
<li>Index 7</li>
<li>Index 8</li>
<li>Index 9</li>
</ul>
</div>
</div>
$('.index-get ul a').click(function(){
var aIndex = $(this).index();
console.log(aIndex);
});
var aIndex = $('.index-get li a').index($(this));
and you should cache $('.index-get li a') outside of the event handler
var $lia = $('.index-get li a');
$lia.on('click',function() {
console.log($lia.index($(this)));
})
Not sure if the question is clear enough but here is something that might relate to what you need:
$('.index-get ul a').click(function(){
var aIndex = $(this).text();
console.log(aIndex);
});
And the fiddle.
EDIT
I think I finally got it the question sigh... :-)
Since there is only one A inside each parent LI the index of A tags will always be 0 in your example because the index is in relation to the sieblings...
If you calculate the index for the LI you might get better result. But keep in mind the index is only counted inside each parent so for the two ULs you will get two sets of LI children hence clicking Index 1 or Index 6 will yield the same result.
New Fiddle
One more edit
Actually the ULs are nor sieblings and one must go above to the DIV and down again into the ULs to be able to count the LI of previous items... But I guess it works just perfect for your needs. I I understood your needs that is... ;-)
$('.index-get ul li').click(function(){
var aIndex = $(this).index()+$(this).parent().parent().prev().find("ul").children().length;
console.log(aIndex);
});
And the fiddle.
Last edit
Making the line slightly simpler
var aIndex = $(this).index() + $(this).parent().parent().prev().find("ul li").length;
&(this) is an LI element.
$(this).parent() is the UL
$(this).parent().parent() is the DIV
$(this).parent().parent().prev() are all the sibling elements of that DIV (hopefully only DIVs in there and all with the same type of content)
$(this).parent().parent().prev().find("ul li") all LI inside UL inside sibling DIVs.
$(this).parent().parent().prev().find("ul li").length; the number of LI in the previous line.
There you go. Simple logic! ;-)
And when you think all is done and settled... There comes one more edit!!! :-)
I was thinking of the solution I proposed and it will eventually fail if you have more then two UL groups and if you click on a LI located in the third UL or later.
Therefore I have created yet another solution which uses .prevAll() instead of just .prev() but then must use the .each() with a function to add up all the count of all LI elements from the previous ULs.
Fiddle
Now I can go sleep in peace! :-)
Fabricio
Try this code:
$('.index-get ul a').click(function(){
var aIndex = $(this).text().match(/\d+$/)[0];
console.log(aIndex);
});
This should work no matter how deep the li's are nested:
var items = $('li');
items.on('click', function() {
var clicked_item = $(this);
items.each(function(index){
if ($(this).is(clicked_item)){
console.log(index);
}
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="index-get">
<div class="column">
Category Title
<ul>
<li>Index 0</li>
<li>Index 1</li>
<li>Index 2</li>
<li>Index 3</li>
<li>Index 4</li>
</ul>
</div>
<div class="column">
<ul>
<li>Index 5</li>
<li>Index 6</li>
<li>Index 7</li>
<li>Index 8</li>
<li>Index 9</li>
</ul>
</div>
</div>

Categories