Javascript remove not removing all elements - javascript

Please refer fiddle - http://jsfiddle.net/fkwwyvz8/
x = document.getElementsByClassName("buddy_blocks");
for(i=0;i<x.length;i++)
x[i].remove();
Click on the last button, and it had to remove all the other buttons, but it does not and only removes some of them, not sure why? And any way to remove all those buttons?

Since you appear to have jQuery in your code already, you can just use this to remove all buttons:
$(".buddy_blocks").remove();
Your attempt was not working for two reasons:
document.getElementsByClassName() returns a dynamic nodeList that changes underneath you each time you remove an element which causes you to miss elements in your iteration
Because a DOM element does not have a .remove() method (in most browsers anyway - it is a proposed method, but isn't widely available yet). The parent has a .removeChild() method you can use instead.
In plain Javascript, you could set up your iteration backwards so that when you remove elements and when this causes the dynamic HTMLCollection to change, it will not mess up your iteration because the changes will be the elements that you have already passed by. And, switch to use .removeChild() like this:
function f() {
var x = document.getElementsByClassName("buddy_blocks");
for(var i = x.length - 1; i >= 0; i--) {
x[i].parentNode.removeChild(x[i]);
}
}
Also, please use var on all variables that are intended to be local to your function so you are not creating "accidental global variables" which will cause hard to figure out bugs at some time in the future.
Or, in modern browsers as of 2021, you can use document.querySelectorAll() (because it doesn't return a live collection and is iterable) and you can use .remove() since all modern browsers support it:
function f() {
const items = document.querySelectorAll(".buddy_blocks");
for (let item of items) {
item.remove();
}
}

When iterating over an array and modifying it, start at the last index to avoid side effects to the current index position when you remove items
$("#c").click(function() {
f();
});
function f() {
x = document.getElementsByClassName("buddy_blocks");
for(i=x.length-1;i>=0;i--)
x[i].remove();
}

You were looping forward through the elements.
x = document.getElementsByClassName("buddy_blocks");
for(i=0;i<x.length;i++)
x[i].remove();
x is a NodeList not an array. It is a live view onto the current elements that matches the elements with that class. So when you delete the first item, what was the first item is no longer in the list, and the second item is now the first item. But, you've moved on to what was the third item, but is the new second item.
The end result is you're only removing half of the elements.
Consider either this:
//remove the first item from the NodeList until there is nothing left
x = document.getElementsByClassName("buddy_blocks");
while(x.length > 0) {
x[0].remove();
}
or:
//remove from the end of the list, which won't cause other elements to change position
x = document.getElementsByClassName("buddy_blocks");
for(i=x.length-1;i>=0;i--) {
x[i].remove();
}

I ran into the same problem and have spent a couple of hours struggling with it. With the help of other answers on this page, I have come up with :
x = document.getElementsByClassName("buddy_blocks");
while(x.length){
x[0].parentElement.removeChild(x[0]);
}

jQuery way to remove all elements with class 'buddy_blocks':
$(".buddy_blocks").remove();
fiddle
plain javascript way:
var elems = document.getElementsByClassName('buddy_blocks'),
elem;
while(elems.length){
elem = elems.item(0);
elem.parentNode.removeChild(elem);
}
fiddle

Reason for such a behavior is the variable containing the DOM element "x" is pointing to the live DOM element. So every time you remove one element, the "x" is holding the new DOM structure. So we need to use the while loop below and keep removing the 1st child from the DOM.
$("#c").click(function() {
f();
});
function f() {
x = document.getElementsByClassName("buddy_blocks");
//for(i=0;i<x.length;i++)
//x[i].remove();
var i = 0;
while(x.length) {
x[0].remove();
i++;
}
}

Related

How to remove all instances of a class when adding it to a new object in Javascript

Trying to create an active state on the most recently clicked navigation bar button. Currently it will add the class to each item selected but the for...of loop doesn't seem to remove the class.
The nav bar is dynamically made with the following code:
function buildNavBar(){
for (section of sections){
numberOfSection = section.getAttribute('data-nav');
idOfSection = section.getAttribute('id');
navBarList = document.createElement('li');
//Uses template literal strings to present the section names in the nav bar
navBarList.innerHTML = `<a class='menu__link' href='#${idOfSection}'>${numberOfSection}</a>`;
navBar.appendChild(navBarList);
}
}
document.getElementById('navbar__list').addEventListener("click", function(event){
function removeStyle(){
let funcElem_Style = 'funcElemStyle';
let menuLinks = document.querySelectorAll('menu__link')
menuLinks = document.getElementsByClassName('funcElemStyle')
for(menuLink of menuLinks){
menuLink.classlist.remove('funcElemStyle')
}
}
event.target.classList.add("funcElemStyle");
});
Don't use for...of to iterate over live HTMLCollections like what getElementsByClassName returns. The reason is that in that loop you are executing code which leads to the element being removed from the live collection instantly, which in turn for the for...of (which internaly works index-based) means that the next element to iterate over actually is the element after the next one, leading to a loop that affects only every 2nd element in the collection.
Instead, spread the result into an array and iterate that.
Next, these two lines don't make sense:
let menuLinks = document.querySelectorAll('menu__link')
menuLinks = document.getElementsByClassName('funcElemStyle')
document.querySelectorAll('menu__link') would find <menu_link> elements (which would be invalid elements and your page probably doesn't have these). You probably meant to select all elements with that CSS class like this: document.querySelectorAll('.menu__link')?
In the next line you directly overwrite what you just tried to select from the DOM: menuLinks = document.getElementsByClassName('funcElemStyle').
Inside your anonymous event listener function you declare another function removeStyle() which you never call.
These things said, this is probably what you are after:
document
.getElementById('navbar__list')
.addEventListener('click', function(event) {
for (const menuLink of [...document.querySelectorAll('.menu__link')]) {
menuLink.classlist.toggle('funcElemStyle', event.target === menuLink)
}
}
);

Why can't I make "onclick" event handler in my javascript code? [duplicate]

I am using getElementById when I need to clone the div element.
Code:
printHTML( document.getElementById("div_name").cloneNode(true));
Now I need to use getElementsByClassName
CloneNode is not working when using getElementsByClassName.
How can I put class name in here?
Thank's
EDIT:
When I try to use this:
printHTML( $('.dataTables_scroll').clone(true) );
You can see my function:
function printHTML(clonedDive){
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.contentWindow.onunload = function(){
$(".DTTT_container").show("fast");
$("#header_outer").show("fast");
$(".ColVis.TableTools").show("fast");
$("#footer").show("fast");
};
iframe.contentWindow.document.body.appendChild(clonedDive);
iframe.contentWindow.print();
document.body.removeChild(iframe);
}
I am getting an error in this line:
iframe.contentWindow.document.body.appendChild(clonedDive);
This is an error description:
Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
getElementsByClassName gets a nodelist, or an array-like object containing elements if you will, as there can be more than one element with the same class.
getElementsByClassName does this even if only one element matches the class.
You can generally recognize methods like that be the s in getElements, which means it gets multiple elements, i.e. a nodeList.
getElementById only gets one element as ID's are unique.
To get the first element in the nodelist, use bracket notation, like so:
document.getElementsByClassName("div_name")[0].cloneNode(true);
or one could use querySelector, which gets the first matching element only
document.querySelector(".div_name").cloneNode(true);
The jQuery solution would be:
$('.div_name').clone(true);
and to iterate over elements with a certain classname you'd use a loop
var elems = document.getElementsByClassName("div_name");
for ( var i=0; i<elems.length; i++ ) {
printHTML( elems[i].cloneNode(true) );
}
Due to getElementsByClassName returns an object's array, so you have to use for loop to iterates among them, as follows:
for (i = 0; i < document.getElementsByClassName("div_name").length; i++){
printHTML( document.getElementsByClassName("div_name")[i].cloneNode(true));
}
otherwise, if you know the index of the element you have let we say 1
printHTML( document.getElementsByClassName("div_name")[1].cloneNode(true));
This does not work? :
printHTML( document.getElementsByClassName("class_name")[0].cloneNode(true));
You can loop through the elements and clone one by one...
var e = document.getElementsByClassName('div');
for (var i = 0; i < e.length; i += 1) {
// Clone e[i] here
console.log(e[i].cloneNode(true));
}

Adding a listener to an array of elements, need unique arguments for function on each item

I have a bit of HTML generated by PHP in the format of:
<div class=zoomButton>
<input type=hidden name=zoomURL value=*different per instance*>
</div>
I am trying to attach a listener (imageZoom(event, url)) to each of the class "zoomButton" elements, but call it with different arguments for each instance.
i.e.
var zoomButtonArray = document.getElementsByClassName('zoomButton');
for (i=0; i<zoomButtonArray.length; i++)
{
var zoomURL = zoomButtonArray[i].children[0].value;
zoomButtonArray[i].addEventListener("mousedown", function(){imageZoom(event,zoomURL);});
}
however it seems that zoomURL is always the value of the very last element. How can I change my code/approach so that the argument passed to the listener is the correct one, and not the last one in the "zoomButtonArray" array?
Thanks
You need to wrap the event listener in a closure:
function makeEventListenerForZoomURL(zoomURL) {
return function(event) {
imageZoom(event, zoomURL);
}
}
var zoomButtonArray = document.getElementsByClassName('zoomButton');
for (i=0; i<zoomButtonArray.length; i++)
{
zoomButtonArray[i].addEventListener(
"mousedown",
makeEventListenerForZoomURL(zoomButtonArray[i].children[0].value)
);
}
This can also be simplified using the ECMAScript5 forEach:
var zoomButtonArray = document.getElementsByClassName('zoomButton');
zoomButtonArray = Array.prototype.slice.call(zoomButtonArray, 0);
zoomButtonArray.forEach(function(node) {
node.addEventListener("mousedown", function(event) {
imageZoom(event node.children[0].value);
});
});
The reason is that each time the for loop executes a new function is created, this new scope references the variable i but i changes each time the loop iterates. So by the time the event listener runs it looks at the value of i only to find that it is the last value when the for loop ended. By using a closure described above the scope created is unique to each iteration of the loop so that when the event listener finally executes the value of the wrapped variable (zoomURL or node in the examples above) will not have changed.
Here is a good article explaining closures in for loops: http://trephine.org/t/index.php?title=JavaScript_loop_closures
I think you are missing quotes around attributes. I just added quotes and the tested at jsFiddle (Fiddle link in comments) and it's working see to console in developer tool. it is iterating through each element as desired. Console screen shot

jquery name selector not working in ie6

var brands = document.getElementsByName("brand");
for(var brand in brands){
$("input[name='brand']").eq(brand).click(function(){
alert("hello22");
loadDataFN(1);
});
}
This code is not executing in ie6,
Any help would be appreciated.
The problem is likely that you are trying to use a for-in construct to iterate over a numeric array. This often won't give expected results. Use an incremental for loop instead:
var brands = document.getElementsByName("brand");
// Use an incremental for loop to iterate an array
for(var i=0; i<brands.length; i++){
$("input[name='brand']").eq(brands[i]).click(function(){
alert("hello22");
loadDataFN(1);
});
}
However,
after seeing the first part of your code, the loop appears unnecessary. You should only need the following, since you are assigning the same function to all brand inputs.
// These will return the same list of elements (as long as you don't have non-input elements named brand)
// though the jQuery version will return them as jQuery objects
// rather than plain DOM nodes
var brands = document.getElementsByName("brand");
$("input[name='brand']");
Therefore, the getElementsByName() and loop are not necessary.
$("input[name='brand']").click(function() {
alert("hello22");
loadDataFN(1);
});
for-in loops are used for iterating over the properties of an object, not over the elements of an array.
Why don't you write the code without jQuery if this doesn't work?
Something like this:
function getInputByName(name) {
var i, j = document.getElementsByTagName('input').length;
for(i=0;i<j;++i) { // You can also use getAttribute, but maybe it won't work in IE6
if(document.getElementsByTagName('input')[i].name === name) {
return document.getElementsByTagName('input')[i];
}
}
return null;
}
I don't know jQuery, but maybe you can do something like this:
$(getInputByName('brand')).eq(brand).click(function(){
alert("hello22");
loadDataFN(1);
});

Javascript function: nothing executes after a for(x in y) loop?

I can't seem to find the answer to this anywhere...
I have a function that needs to set the bg color of two tables. Only the first table in the function is being affected. Is only one for loop allowed in a function? ...or is the 1st for loop maybe never exiting?
I can pretty much work around this by creating multiple functions but I really want to understand why this behaves the way it does!
Thanks!
Here is my simplified code:
function setColor()
{
//This works
var t1rows=document.getElementById("table1").getElementsByTagName("tr");
var x;
for (x in t1rows)
{
t1rows[x].style.backgroundColor='yellow';
}
//this one does not work
var t2rows=document.getElementById("table2").getElementsByTagName("tr");
var y;
for (y in t2rows)
{
t2rows[y].style.backgroundColor='yellow';
}
}
getElementsByTagName() returns a NodeList object, and for-in will iterate its properties which you don't want. You want to use a straight for loop:
for(var i=0; i < t2rows.length; i++) {
t2rows[i].style.backgroundColor='yellow';
}
You're not getting to the second loop because the first is failing when it tries to access a non-existent style property on one of the NodeList's members.

Categories