trying to write character by character with delay using setTimeout - javascript

I'm trying to make a function to write some text character by character, with some delay, I've expected this would work but it doesn't, after 1000ms the word is written completely, if anyone can help me to fix this or show me a better way to do this it would be really great, here is my code:
const container = document.getElementById('typer')
function typer(text) {
for(let i = 0; i <= text.length; i++) {
setInterval(() => {
container.innerHTML = text.substr(0, i)
}, 1000)
}
}
let x = "hello"
typer(x)

You need to use one setInterval() that runs each 1000ms, and when it finishes the task you need to stop it by using clearInterval, check this link to understand better how to use it.
Try this code:
const container = document.getElementById('typer')
function typer(text) {
const textLength = text.length
let i = 0
const writter = setInterval(() => {
if ( i === textLength ) {
clearInterval(writter)
} else {
container.innerHTML += text[i]
i++
}
}, 1000)
}
let x = "hello"
typer(x)
<p id="typer"></p>

setInterval is already act as a loop. So you can do as simple as below
const container = document.getElementById('typer');
function typer(text) {
let counter = 0, my_delay = setInterval(() => {
if(counter<text.length) container.innerHTML += text[counter++];
else clearInterval(my_delay);
}, 1000);
}
typer("hello");
<p id="typer"></p>

She setInterval function runs after set time, but does not block following code from executing before the timer is done.
try:
let i=0;//counter variable
setInterval(() => {// runs every second.
if (i <= text.length()){//run if the counter below the string length
container.innerHTML = text.substr(0, i);
}
i++;//increment counter
}, 1000)

actually you are doing it partially right but when the for loop runs it set all the setintervals for 1000 millisecond so all of them fired after 1 second that's why we always see hello
what i am done is i put fe for loop inside setinterval and added a setTimeout and each timeout will fire only after 500 millisecond after than the previous one,
the const sum = (text.length * (text.length +1)) / 2; is used to find sum of all i values which is used inside the for loop then only we will be able to fire setInterval only after the hello completed hope this helps you
but actually there is a simple css way is there you must try that.
https://css-tricks.com/snippets/css/typewriter-effect/
const container = document.getElementById('typer')
function typer(text) {
const sum = (text.length * (text.length +1)) / 2;
setInterval(()=>{
for(let i = 0; i <= text.length; i++) {
setTimeout(() => {
container.innerHTML = text.substr(0, i)
}, 500+500*i)
}
},sum*500)
}
let x = "hello"
typer(x)
<span id="typer"></span>

Related

setInterval vs setTimeout which is better to run a loop at a specified speed

