Can I auto-generate JS animation functions?

I have a few DOM nodes:

    <span id="purple">A</span>
    <span id="pink">B</span>
    <span id="green">C</span>
    <span id="red">D</span>
    <span id="darkgreen">E</span>

And their animation times are randomized. Because I want each of them to have different durations, I ended up writing a monster script:

      var purple = document.getElementById("purple");
      var pink = document.getElementById("pink");
      var green = document.getElementById("green");
      var red = document.getElementById("red");
      var darkgreen = document.getElementById("darkgreen");

      function blinkpurple() {
        var dur = Math.random() * 2.5 + 1;
        purple.style.animationDuration = `${dur}s`;
      }

      function blinkpink() {
        var dur = Math.random() * 2.5 + 3;
        pink.style.animationDuration = `${dur}s`;
      }
      function blinkgreen() {
        var dur = Math.random() * 2.5 + 3;
        green.style.animationDuration = `${dur}s`;
      }

      function blinkred() {
        var dur = Math.random() * 2.5 + 3;
        red.style.animationDuration = `${dur}s`;
      }

      function blinkdarkgreen() {
        var dur = Math.random() * 2.5 + 3;
        darkgreen.style.animationDuration = `${dur}s`;
      }

      purple.addEventListener("animationiteration", blinkpurple);
      pink.addEventListener("animationiteration", blinkpink);
      green.addEventListener("animationiteration", blinkgreen);
      red.addEventListener("animationiteration", blinkred);
      darkgreen.addEventListener("animationiteration", blinkdarkgreen);

Is there any way to perhaps iterate over the elems (maybe add a class attr like class="neon darkgreen" and change all their animation-durations with only one function?

You can. Here’s some ideas to get you started:

  • To get multiple elements from the page, look up document.querySelectorAll or document.getElementsByClassName. These let you get more than one element at a time the way getElementById does. The result is kind of like an array, but check what the return type is in their documentation if you want to learn about the subtle differences. See if you can code something up that uses this instead of multiple getElementByIds.
  • To perform kind of the same thing multiple times, such as doing the same thing to each element in a list of DOM nodes, look into using loops. A for loop could be suitable here, which gives you a variable that you can use inside the loop body to refer to a different value on each iteration, such as the iteration number or a different element from the list. See if you can write a loop to do something simple to each element that you get from the previous step.
  • Creating a callback function in a loop can be tricky because you might be closing over a loop variable that gets reassigned on each iteration. Use let instead of var to create separate variables for each iteration.
1 Like

Thanks for the guidance! My biggest obstacle is probably randomizing the duration on each step of the loop. Why would I need a callback in the loop?

I’m not sure if this is consistent with the code you posted. Is it about looping through elements and giving each one a random duration, and then each one continuing to animate with that duration going forward? The code was something else, which looks like it would set a new random duration for each iteration of the animation, for each element.

It’s a way to do it, to have a variable that you can use inside the animationiteration callback that refers to the element that’s had an animation iteration end. You’re right to ask though; there is another way to get that. When the browser calls your event listener, it passes an event argument. Look up the Event class’s target attribute for information on this.

1 Like

Ah yeah, I just realized - I could very easily randomize the durations once with a loop, but to keep doing it on animationend I would need callbacks. Having animationend in a loop would be the true obstacle.

This should be the equivalent of your code:

HTML (blink class added):

<span id="purple" class="blink">A</span>
<span id="pink" class="blink">B</span>
<span id="green" class="blink">C</span>
<span id="red" class="blink">D</span>
<span id="darkgreen" class="blink">E</span>

JavaScript:

var blinkElements = document.getElementsByClassName('blink');

function setBlink(event) {
  var blinkElement = event.target;
  var duration = Math.random() * 2.5;
  switch (blinkElement.id) {
    case 'purple':
      duration += 1;
      break;
    default:
      duration += 3;
  }
  blinkElement.style.animationDuration = `${duration}s`;
}

var index = blinkElements.length;
while (index--) {
  var blinkElement = blinkElements[index];
  blinkElement.addEventListener('animationiteration', setBlink);
}
1 Like

That’s a pretty interesting of way of breaking from a while loop - by using 0 as false.
Why does the purple one have a shorter duration?

It has a shorter duration in your code, so I just assumed it was on purpose.

1 Like

oh haha, that was a mistake. Thanks for the code! I’ll mess it with a little so I understand how it works and don’t become a cargo coder.

Thanks so much for posting the example! I was able to understand event.target really quickly and even mock up my own example:

<div class="neon one"></div>
<div class="neon two"></div>
var arr = document.getElementsByClassName("neon")

function test(event) {
	console.log(event.target)
	event.target.innerText = Math.round(Math.random() * 10)
}
for (var i = 0; i < arr.length; i++) {
	var el = arr[i]
	arr[i].addEventListener("animationiteration", test)
}
1 Like

Nice! Glad I could help.

1 Like