2

I have 5 blank boxes, and I would like each of them to be filled up in order every 1 second. How do I do this? My JS is wrong and not working.

let blankSquare = document.querySelectorAll(".square");

function fillUp() {
  setInterval(() => {
    blankSquare.classList.add(".fill")
  }, 1000)
}


blankSquare.addEventListener("fillUp");
.square {
  margin: 1vh;
  height: 3vh;
  width: 3vh;
  background-color: none;
  border: solid 2px grey;
}

.fill {
  background-color: grey;
}
<div class="squares">
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
</div>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313

2 Answers2

3

Staggered animation using CSS

Here's an example where you don't necessarily need JavaScript, but simply a style attribute containing a CSS var (variable) like style="--anim=N" where N is an index from 0 to N, and use CSS calc() for the animation-delay property:

.square {
  height: 3vh;
  aspect-ratio: 1;
  border: solid 2px grey;
  animation: fill 0.3s calc(var(--anim) * 1s) forwards;
}

@keyframes fill {
  to { background: grey; }
}
<div class="squares">
  <div class="square" style="--anim:0"></div>
  <div class="square" style="--anim:1"></div>
  <div class="square" style="--anim:2"></div>
  <div class="square" style="--anim:3"></div>
  <div class="square" style="--anim:4"></div>
</div>

If you don't want to hardcode manually the style attribute, simply add this JS:

const ELS = (sel, el) => (el || document).querySelectorAll(sel);
ELS(".square").forEach((el, i) => el.style.setProperty("--anim", i));

Staggered animation using JavaScript

Given a function in JavaScript with which you can decide when to start your staggered animation:

Use setTimeout instead of setInterval inside a recursive fillUp function which accepts as arguments 1: a collection of elements, 2: a start index.
An incremented index is passed for every next recursive function call

const fillUp = (els, i = 0) => {
  els[i].classList.add("fill");
  i += 1;
  if (i < els.length) setTimeout(() => fillUp(els, i), 1000);
};

fillUp(document.querySelectorAll(".square"));
.square {
  height: 3vh;
  aspect-ratio: 1;
  border: solid 2px grey;
}

.fill {
  background-color: grey;
}
<div class="squares">
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
</div>

Related articles / resources:

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • 1
    Learn something new every day. I haven't kept up with css animations as much as I should and the `--anim` is very cool. Is the name of that variable arbitrary so `style="--foo:3` and `calc(var(--foo) * 1s) ` would work? – charlietfl Dec 24 '21 at 22:08
  • @charlietfl exactly, it's absolutely arbitrary. `--this-is-my-index`, `--foo`, whatever. It just has to be prefixed with `--` and be used with `var()` like i.e: `--red-deep: #c11; color: var(--red-deep);` https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties and https://developer.mozilla.org/en-US/docs/Web/CSS/var() – Roko C. Buljan Dec 24 '21 at 22:15
  • Seemed like it, thanks Roko – charlietfl Dec 24 '21 at 22:16
0

You can store the index of the item in a variable, and increment it inside the interval.

let blankSquare = document.querySelectorAll(".square");

let index = 0;

function fillUp() {
  setInterval(() => {
    blankSquare[index++].classList.add("fill")
  }, 1000)
}


fillUp()
.square {
  margin: 1vh;
  height: 3vh;
  width: 3vh;
  background-color: none;
  border: solid 2px grey;
}

.fill {
  background-color: grey;
}
<div class="squares">

  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>
  <div class="square"></div>

</div>
Spectric
  • 30,714
  • 6
  • 20
  • 43
  • 1
    This is inevitably going to run into a `cannot access property "classList" of undefined` exception (exact error message depending on browser). – connexo Dec 24 '21 at 22:03