0

I'm trying to remove all elements from the DOM of a specific class, after a specific timeout (waiting for an animation to finish).

I've tried using a for loop both on a live list (getElmentsByClassName) as well as in node list (querySelectorAll). In both cases, only one element is removed.

function removeElems() {
  elems = document.querySelectorAll('.header');
  for (e of elems) {
    setTimeout(function() {e.remove();}, 1000);
  }
}
<div id="container">
  <div class="header">1</div>
  <div class="header">2</div>
  <div class="header">3</div>
  <div class="header">4</div>
  <div class="header">5</div>
  <div class="header">6</div>
</div>

I'm not sure why it's not working, and am looking for an explanation as to why it's executing in the manner it does and not actually removing all the elements.

Thank you!

(If you're kind enough to offer examples of working code, vanilla JS only please.)

Eddie
  • 26,593
  • 6
  • 36
  • 58
user2442072
  • 437
  • 2
  • 7
  • 16
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) - `e` is declared globally and overwritten with each iteration. When your `remove()` executes, the loop has long-since finished, and left `e` equal to whatever it was set to last. – Tyler Roper Apr 09 '19 at 02:42

4 Answers4

2

One option is to pass the variable e (the element) on the setTimeout as the third parameter.

function removeElems() {
  let elems = document.querySelectorAll('.header');
  for (const e of elems) {
    setTimeout(function(o) {        //2. Receive the element in variable o
      o.remove();                   //3. Remove the o variable
    }, 1000, e);                    //1. Pass the element here
  }
}

removeElems();
<div id="container">
  <div class="header">1</div>
  <div class="header">2</div>
  <div class="header">3</div>
  <div class="header">4</div>
  <div class="header">5</div>
  <div class="header">6</div>
</div>
Eddie
  • 26,593
  • 6
  • 36
  • 58
2

Well, if you declare your variable properly with let or const, it'll be fixed because e will be scoped to each iteration of the loop.

function removeElems() {
  const elems = document.querySelectorAll('.header');
  for (const e of elems) {
    setTimeout(function() {e.remove();}, 1000);
  }
}

However, these multiple setTimeout calls are redundant. Just move your loop to a single callback.

function removeElems() {
  const elems = document.querySelectorAll('.header');
  setTimeout(function() {
    for (const e of elems) {
      e.remove();
    }
  }, 1000);
}
ziggy wiggy
  • 1,039
  • 6
  • 6
0

You can use forEach to loop through all the headers

function removeElems() {
  const parent = document.getElementById('container');
  const elems = document.querySelectorAll('.header');
  elems.forEach((node) => {
    setTimeout(function() {
    parent.removeChild(node);
   }, 1000);
 });
}
Julius Guevarra
  • 690
  • 1
  • 7
  • 19
0

See the current code as you posted:

function removeElems() {
    elems = document.querySelectorAll('.header');
    for(e of elems) {
        setTimeout(function() { 
          console.log(e);
          e.remove(); 
        }, 1000 );
    }
}

removeElems();
<div id="container">
  <div class="header">1</div>
  <div class="header">2</div>
  <div class="header">3</div>
  <div class="header">4</div>
  <div class="header">5</div>
  <div class="header">6</div>
</div>

Notice that e is calling only last header.

You can use the following:

function removeElems() {
    elems = document.querySelectorAll('.header');
    for(let i = 0; i < elems.length; i++) {
        setTimeout(function() { 
          elems[i].remove(); 
        }, 1000 );
    }
}

removeElems();
<div id="container">
  <div class="header">1</div>
  <div class="header">2</div>
  <div class="header">3</div>
  <div class="header">4</div>
  <div class="header">5</div>
  <div class="header">6</div>
</div>

to remove all after delay. Or:

var i = 0;
var elems = document.querySelectorAll('.header');
function removeElems() {
    if( i < elems.length) {
        setTimeout(function() { 
         elems[i].remove();
         i++;
         removeElems();
        }, 1000 );
    }
}

removeElems();
<div id="container">
  <div class="header">1</div>
  <div class="header">2</div>
  <div class="header">3</div>
  <div class="header">4</div>
  <div class="header">5</div>
  <div class="header">6</div>
</div>

to remove element 1 by 1 starting from the first.

Or from last:

var elems = document.querySelectorAll('.header');
var i = elems.length-1;
function removeElems() {
    if( i >= 0) {
        setTimeout(function() { 
         elems[i].remove();
         i--;
         removeElems();
        }, 1000 );
    }
}

removeElems();
<div id="container">
  <div class="header">1</div>
  <div class="header">2</div>
  <div class="header">3</div>
  <div class="header">4</div>
  <div class="header">5</div>
  <div class="header">6</div>
</div>
Shinjo
  • 677
  • 6
  • 22