var speed = 200,
len = 0;
if (e in know) {
function typing() {
if (len < know[e].length) {
if (len === know[e].length - 1) {
t.innerHTML = know[e];
g = d.getAttribute("data-bottom") ? d.getAttribute("data-bottom") : 0;
if (parseInt(g) < ((d.scrollHeight - t.offsetTop) - p.clientHeight) + 10) d.scrollTo(0, d.scrollHeight);
else alert("new message");
}
len++;
setTimeout(typing, speed);
}
}
typing();
setTimeout vs
setInterval code...
var setIn = setInterval(() => {
if (len < know[e].length) {
if (len === know[e].length - 1) {
clearInterval(setIn);
t.innerHTML = know[e];
g = d.getAttribute("data-bottom") ? d.getAttribute("data-bottom") : 0;
if (parseInt(g) < ((d.scrollHeight - t.offsetTop) - p.clientHeight) + 10) d.scrollTo(0, d.scrollHeight);
else alert("new message");
}
len++;
}
}, speed)
I'm trying to make a reply bot that starts typing after 1 seconds and that will reply back at the speed of the String.length and they both did the same thing but someone once told me using setTimeout is bad practice, but I need them to run that exact way. If there is another work around for it I'm open to all suggestions, Thanks in advance.
You can use async/await to emulate typing and create delays:
//
// Async timeout function
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
//
// Bot typing function
const runBot = text => {
// Use promises
return new Promise(async (resolve, reject) => {
// DOM
const div = document.getElementById('text');
// Create chars array
const chars = text.split('');
// Wait 1 sec before typing
await timeout(1000);
// Type text
while(chars.length) {
// Put one char at iteration
div.innerHTML += chars.shift();
// Wait some amount
await timeout(100);
}
// Add new line to the text box
div.innerHTML += '<br>'
// Finish function
return resolve();
});
}
//
// Text typing runner and button controller
const typeText = async text => {
// DOM
const button = document.querySelector('button');
// Disable button
button.disabled = true;
// Run bot and wait till it finishes
await runBot(text);
// Enable button
button.disabled = false;
}
<div id="text" style="padding:10px;border:1px solid #000"></div>
<button onClick="typeText('This is demo text!')">Start bot</button>
You can have multiple approaches:
setInterval
setTimeout
window.requestAnimationFrame
If you need to wait for 1 second, then you must have a setTimeout and inside, I encourage you to use the 3rd option if you want to save performance, but combining setTimeout with setInterval is an available option. The use of them is not a bad practice in general, just in particular cases.

infinitetly loop array with setTimeout javascript

I have an array of strings that I'm passing to a for loop with a setTimeout inside.
I've tried to for(; ;) and while(true) the function but this just causes the browser to crash.
I've also tried adding an if statement inside the function
if (x == links.length) { loopUrls() }
Which works kind of but skips the entire interval section of the function, cycling the entire array infinitely with no wait.
function loopUrls() {
for (var x = 0, ln = links.length; x < ln; x++) {
setTimeout(function (y) {
document.getElementById('ifURLS').src = links[y];
},x * 500,x);
}
};
loopUrls();
The aim is to have a list of urls that infinitely assigned to the iframe to show reports on a sreen. (the time interval is just small for testing purposes)
There is no need to have multiple timeouts. You can achieve this using setInterval which executes a function every x amount of time. Then in that funciton you keep a state of the current url so you can show the next time the function is run
// creates a function that iterates over the array and every time its called returns the next element
function createIterator(array){
let sourceArray = [...array];
// starts at the end so it resets automatically
let topIndex = sourceArray.length;
let currentIndex = topIndex - 1;
return function (){
currentIndex++;
// if the limit is reached, its reseated
if (currentIndex === topIndex) {
currentIndex = 0;
}
return sourceArray[currentIndex];
}
}
// your urls
let urls = ["a", "b", "c"];
// uses the function to create the iterator
let iterator = createIterator(urls);
setInterval(function() {
let currentUrl = iterator();
document.getElementById('ifURLS').src = currentUrl;
},1000);
No need for loops, as the iterator handles seamlessly
If I understand correctly, you want for every link to have some function that is being executed every X milliseconds.
In order to achieve this I think it's better to use setInterval.
You can do something like:
links.forEach((link) => {
setInterval(() => {
//do something every X milliseconds
}, X)
})
a way to do that :
// const links = [ ... ]
const myImg = document.queryselector('#ifURLS')
function LoadImg()
{
let
indx = 0
, let refIntv =
setInterval( ()=>
{
myImg.src = links[indx++]
if (indx >= links.length)
clearInterval( refIntv )
}
, 5000 ) // 5s delay
}

text.length is not defined in Javascript, but text is defined

I have a code that is supposed to show some text in a typewriter effect, but the console says that length is not defined. I do not know what to do, please help me.
function typeWriter(theX) {
var i = 0;
text = theX;
var leng = text.length;
if (i < leng) {
document.getElementById("theTexts").innerHTML += text.charAt(i);
setTimeout(typeWriter, speed);
}
}
var speed = 50;
typeWriter('Frog-E Console');
var speed = 60;
typeWriter('Booting up');
By calling setTimeout(typeWriter, speed) you are calling typeWriter without passing it anything. This function expect one argument (a string), so you need to pass it :
function typeWriter(theX) {
var i = 0;
text = theX;
var leng = text.length;
if (i < leng) {
document.getElementById("theTexts").innerHTML += text.charAt(i);
setTimeout(typeWriter.bind(this, text), speed);
}
}
var speed = 50;
typeWriter('Frog-E Console');
var speed = 60;
typeWriter('Booting up');
<div id="theTexts"></div>
However this doesn't produce the expected result, you should do something like this :
function typeWriter(text) {
const char = text[0]
theTexts.innerHTML += char;
if (text.length > 1) setTimeout(typeWriter.bind(this, text.substring(1)), speed);
}
var speed = 50;
typeWriter('Frog-E Console');
<div id="theTexts"></div>
Some issues:
When setTimeout calls your typeWriter function, it won't pass it a value for theX so you end up with theX being undefined, so text is undefined, so you get the error.
Your second typeWriter call won't wait until the previous one finishes before starting, so they'll stomp on each other. You can fix that by having typeWriter return a promise it fulfills when it's done.
Your text variable is undeclared, making it what I call an implicit global; we don't need that variable, so let's just ditch it.
Probably best to pass speed in rather than using a global.
Rather than having setTimeout call typeWriter directly, I'd probably use an inner function. See comments:
// Accept both text and speed
function typeWriter(text, speed) {
// Return a promise we'll fulfill when done
return new Promise(resolve => {
// Start out showing nothing
let currentLength = 0;
// Start the process by showing the first character
tick();
function tick() {
// Add one to what we're showing
++currentLength;
// Show it
document.getElementById("theTexts").innerHTML = text.substring(0, currentLength);
// Done?
if (currentLength === text.length) {
// Yes, fulfill the promise
resolve();
} else {
// No, keep going after a delay
setTimeout(tick, speed);
}
}
});
}
// Do the first one, wait for it to finish, then do the next
typeWriter("Frog-E Console", 50)
.then(() => {
return typeWriter("Booting up", 60);
});
<div id="theTexts"></div>

How am I able to add timing to javascript that alters text every second?

I would like this code to count up from 0 to 940 (very fast) and alter the text every time it updates
Here's my code (inside my head tag):
<script type="text/javascript">
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function onLoad(){
var x = document.getElementById("numberID");
var n = 940;
var text = "";
for(i = 0;i < n + 1;i++){
text = i;
x.innerHTML = text;
sleep(1);
}
}
</script>
At the moment, it just waits a second then displays '940' on screen and doesn't display it counting up.
Any help would be appreciated, thanks!
Here's the code I recently put in, still doesn't work:
const x = document.getElementById("numberID");
function newFrame(duration, start = performance.now()) {
requestAnimationFrame((now) => {
const elapsed = now - start;
x.innerText = Math.max(0, Math.min(duration,
Math.round(elapsed)));
if(elapsed < duration)
newFrame(duration, start);
})
}
}
newFrame(940);
Using a while loop to "sleep" is going to block the page's thread and nothing else can happen in the meantime. This is considered bad practice.
setTimeout guarantees that at least the defined time has passed, but can take (much) longer. This is imprecise, and especially bad for shorter intervals. Same with setInterval. They're also not recommended for callbacks that involve updating the DOM.
What you need to do is use a requestAnimationFrame.
function newFrame(duration, start = performance.now()) {
requestAnimationFrame((now) => {
const elapsed = now - start
console.log(`time passed: ${elapsed} ms`)
if(elapsed < duration)
newFrame(duration, start)
})
}
newFrame(940)
In your specific case, I'd replace the console.log statement put there for didactic purposes, with something along the lines of:
x.innerText = Math.max(0, Math.min(duration, Math.round(elapsed)))
Here's what that would look like:
const x = document.getElementById("numberID")
function newFrame(duration, start = performance.now()) {
requestAnimationFrame((now) => {
const elapsed = now - start
x.innerText = Math.max(0, Math.min(duration, Math.round(elapsed)))
if(elapsed < duration)
newFrame(duration, start)
})
}
newFrame(940)
<span id="numberID"></span>
The sleep function is not doing anything, what you need is a setTimeout to display the text at every x milliseconds.
Something like the below will work.
let x = null;
let timeout = null;
const changeText = (text) => {
x.innerHTML = text;
clearTimeout(timeout);
}
function onLoad() {
x = document.getElementById("numberID");
const n = 940;
const t = .01; // in seconds
for( let i = 0; i <= n; i++) {
timeout = setTimeout( () => changeText((i+1).toString()), (t * i) * 1000);
}
}
onLoad();
<span id="numberID"></span>

