For loop with increasing setTimeout interval - javascript

Hi I'm trying to get a for loop to run 3 times with an increasing interval each time. I'd like the loop to output text to the console each time it runs but I can't stop the loop from running all at once and showing done, finished, and done, then finished twice. Code:
function tellMeWhenDone () {
for(var i=0; i<3; i++) {
if (i === 0)
var text = console.log('done');
else if (i === 1)
var text = console.log('and done');
else (i === 2)
var text = console.log('finished');
time(i);
}
}
function time (i){
setInterval(function(text){
return text;
}, 1000*(i+1))
}
tellMeWhenDone();
Any help would be greatly appreciated! Thank you.

setInterval sets up a recurring event. setTimeout will only fire once.
To create three timeouts, 1, 2 and 3 seconds - you may use:
setTimeout(function(){console.log('done')},1000);
setTimeout(function(){console.log('and done')},2000);
setTimeout(function(){console.log('finished')},3000);
Notice there is no loop. Each setTimeout executes and when the timeout is reached, the console.log statement is run.
Your for loop is executing completely before the setInterval call.

Remove the condition in else loop
function tellMeWhenDone() {
for (var i = 0; i < 3; i++) {
if (i === 0)
var text = console.log('done');
else if (i === 1)
var text = console.log('and done');
else
var text = console.log('finished');
time(i);
}
}
function time(i) {
setInterval(function(text) {
return text;
}, 1000 * (i + 1))
}
tellMeWhenDone();

Try with this..I hope its resolved your prblm
function tellMeWhenDone () {
for(var i=0; i<3; i++) {
if (i === 0)
var text = console.log('done');
else if (i === 1)
var text = console.log('and done');
else if (i === 2)
var text = console.log('finished');
time(i);
}
}
function time (i){
setInterval(function(text){
return text;
}, 1000*(i+1))
}
tellMeWhenDone();

You may try with callback:
function log(msg) {
document.getElementById('logger').innerHTML += '<br/>' + msg;
}
function on_complete() {
log('all done, when = ' + Date.now());
}
for (var i = 1, max = 5; max >= i; i++) {
(function(x) {
setTimeout(function() {
log('loop, x = ' + x + ', when = ' + Date.now());
if (max === x)
on_complete();
}, 1000 * x); //increasing timeout
})(i);
}
<pre id='logger'>Running code...</pre>

Related

How to wait for function to stop before calling it again

I have had trouble trying to get my function to finish before calling it again. This is my code now:
function TypeAnimation(str, time)
{
var text = document.getElementById('loading');
for (let i = 0; i < str.length; i++) {
let k = i;
setTimeout(function(){
text.innerHTML += str.charAt(i);
}, time * (k + 1));
}
}
function StartGame()
{
TypeAnimation('first line of text', 50)
TypeAnimation('next line of text but slower', 100)
}
window.onload = StartGame;
When doing this I get the output as finrset xlitne olf itenxte of text but slower.
I have tried promises by using the following code:
function TypeAnimation(str, time)
{
return new Promise((resolve) => {
var text = document.getElementById('loading');
for (let i = 0; i < str.length; i++) {
let k = i;
setTimeout(function(){
text.innerHTML += str.charAt(i);
}, time * (k + 1));
}
resolve();
});
}
function StartGame()
{
TypeAnimation('first line of text', 50).then(TypeAnimation('next line of text but slower', 100))
}
window.onload = StartGame;
But when I do this I still get the same result
On top of this, I also tried the same thing but with with a different StartGame()
async function StartGame()
{
await TypeAnimation('first line of text', 50)
TypeAnimation('next line of text but slower', 100)
}
Once again, the same issue arises and I get the same output
If it helps, this is all created with an electron js app.
Your code doesn't work, because you resolve the promise before timeouts even happen
The approach like this will make it easier
async function TypeAnimation(str, time) {
const text = document.getElementById('loading');
for (let i = 0; i < str.length; i++) {
text.innerHTML += str.charAt(i);
await wait(time)
}
}
async function StartGame() {
await TypeAnimation('first line of text', 50)
await TypeAnimation('next line of text but slower', 100)
}
StartGame()
function wait(time) {
return new Promise(r => setTimeout(r, time))
}
<span id="loading"></span>

