1

I have a text "HELLO" and I want to loop through every letter and animate it so it fades in and out.Here's my code.

EDIT: I put the answer in the snippet to see it in action.

Code:

$(document).ready(function() {
  var $letters = $('p[id^="letter-"');
  $letters.each(function(index) {
    $(this).css({
      'animation': 'pulse 500ms ' + index * 500 + 'ms' + ' linear'
    })
  });
});
html,
body {
  font-size: 45px;
}

p {
  position: absolute;
  left: 400px;
  top: 100px;
  color: rgba(0, 0, 0, 0);
}

@keyframes pulse {
  0% {
    color: rgba(0, 0, 0, 0);
  }
  25% {
    color: rgba(0, 0, 0, 0.5);
  }
  50% {
    color: rgba(0, 0, 0, 1);
  }
  75% {
    color: rgba(0, 0, 0, 0.5);
  }
  100% {
    color: rgba(0, 0, 0, 0);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id='letter-0'>H</p>
<p id='letter-1'>E</p>
<p id='letter-2'>L</p>
<p id='letter-3'>L</p>
<p id='letter-4'>O</p>

And here's a link to a pen. Instead of doing the animation one letter at a time, it's animating the whole thing at once.How can this be fixed? Shouldn't a loop finish executing all the commands and then move on to the next step? Maybe there's a better approach to this that I don't know of?

Cœur
  • 32,421
  • 21
  • 173
  • 232
  • 4
    The loop isn't doing animation, it's just changing CSS. The browser performs the animation when the Javascript finishes. – Barmar Jul 03 '17 at 20:58
  • To expand on @Barmar's, the loop is executing within milliseconds, so the classes are added so closely the animations appear to start at the same time. It's not waiting for the first animation to finish before starting the next. – Brett DeWoody Jul 03 '17 at 21:00
  • @Barmar So the loop assigns the css to all the elements and then the browser executes it all at once ? –  Jul 03 '17 at 21:00
  • Yes. If you want to start animations at different times, you could use `setTimeout` or `setInterval` to delay changing each CSS. – Barmar Jul 03 '17 at 21:01
  • @BrettDeWoody Oh I get it. So is there any way of doing this other than setting the css by hand ? –  Jul 03 '17 at 21:01
  • You could also use jQuery `.animate()`, and use the callback function for element N to start the animation for element N+1. – Barmar Jul 03 '17 at 21:01

2 Answers2

2

Use animation-delay in combination with your loop variable:

$(document).ready(function() {
  for (var i = 0; i < 5; i++) {
    $('#' + i).css({
      'animation': 'pulse 0.5s linear',
      'animation-delay': i + 's'
    })
  }
});
html,
body {
  font-size: 45px;
}

p {
  position: absolute;
  left: 400px;
  top: 100px;
  color: rgba(0, 0, 0, 0);
}

@keyframes pulse {
  0% {
    color: rgba(0, 0, 0, 0);
  }
  25% {
    color: rgba(0, 0, 0, 0.5);
  }
  50% {
    color: rgba(0, 0, 0, 1);
  }
  75% {
    color: rgba(0, 0, 0, 0.5);
  }
  100% {
    color: rgba(0, 0, 0, 0);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id='0'>H</p>
<p id='1'>E</p>
<p id='2'>L</p>
<p id='3'>L</p>
<p id='4'>O</p>
Paul Abbott
  • 6,915
  • 3
  • 25
  • 42
0

One thing you should definitely correct is the naming of your ids. A single number is not a valid id.

A better option might be to name them something like letter-0, letter-1, etc.

Next, you can use the animation-delay property to offset the start of each animation. We'll use the shorthand animation property. To do this, we'll find each element with an id starting with letter-, then loop through them. For each consecutive letter we'll add the animation, and include an animation delay of 500ms * its position index in the string. For example, the first letter (index 0) will have an animation delay of 0ms. The second letter (index 1) will have an animation delay of 500ms, and so on.

$(document).ready(function() {
  var $letters = $('p[id^="letter-"');
  $letters.each(function(index) {
    $(this).css({
      'animation': 'pulse 500ms ' + index * 500 + 'ms' + ' linear'
    })
  });
});
html,
body {
  font-size: 45px;
}

p {
  position: absolute;
  left: 400px;
  top: 100px;
  color: rgba(0, 0, 0, 0);
}

@keyframes pulse {
  0% {
    color: rgba(0, 0, 0, 0);
  }
  25% {
    color: rgba(0, 0, 0, 0.5);
  }
  50% {
    color: rgba(0, 0, 0, 1);
  }
  75% {
    color: rgba(0, 0, 0, 0.5);
  }
  100% {
    color: rgba(0, 0, 0, 0);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id='letter-0'>H</p>
<p id='letter-1'>E</p>
<p id='letter-2'>L</p>
<p id='letter-3'>L</p>
<p id='letter-4'>O</p>
Brett DeWoody
  • 50,328
  • 25
  • 121
  • 168
  • This is actually what I had in mind.I just thought that the normal loop will work like python and wait until one animation stops to start the next one. –  Jul 03 '17 at 21:14
  • I improved the solution, including a note about the `id`s you're using. – Brett DeWoody Jul 03 '17 at 21:23
  • This is great.The only thing I don't get is where the value of the "index" argument is coming from.Does jQuery automatically assign "$letters" to it because it's the variable that the function is being call on ? –  Jul 03 '17 at 21:33
  • 1
    The [jQuery `.each` method](https://api.jquery.com/each/) is passed the current loop iteration each time its called. In this case, `each()` is being called on the `$letters` array, and I'm passing a name (`index`) to the callback function to use as the variable name for the iteration index. On the first iteration of the `each()` loop, `index` is `0`, on the 2nd loop `index` is `1`, etc. – Brett DeWoody Jul 03 '17 at 21:38