Reputation: 2422
i'm trying to make a "console typing effect" with js, and in the next function i take the text of an element, then i use a "for" loop for slicing that text and paste in within with a delay.
After debugging the code in chrome i can see that javascript doesn't run the setTimeout... it just ignores it.
function type() {
var text = document.querySelector('.console-effect').textContent;
for (var i = 0; i <= text.length; i++) {
setTimeout(function() {
document.querySelector('.console-effect').textContent = text.substr(0, i)
}, 50);
}
}
type();
Upvotes: 0
Views: 121
Reputation: 1276
I didn't want to believe @blex, but he was correct about the scope. He was right on both points he made, but the closure stunned me. How have I never encountered this and been forced to puzzle my way out before?
So the idea here is that instead of scheduling a dozen or so calls to your function at the beginning, you only schedule the next call. After that one is called, it sees if it needs to schedule the next.
function type() {
var text = document.querySelector('.console-effect').textContent;
var i = 0;
var typeNext = function() {
++i;
document.querySelector('.console-effect').textContent = text.substr(0, i);
if(i < text.length) {
setTimeout(typeNext, 50);
}
}
setTimeout(typeNext, 50);
}
type();
<span class="console-effect">This is a test</span>
Upvotes: 1
Reputation: 3241
a solution without 50*i, take a look at the css effect too. You problem is setTimeout is executed asynchronously (the 'control flow' doesn't wait for those 50ms), so they are fired all together with value i = text.length (if your text is small enough)
<p class="console-effect">console effects</p>
<script>
function type() {
var i=0;
var t = document.querySelector('.console-effect').textContent;
var fn = setInterval(function() {
print_text(++i,t,fn)
}, 500);
}
function print_text(i,t,fn){
if(i <= t.length){
document.querySelector('.console-effect').textContent = t.substr(0, i)
} else clearInterval(fn)
}
type();
</script>
<style>
@-webkit-keyframes blinker {
from { opacity: 1.0; }
to { opacity: 0.0; }
}
.console-effect:after{
content:'_';
text-decoration: blink;
-webkit-animation-name: blinker;
-webkit-animation-duration: 0.2s;
-webkit-animation-iteration-count:infinite;
-webkit-animation-timing-function:ease-in-out;
-webkit-animation-direction: alternate;
}
</style>
Upvotes: 0
Reputation: 9460
Do something like this.
function type() {
var text = document.querySelector('.console-effect').textContent;
document.querySelector('.console-effect').textContent = '';//clear content
for (var i = 0; i <= text.length; i++) {
setTimeout(function(j) {
document.querySelector('.console-effect').textContent += text[j];// or .charAt(j)
}, 50 * i, i);
// 50 * i sets timeout for each iteration
// i (3rd arg) passes to the inner function
}
}
type();
Better version.
function type(text) {
var textObj = document.querySelector('.console-effect');
for (var i = 0; i <= text.length; i++) {
setTimeout(function(ch) {
textObj.textContent += ch;
}, 50 * i, text[i]);
// 50 * i sets timeout for each iteration
// i (3rd arg) passes to the inner function
}
}
type('.console-effect');
Upvotes: 0
Reputation: 65
var text = document.querySelector('.console-effect').textContent;
var index = 0;
setInterval(function(){
document.querySelector('.console-effect').textContent = text.substr(0, index);
if(index == text.lenght){
clearInterval(this);
}
index++;
},1000);
Upvotes: 0
Reputation: 25634
Your setTimeout
s are all executing at the same time, because the for
loop does not wait for them to execute on each iteration. You have to delay each timeout using a value such as 50*i
.
Then, to preserve the value of i
in this case, you'll need to use a closure. Otherwise, by the time your timeouts come to an end, the loop will be over, and i
will be the final value, for all of them.
var text = document.querySelector('.console-effect').textContent;
for (var i = 0; i <= text.length; i++) {
(function(i) {
setTimeout(function() {
document.querySelector('.console-effect').textContent = text.substr(0, i);
}, 50*i);
})(i);
}
body{background: #333;}
.console-effect{color: #0f0; font-family: monospace; font-size: 2em;}
<div class="console-effect">This is some example text</div>
Upvotes: 3
Reputation: 65
setTimeout run only once(in your case after delay of 50ms).
For this purpose you should use setInterval
Upvotes: -2
Reputation: 342
Is not a good idea to make functions inside a loop in Javascript, I had bad experiences with it.
This code done this way should work correctly:
function type() {
var text = document.querySelector('.console-effect').textContent;
var loopFunc = function(i) {
return function() {
document.querySelector('.console-effect').textContent = text.substr(0, i)
};
};
for (var i = 0; i <= text.length; i++) {
setTimeout(loopFunc(i), 50);
}
}
type();
Upvotes: 1