How do I turn this into a callback function?

I'm new to Javascript, and callbacks are blowing my mind a little at the moment. How do I turn the teletyperDiologue function into a callback? The main reason is I want the teletyper to finish it's job before the displayOut function finishes. Thank you in advance for your help.
function displayOut() {
// images
document.getElementById("imgBox").style.backgroundImage = "url(" + db.rooms[roomLoc].roomImg + ")";
// Diologue box
diologueBox.innerHTML = ""; // Clear Box
teleTyperDiologue(db.rooms[roomLoc].description +
" The room contains: " +
(function() {
let x = "";
for (let i = 0; i < db.items.length; i++) {
if (db.items[i].location === roomLoc && db.items[i].hidden === false) {
x += db.items[i].name + ", "
}
}
x = x.slice(0, x.length -2);
if (x === "") {
x = " nothing of special interest";
}
return x;
})()
+ ".");
};
// Teletyper for Diologue Box
function teleTyperDiologue(string) {
for (let i = 0; i < string.length; i++) {
setTimeout(function() {
diologueBox.innerHTML += string.slice(i, i + 1);
}, 5 * i);
}
}
As an example:
function test(a) { a(); }
function x() { alert('hello'); }
test(x);
in your case:
function displayOut(callback) {
// images
document.getElementById("imgBox").style.backgroundImage = "url(" + db.rooms[roomLoc].roomImg + ")";
// Diologue box
diologueBox.innerHTML = ""; // Clear Box
callback(db.rooms[roomLoc].description +
" The room contains: " +
(function() {
let x = "";
for (let i = 0; i < db.items.length; i++) {
if (db.items[i].location === roomLoc && db.items[i].hidden === false) {
x += db.items[i].name + ", "
}
}
x = x.slice(0, x.length -2);
if (x === "") {
x = " nothing of special interest";
}
return x;
})()
+ ".");
};
displayOut(teleTyperDiologue);
You can pass functions around like variables and return them in functions and use them in other functions. So, when you pass a callback function as an argument to another function, you only need to pass the function definition.
See example below.
function displayOut() {
console.log("Display Out running...");
}
function teleTyperDiologue(stringParam, callback) {
console.log("Running teleTyper with string param passed of: ", stringParam);
callback();
}
teleTyperDiologue ("Test string", displayOut);

Progressively add strings from array to html element using timeout

I'm trying to add a string to a html element every 500ms using a for loop to pass the string to a function which updates the target element.
I'm not sure if i'm approaching this the right way or if it's possible as it just displays the strings all at once rather than every 500ms.
The desired effect is the strings displays as if someone is typing.
The code is below and here is a jsFiddle.
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
for (i = 0; i < split.length; i++) {
addChar(split[i]);
}
function addChar(char) {
if (timer) {
clearTimeout(timer);
}
var timer = setTimeout(function() {
target[0].innerHTML += char;
}, 500);
}
Just a proposal without setTimeout, but with setInterval and some other changes.
var content = "Hello, Universe!",
target = document.getElementById('ticker'),
i = 0,
timer = setInterval(addChar, 500);
function addChar() {
if (i < content.length) {
target.innerHTML += content[i];
i++;
} else {
clearTimeout(timer);
}
}
<div id="ticker"></div>
The problem is that all timeout functions starts at the same time (in 500ms). You can compute different timeouts by multiplying interval with the index of char in array:
for (i = 0; i < split.length; i++) {
addChar(split[i], i);
}
function addChar(char, i) {
setTimeout(function () {
target[0].innerHTML += char;
}, 500 * i);
}
You can mix setInterval and shift to do this :
var target = document.getElementsByClassName('place-here');
function display(element, string, timer) {
var split = string.split("");
var interval = setInterval(function() {
element.innerHTML += split.shift()
if (split.length == 0) {
clearInterval(interval);
}
}, timer)
}
display(target[0], "Hello, Universe!", 500)
I made you a working javascript code that you can paste in your fiddle. Your problem was that you call the setTimetout all at the same time so after 500 milliseconds everything executes instead of waiting on the other one to finish
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
console.log(split);
addChar(0);
function addChar(i) {
var currentChar = split[i];
console.log(i);
console.log(currentChar);
setTimeout(function() {
if(typeof currentChar !== 'undefined'){
target[0].innerHTML += currentChar;
addChar(i+1);
}
}, 1000);
}
Why not using setInterval() :
here is a fiddle
code here :
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
var length = split.length;
var count = 0;
var timer = setInterval(function() {
addChar(split[count]);
count++;
if(count>=length) clearInterval(timer);
}, 500);
function addChar(char) {
target[0].innerHTML += char;
}
<div class="place-here">
</div>
I made a few changes in your jsfiddle: https://jsfiddle.net/vochxa3f/10/
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
function addChar(str, i) {
if(i < str.length) {
target[0].innerHTML += str[i];
setTimeout(addChar, 500, str, ++i);
}
}
setTimeout(addChar(content, 0), 500);
The addChar function call itself with setTimeout() incrementing the variable i so in the next time it'll get another character from content.
Note that setTimeout() first argument is the reference to the function, in this case only "addChar" without "()". The arguments for the function stars at 3rd parameter and forth.

