Why does the last character keeps on printing repeatedly?

  • A+
Category:Languages

I have written the following to animate the text in a div, but I cannot find how does the last character gets printed repeatedly.

var textClass = $(".first-text"); var text = textClass.text(); textClass.text(""); for (var i in text) {   $(textClass).animate({     opacity: 0.25   }, 200, function() {     $(textClass).append(text.charAt(i));   }); }
p:not(:first-child) {   display: none; }  p {   margin: 0 auto;   font-size: 24px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="animate-text">   <p class="first-text">HTML</p><br> </div>

If I try to the alert the value of i or text.charAt(i), I always get the desired output, but when I try to append the same in a div, I always get the same last letter that is printed repeatedly. I cannot find where I am mistaken. I cannot the find the bug in my logic.

If anyone could enlighten me on my mistake in the above code, I would be glad to hear it.

Here is the link to my fiddle where I tried this code.

Thanks in advance.

 


You've stumbled into a bit of learning when it comes to closures. When i loops through, when it eventually gets run inside the function it's only looking at the last character, because that's what i was overwritten to before the first animate() actually fires.

You can counteract this by manually creating a closure yourself, wrapping it in a function and passing it in, to preserve the variable at the time of the loop.

For more information on closures, check out: What is a 'Closure'?

var textClass = $(".first-text"); var text = textClass.text(); textClass.text(""); for (var i in text) {   (function (char) {     $(textClass).animate({       opacity: 0.25     }, 200, function() {       $(textClass).append(text.charAt(char));     });   })(i) }
p:not(:first-child) {   display: none; }  p {   margin: 0 auto;   font-size: 24px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="animate-text">   <p class="first-text">HTML</p><br> </div>

Alternatively, you can use new let or const syntax, which defines i for the scope of the block (Which essentially creates a closure around your if block.)

var textClass = $(".first-text"); var text = textClass.text(); textClass.text(""); for (const i in text) {   $(textClass).animate({     opacity: 0.25   }, 200, function() {     $(textClass).append(text.charAt(i));   }); }
p:not(:first-child) {   display: none; }  p {   margin: 0 auto;   font-size: 24px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="animate-text">   <p class="first-text">HTML</p><br> </div>

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: