removechild loop exits before finish - javascript

I have the following piece of code that finds all elements in the document with classname foo and then removes them all
function(doc) {
var items = doc.getElementsByClassName('foo');
alert(items.length);
if(items.length>0) {
for(var i=0;i<items.length;i++) {
alert(i);
doc.body.removeChild(items[i]);
}
}
Forexample, the items.length is 3 and the function exits after running one loop and when the length is 8 it exits at 3. Any help would be greatly appreciated. Also, when I run the function again and again it does eventually remove all elements.

Your problem is that the NodeList returned by getElementsByClassName() is live. Either convert it into an array first as Felix suggests or iterate backwards:
var items = doc.getElementsByClassName('foo');
var i = items.length;
while (i--) {
items[i].parentNode.removeChild(items[i]);
}
This works because the item removed from the list each iteration is the last item in the list, therefore not affecting earlier items.
I also changed doc.body to items[i].parentNode for greater generality, in case you need to deal with elements that are not direct children of the <body> element.

The problem is that items is a live NodeList, i.e. whenever you access a property of the list (items.length), the list is reevaluated (elements are searched again).
Since you delete elements in the meantime, the list becomes shorter, but you keep the index.
You could convert the NodeList to an array first:
var items = [].slice.call(doc.getElementsByClassName('foo'));
The array size won't change when you delete the DOM elements.

Related

insertBefore function for arrays and/or HTMLCollections?

Does there exist a function in vanilla JavaScript or jQuery that operates similarly to Node.insertBefore(), but for arrays and/or HTMLCollections?
An example could look something like:
var list = document.getElementsByClassName("stuff");
var nodeToMove = list[0];
var otherNode = list[4];
list.insertBefore(nodeToMove, otherNode);
Basically I'm trying to perform insertBefore() without manipulating the actual DOM, as I want the changes to only be applied to the DOM under certain conditions. If those conditions are met, then I would perform insertBefore() on the actual nodes.
To clarify, I'm looking for a function that would insert an element before a target element at a given index in an array, not necessarily at a given index. Examples I've seen using splice() usually insert an element at a given index, which sometimes puts the element before the target element, and sometimes after, depending on where the element to be moved originally was in the array. I'm looking for something that would reliably put the element to be moved before the target element.
HTMLCollection does not have an insertBefore method. jQuery can apply any jQuery methods both to a single element being selected, as well as many.
https://api.jquery.com/insertBefore/
There is no single method to do this in one step, but there doesn't need to be. If you convert the collection to an Array, you can call the Array.prototype.splice() method to achieve the same result.
Here's an example:
let ary = [1,2,3,4,5];
// Swap 2 and 3
// Start at the 3rd item and remove one item (3).
// Store the removed item
let removed = ary.splice(2,1);
// Start at the second item, don't remove anything, insert the removed
// item at that position
ary.splice(1,null,removed[0]);
// Log the result
console.log(ary);
And, with that knowledge, you can create your own more easily callable function:
let ary = [1,2,3,4,5];
function insertBefore(ary, newItem, target){
ary.splice(target,null,newItem);
}
// Insert 999 before the 3rd array item
insertBefore(ary,999,2)
console.log(ary);
You need to get the index you want, then use Array.splice.
Myself I would do something like this :
const myArr = ['Aurore', 'Dimitri', 'Alban', 'Frédéric'];
const insertBeforeThis = 'Alban';
const eltToInsert = 'Laura';
const index = myArr.findIndex(name => name === insertBeforeThis);
myArr.splice(index, 0, eltToInsert);
Please feel free to try it out in your browser's console. Note i used const for my array, as it fixes the type of the variable as an array but allow me to manipulate it.
MDN: Array.prototype.findIndex()
stackoverflow: How to insert an item into an array at a specific index (JavaScript)?
Have a happy coding time!

JavaScript - Arrays - Deleting elements from an array

I've looked up arrays and how they work, looked at a lot of other stackoverflow questions on this same topic but the answer still doesn't remain clear. How can I remove a specified element from an array?
I've tried:
array.splice(5);
array.splice(i, 5);
delete array[5]; //doesn't actually delete - I know
1 of 2 things happen every time. 1. The whole array is deleted with either of the first 2 methods mentioned above. or 2. Everything before/after the element specified is removed.
For example, I had an array that contained a Clash Royale deck:
var deck = ["Barbarians", "Goblin_Barrel", "Inferno_Tower", "Fireball", "Zap", "Hog_Rider", "Spear_Goblins", "Minion_Horde"];
Then if I wanted to remove, lets say, Fireball, then I did:
deck.splice("Fireball");
And the array now looked like this:
deck = [];
So, to restate my question. How do I remove a specified, and only the specified, element from an array?
Check the usage for splice in a JavaScript reference. You need to pass in the index of the thing to delete, then how many things to delete.
First find the index of element then remove it using splice.
Find Index of element
var index = deck.indexOf("Fireball");
Now remove element using splice.
if (index > -1) {
deck.splice(index,1);
}
if duplicate values exists then
for(ind = 0 ; ind <deck.length; ind++){
if(deck[ind]=="Fireball"){
deck.splice(ind--,1);
}
}

Get the last item from node list without using .length

The following command
document.querySelectorAll('#divConfirm table')[1].querySelectorAll('tr')
gives a node list with 3 tablerow (tr) elements in it. If I know the list size, I can access the last element via.item(2).
Is there a way to get the last element directly without resorting to .length first?
There's at least one way
var els = document.querySelectorAll('#divConfirm table')[1].querySelectorAll('tr');
var last = [].slice.call(els).pop();
but, the following statement
But if I do not know the length prior to running the script
makes no sense, you already have the collection of elements, so you would always know the length
var els = document.querySelectorAll('#divConfirm table')[1].querySelectorAll('tr');
var last = els[els.length - 1];
Another option as the8472's answer suggests would be
document.querySelector('#divConfirm table:nth-child(2) tr:last-child');
Since NodeList doesn't have a pop method.
Using the spread syntax in a new array, then pop() to get the last element.
This basically copy the Nodelist as a regular array, thus the pop() and other array methods become available.
console.log(
// Last elem with pop()
[...document.querySelectorAll("div")].pop()
)
console.log(
// 2nd elem
[...document.querySelectorAll("div")].slice(1)[0]
)
console.log(
// By index :) just in case
document.querySelectorAll("div")[0]
)
<div>The sun</div>
<div>is</div>
<div>shinning</div>
depending on circumstances this may work: document.querySelector('#divConfirm table tr:last-of-type')
You can transform the NodeList into an Array. Then can use array.pop(). It return the last item BUT remove it to the array!
const elementsArray = Array.from(elements);
elementsArray.pop()

Make sorted array show results on webpage?

I have this fiddle that sorts items on my webpage. I can see it works by checking the console.log results. I just don't know how to make the results appear on the actual webpage -- the items sorted in order.
http://jsfiddle.net/AQFFq/21/
function myFunction() {
var elements = [].slice.call(document.getElementsByClassName("price"));
elements.sort(function(a, b) {
return parseFloat(b.innerHTML.substring(1)) - parseFloat(a.innerHTML.substring(1));
});
for (var i = 0; i < elements.length; i++)
elements[i].parentNode.appendChild(elements[i]);
console.log(elements);
}
Thanks.
as i mentioned in my comments you have a couple of problems with your sorting. 1 there isnt {} around your for loop so it is only running once. 2 your are appending the price to its own parent node and since the parent nodes are still in their original order the function does nothing. what you need to do is select the parent node that is the entire element you want to sort and append that to the first parent node your sortable items have in common. here is a jsfiddle for what i mean functioning fiddle ps i switched a and b in your sort function so that the result is actually different then the original page.

Delete elements with a particular class name (using javascript)?

I was trying to delete elements with a particular class name, but it doesnt work. Any idea why?
function delete_elems(){
var my_elems = document.getElementsByClassName("my_elem");
for(k=0; k<my_elems.length; k++){
my_elems[k].parentElement.removeChild(my_elems[k]);
}
}
I also tried:
function delete_elems(parentElem){
var my_elems = document.getElementsByClassName("my_elem");
for(k=0; k<my_elems.length; k++){
parentElem.removeChild(my_elems[k]);
}
}
Begin at the end:
for(k=my_elems.length-1;k>=0; --k)
http://jsfiddle.net/doktormolle/fDNqq/
The getElementsByClassName method returns a live node list, so iterating over it using an index and at the same time modifying the list (by removing nodes from the document) means that as elements are removed, the list is modified so what started as a list with 10 members will only have 9 once one is removed. If you remove item 0, then all items are re-indexed so what was item 1 is now 0, 2 is 1 and so on up to 8 (since there are only 9 left).
As noted by Dr. Molle, you may be able to avoid this issue by iterating over the list from the end.
You can avoid a counter altogether:
var el;
while ((el = my_elems[0])) {
el.parentNode.removeChild(el);
}
Note that when using a NodeList, each time an element is removed, the list must be re-generated by the browser (regardless of which of the above methods is used) so this method will likely be slow where the nodeList is large.
You can avoid that by converting the list to an array first (simply iterate over it) in which case you can use either an incrementing or decrementing counter and not worry about missing members in the list (since it isn't modified when elements are removed from the document).

Categories

Resources