jQuery .each() multiplying "i"?

I'm trying to make a kind of newswire for a school project but I'm having a few problems with jQuery's .each() function. I'm trying to find a way to skip every 2nd array element in a loop.
Basically I have data from a NY Times API and got both title and abstract and push these into an array that I then loop and animate every once and awhile.
My problem is, I can't seem to find a way to get Title + Abstract (Index[0]+[1]) without the loop just moving to index[1] again. Now I knows in Javascript you can simply use a for (i=0; i < array.length; i+2) and thus skip every 2nd array element, but I haven't had any luck incorporating that. Any suggestions? :)
$(document).ready(function() {
var newsWire = [];
function loadNewswire() {
return $.getJSON('http://api.nytimes.com/svc/news/v3/content/all/all.json',
{'api-key': 'XXXXXXXXXXXXXXXXXXX'},
function(data) {
console.log(data)
var newsWireTemp = [];
for (var i = 0; i < data.results.length; i++) {
var breakingNews = data.results[i];
var breakingTitle = breakingNews.title.toUpperCase();
var breakingAbstract = breakingNews.abstract;
newsWireTemp.push(breakingTitle);
newsWireTemp.push(breakingAbstract);
}
newsWire = newsWireTemp;
});
}
loadNewswire().done(function () {
var items = newsWire;
$text = $('#newswiretxt span'),
delay = 10; //seconds
function loop (delay) {
$.each(items, function (i, elm){
$text.delay(delay*1E3).fadeOut();
$text.queue(function(){
$text.html(items[i]+ ": " +items[i+1]);
$text.dequeue();
});
$text.fadeIn();
$text.queue(function(){
if (i == items.length -1) {
loop(delay);
}
$text.dequeue();
});
});
}
console.log(items.length);
loop(delay);
});
});
Basically, just push the desired text concatenated into the array for the load function. Then as you iterate you can simply write the contents as is without messing with the iteration.
$(document).ready(function() {
var newsWire = [];
function loadNewswire() {
return $.getJSON('http://api.nytimes.com/svc/news/v3/content/all/all.json',
{'api-key': 'XXXXXXXXXXXXXXXXXXX'},
function(data) {
console.log(data)
var newsWireTemp = [];
for (var i = 0; i < data.results.length; i++) {
var breakingNews = data.results[i];
var breakingTitle = breakingNews.title.toUpperCase();
var breakingAbstract = breakingNews.abstract;
newsWireTemp.push(breakingTitle + ': ' + breakingAbstract);
}
newsWire = newsWireTemp;
});
}
loadNewswire().done(function () {
var items = newsWire;
$text = $('#newswiretxt span'),
delay = 10; //seconds
function loop (delay) {
$.each(items, function (i, elm){
$text.delay(delay*1E3).fadeOut();
$text.queue(function(){
$text.html(items[i]);
$text.dequeue();
});
$text.fadeIn();
$text.queue(function(){
if (i == items.length -1) {
loop(delay);
}
$text.dequeue();
});
});
}
console.log(items.length);
loop(delay);
});
});
See if this SO thread helps you.
From what I understand, you'd like to skip every other iteration, so checking i's parity to skip when appropriate should work.
For the lazy:
$.each(array, function(index, item) {
if(index % 2 === 0) return true; // This would skip
// Other logic
});
Let me know if it helps or not.
Instead of using two array indexes, use one object, var bn={};, add the two entries, bn.breakingTitle=breakingNews.title.toUpperCase(); and bn.breakingAbstract=breakingNews.abstract; then one push newsWireTemp.push(bn); so each entry in newsWire is more like newsWire[i].breakingTitle and newsWire[i].breakingAbstract.
One way to do it:
Fiddle: http://jsfiddle.net/q18dv4wr/
HTML:
<div id="test1">odds:</div>
<div id="test2">evens:</div>
JS:
var someData = [0,1,2,3,4,5,6,7,8,9,10];
var div1 = $('#test1');
var div2 = $('#test2');
$.each(someData,
function (index, value) {
if (index % 2 == 0) {
return;
}
else {
div1.append(' ' + value);
}
}
);
$.each(someData,
function (index, value) {
if (index % 2 != 0) {
return;
}
else {
div2.append(' ' + value);
}
}
);
EDIT: Seems I posted a moment too late. Someone else gave same idea already. =] Oh well.
You could do this:
$text.html(items[i]+ ": " +items[(i+=1)]);
But personally, I would push the breakingNews object into the array instead of having a different index for each property:
$(document).ready(function() {
var newsWire = [];
function loadNewswire() {
return $.getJSON('http://api.nytimes.com/svc/news/v3/content/all/all.json',
{'api-key': 'XXXXXXXXXXXXXXXXXXX'},
function(data) {
console.log(data)
var newsWireTemp = [];
for (var i = 0; i < data.results.length; i++) {
newsWireTemp.push(data.results[i]);
}
newsWire = newsWireTemp;
});
}
loadNewswire().done(function () {
var items = newsWire;
$text = $('#newswiretxt span'),
delay = 10; //seconds
function loop (delay) {
$.each(items, function (i, elm){
$text.delay(delay*1E3).fadeOut();
$text.queue(function(){
$text.html(items[i].title.toUpperCase()+ ": " +items[i].abstract);
$text.dequeue();
});
$text.fadeIn();
$text.queue(function(){
if (i == items.length -1) {
loop(delay);
}
$text.dequeue();
});
});
}
console.log(items.length);
loop(delay);
});
});
Try using .append() , checking if items[i + 1] is defined before appending items[i + 1] , else return empty string
$text.append(items[i] + (!!items[i+1] ? ":" + items[i+1] + " ": ""))
var items = "abcdefg".split("")
$.each(items, function(i, item) {
$("body").append(items[i] + (!!items[i+1] ? ":" + items[i+1] + " ": ""))
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

How to automatically type text repeatedly

I am trying to make a javascript/jquery code in which it automatically types a string, and then after the string is completely typed, clears the previously typed string and types it again. Currently, all my code does is type the string once. I know how to keep looping it, but it just starts typing where it left off, and doesn't clear the string. Javascript:
var txt='Typing text...'.split('');
var delay=850;
for ( i=0; i<txt.length;i++){
setTimeout(function(){
$('.autoText').append(txt.shift() )
}, delay * i)
}
and here is my html:
<h1 class="autoText" style="font-size: 50px; color: #7CFC00; margin-top: 0em; margin-left: 25%;"></h1>
var txt = 'Typing text...'.split(''),
$h1 = $('.autoText'),
len = txt.length,
delay = 850,
i = 0;
setInterval(function() {
$h1.append(txt[i++]);
if (i > len) {
$h1.empty();
i = 0;
}
}, delay);
DEMO: http://jsfiddle.net/yt6hm4hc/
How about this:
var txt='Typing text...';
var delay=850;
var i = 0;
function type() {
if (i < txt.length) {
$('.autoText').append(txt[i]);
i++;
} else {
$('.autoText').text('');
i = 0;
}
setTimeout(type, delay);
}
type();
http://jsfiddle.net/16v15ufv/
Forgive the overkill, but if you make everything really generic then you can re-use bits in other places
A function to generate each char of a string, one per invocation
function stringGenerator(str) {
var i = 0;
return function () {
if (i < str.length)
return str.charAt(i++);
i = 0;
return null;
};
}
A function to loop using setTimeout, with a couple neat little tricks for ending the loop
function timeoutLoop(fn, freq, callback) {
function looper() {
var ret = fn();
if (ret !== false && ret !== null && ret !== undefined)
return window.setTimeout(looper, freq);
if (callback)
callback();
}
window.setTimeout(looper, freq)
}
A function which combines these with logic about text in the DOM, to produce your typing effect
function type(node, str, freq) {
var s = stringGenerator(str),
f = function () {
var chr = s();
if (chr === null)
return false;
if (node.lastChild.nodeType === 3)
node.lastChild.data += chr;
else
node.appendChild(document.createTextNode(chr));
return true;
};
timeoutLoop(f, freq);
}
Finally, invocation, e.g. to have the words Hello world! written to the <body> one character every 500 ms
type(document.body, 'Hello world!', 500);
This is 100% vanilla
Is this what you want?
DEMO
var txt = 'Typing text...'.split('');
var delay = 850;
function type() {
for (i = 0; i < txt.length; i++) {
setTimeout(function () {
$('.autoText').append(txt.shift());
}, delay * i);
}
// delay * i + 1000 means 1 second after the above text was finished typing
setTimeout(function(){
// after finish, clear text in h1 element
$('.autoText').text('');
// fire `type` method again, with `txt` reset to its original value
type(txt = 'Typing text...'.split(''));
}, delay * i + 1000);
}
type(); // call type at least once
var txt='Typing text...'.split('');
var delay=850;
for ( i=0; i<txt.length;i++){
setTimeout(function(){
$('.autoText').text('');
$('.autoText').append(txt.shift() )
}, delay * i)
}
You could use recursion:
var txt='Typing text...';
var delay=850;
function TypeText($el, txt, currentIndex, timeout) {
setTimeout(function() {
//If finished writing, restart at 0
if (currentIndex >= txt.length) {
TypeText($el, txt, 0, 1000);
return;
}
//If at 0, empty the text
if (currentIndex == 0) $el.empty();
//Append current letter
$el.append(txt[currentIndex]);
//Append next letter
TypeText($el, txt, currentIndex+1, timeout);
}, timeout);
}
TypeText($('.autoText'), txt, 0, delay)
fiddle here
Using setTimeout within a loop you'll create your own pain in the neck.
You could use recursion, something like this:
function repeatEternal(str, time){
time = time*1000 || 200;
var position = 0
,rdiv = document.querySelector('#repeater')
,l2r = true;
return eternal(str);
function eternal(s){
rdiv.textContent = l2r ? rdiv.textContent + s[0] : s.slice(0, -1);
l2r = s.length === 1 ? !l2r : l2r;
s = s.length < 2 ? str : !l2r ? s.slice(0,-1) : s.slice(1);
setTimeout( function(){ eternal(s); }, time);
}
}
// usage
repeatEternal('Typing galore!', 0.3);
Here's the jsFiddle
Here is the OOP solution,with Object literal way. also with method that you can change the Delay. and in the future you can add more methods like change case of string or change effects of animation with setters methods. you can append it to any element with any string.
also it's not have dependencies like a jQuery. and this code you must put in the bottom of the DOM if you don't want dependency of jQuery or inside document ready func and with that you have dependency .
Hope it's help you.
var animText = {
animStr: '',
delay: 300,
setDelay: function(delay) {
this.delay = delay;
},
DoAnimation: function(string, selector) {
this.animStr = string.split('');
for (var i = 0; i <= string.length-1; i++) {
setTimeout(function () {
document.getElementById(selector).innerHTML += animText.animStr.shift();
},this.delay * i);
};
setTimeout( function() {
document.getElementById(selector).innerHTML = '';
animText.DoAnimation(string, selector);
}, this.delay * i + 1200)
}
}
animText.DoAnimation("Hello", "animStr");
animText.setDelay(900);

Categories