How to create pause or delay in FOR loop?

I am working on a website, where I need to create a pause or delay.
So please tell me How to create pause or delay in for loop in javascript or jQuery
This is a test example
var s = document.getElementById("div1");
for (i = 0; i < 10; i++) {
s.innerHTML = s.innerHTML + i.toString();
//create a pause of 2 seconds.
}
You can't use a delay in the function, because then the change that you do to the element would not show up until you exit the function.
Use the setTimeout to run pieces of code at a later time:
var s = document.getElementById("div1");
for (i = 0; i < 10; i++) {
// create a closure to preserve the value of "i"
(function(i){
window.setTimeout(function(){
s.innerHTML = s.innerHTML + i.toString();
}, i * 2000);
}(i));
}
var wonderfulFunction = function(i) {
var s = document.getElementById("div1"); //you could pass this element as a parameter as well
i = i || 0;
if(i < 10) {
s.innerHTML = s.innerHTML + i.toString();
i++;
//create a pause of 2 seconds.
setTimeout(function() { wonderfulFunction(i) }, 2000);
}
}
//first call
wonderfulFunction(); //or wonderfulFunction(0);
You can't pause javascript code, the whole language is made to work with events, the solution I provided let's you execute the function with some delay, but the execution never stops.
I tried all one, but I think this code is better one, it is very simple code.
var s = document.getElementById("div1");
var i = 0;
setInterval(function () {s.innerHTML = s.innerHTML + i.toString(); i++;}, 2000);
if you want to create pause or delay in FOR loop,the only real method is
while (true) {
if( new Date()-startTime >= 2000) {
break;
}
}
the startTime is the time before you run the while
but this method will cause the browsers become very slow
It is impossible to directly pause a Javascript function within a for loop then later resume at that point.
This is how you should do it
var i = 0;
setTimeout(function() {
s.innerHTML = s.innerHTML + i.toString();
i++;
},2000);
The following code is an example of pseudo-multithreading that you can do in JS, it's roughly an example of how you can delay each iteration of a loop:
var counter = 0;
// A single iteration of your loop
// log the current value of counter as an example
// then wait before doing the next iteration
function printCounter() {
console.log(counter);
counter++;
if (counter < 10)
setTimeout(printCounter, 1000);
}
// Start the loop
printCounter();
While several of the other answers would work, I find the code to be less elegant. The Frame.js library was designed to solve this problem exactly. Using Frame you could do it like this:
var s = document.getElementById("div1");
for (i = 0; i < 10; i++) {
Frame(2000, function(callback){ // each iteration would pause by 2 secs
s.innerHTML = s.innerHTML + i.toString();
callback();
});
}
Frame.start();
In this case, it is nearly the same as the examples that use setTimeout, but Frame offers a lot of advantages, especially if the you are trying to do multiple or nested timeouts, or have a larger JS application that the timeouts need to work within.
I am executing a function where I need access to the outside object properties. So, the closure in Guffa solution doesn't work for me. I found a variation of nicosantangelo solution by simply wrapping the setTimeout in an if statement so it doesn't run forever.
var i = 0;
function test(){
rootObj.arrayOfObj[i].someFunction();
i++;
if( i < rootObj.arrayOfObj.length ){
setTimeout(test, 50 ); //50ms delay
}
}
test();
The way I found was to simply use setInterval() to loop instead. Here's my code example :
var i = 0;
var inte = setInterval(() => {
doSomething();
if (i == 9) clearInterval(inte);
i++;
}, 1000);
function doSomething() {
console.log(i);
};
This loops from 0 to 9 waiting 1 second in between each iteration.
Output :
0 1 2 3 4 5 6 7 8 9
It is not possible to pause a loop. However you can delay the execution of code fragments with the setTimeout() function. It would not make a lot of sense to pause the entire execution anyway.
I am using while loop and check the pause variable to check the user pause/resume the code.
var pause = false;
(async () => {
for (let index = 0; index < 1000; index++) {
while (pause) {
await new Promise((res) => setTimeout(res, 1000));
console.log("waiting");
}
await new Promise((res) => setTimeout(res, 1000));
console.log(index);
}
})();
const pausefunc = async () => {
pause = true;
};
const playfunc = () => {
pause = false;
};
<button onclick="playfunc()">Play</button>
<button onclick="pausefunc()">Pause</button>
I used a do...while loop to put a delay in my code for a modal dialog that was closing too quickly.
your stuff....
var tNow = Date.now();
var dateDiff = 0;
do {
dateDiff = Date.now() - tNow;
} while (dateDiff < 1000); //milliseconds - 2000 = 2 seconds
your stuff....

Categories

Resources