I've been trying to get display a bunch of letters that continuously change. They only change each time I reload the page. Please help.
const action = false;
var i = 0;
function randomized() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const still = document.getElementById('c1');
const still2 = [document.getElementById('c1'),document.getElementById('c2'),document.getElementById('c3'),document.getElementById('c4'),document.getElementById('c5'),document.getElementById('c6'),document.getElementById('c7'),document.getElementById('c8'),document.getElementById('c9'),document.getElementById('c10'),document.getElementById('c11'),document.getElementById('c12')]
while (action === false) {
i++;
var letter = possible.charAt(Math.floor(Math.random() * possible.length));
still2[i].innerHTML = letter;
}
/*for(var i = 0; action === false; i++){
var letter = possible.charAt(Math.floor(Math.random() * possible.length))
still2[i].innerHTML = letter;
};*/
}
As mentioned in another answer the while loop will lock the main thread and it will never render anything. I suggest using the requestAnimationFrame() function to queue the updates for the elements.
Below is a proof of concept you can adapt to your specific case:
A function to generate the random character
User global <span> selector for simplicity
A function to loop through the elements, update their content and queue the next frame indefinitely.
For the demo purposes stop the loop after 2 seconds. Note how the last frame animation is logged and used to then stop the infinite loop.
// Generate random letter
function randomLetter() {
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return possible.charAt(Math.floor(Math.random() * possible.length));
}
// Id to stop the animation frame loop
let animationId = 0;
// Update all nodes at the same time
function updateNodes() {
// List all nodes to update
let nodes = document.getElementsByTagName('span');
for (let i = 0; i < nodes.length; i++) {
nodes[i].innerHTML = randomLetter();
}
// Queue the next update
animationId = requestAnimationFrame(updateNodes);
}
// Start the animation loop
animationId = requestAnimationFrame(updateNodes);
// Stops animation after 2 seconds
setTimeout(function(){
cancelAnimationFrame(animationId);
}, 2000);
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
Call your page load method inside window.onload and use localStorage
window.onload = function() {
randomized(localStorage.getItem("randomString"));// Retrieve
}
function randomized(preRandomString){
// Store and update localStorage
return prvRandomString + localStorage.setItem("randomString", "abc123");
}
The while loop locks the thread and prevents the page from rendering each of your letters. Try setInterval() or similar asynchronous loop to release the thread and show each new letter:
function setRandomLetter() {
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const still = document.getElementById('c1');
const letter = possible.charAt(Math.floor(Math.random() * possible.length));
still.innerText = letter;
}
// set a random letter every second
setInterval(setRandomLetter, 1000);
<span id="c1"></span>
Related
new to JS here and was wondering if someone could guide me on how I can pause and delete when implementing a typewriter effect in JavaScript.
I've got a function that successfully types out each word in an array, however I would like it to pause after typing each word and backspace or delete the letters before typing the next one.
//counter
var i = 0;
var index = 0;
var texts = ['Salesforce Consultant', 'Developer', 'Writer'];
var speed = 110;
let letter = '';
let currentText = '';
let delay = 25;
function typeWriter() {
//If counter is less than the # of letters in txt, reset array of words
if (i === texts.length) {
i = 0;
}
//Use count to select text to display
currentText = texts[i];
letter = currentText.slice(0, ++index);
document.querySelector("#demo").textContent = letter;
//If letters displayed are the same number of letters in the current text
if (letter.length === currentText.length) {
//Pause before deleting
//Delete letters in word
//Word is done displaying, and reset letters on screen
i++;
index = 0;
}
setTimeout(typeWriter, speed);
}
typeWriter();
<div id="demo"></div>
HTML
<div class="centered">
<div class="intro">
<p>A </p>
<p class ="typing" id="demo"></p>
</div>
</div>
You can do this by introducing a variable that determines how the index will change (+1 or -1). The different delay is just a different argument to setTimeout.
I would also suggest converting some global variables into function parameters: that way they are better (more narrowly) scoped. The change that these variables get, can be managed by what you let setTimeout pass on to the next call.
Here is how that could work:
const texts = ['Salesforce Consultant', 'Developer', 'Writer'];
const speed = 110;
const pause = 800; // <--- the longer delay between text direction changes
function typeWriter(i=0, index=1, direction=1) {
let displayed = texts[i].slice(0, index);
document.querySelector("#demo").textContent = displayed;
if (displayed.length >= texts[i].length) { // start removing after pause
setTimeout(() => typeWriter(i, index-1, -1), pause);
} else if (displayed.length === 0) { // go to next text after pause
setTimeout(() => typeWriter((i+1) % texts.length), pause);
} else { // continue in the current direction
setTimeout(() => typeWriter(i, index+direction, direction), speed);
}
}
typeWriter();
<div id="demo"></div>
Im trying create some type of number generator on webpage. I want to show like five numbers before the generated number show. For better imagine, you can look to google generator. When you click generate, it shows like 3-4 numbers before generated number. I use setInterval or setTimeout but i dont know how it works. My js code:
var button = document.querySelector("button");
button.addEventListener("click",function() {
for (var i = 0; i < 8; i++) {
setInterval(textC,5);
}
});
function textC(){
number.textContent = Math.floor(Math.random() * 1000) + 1;
}
Thanks for every help!
The issue with setInterval() is that it will continue forever unless cleared, causing you to keep generating random numbers. Instead you can use setTimeout(), but set the timeout to change based on the value of i in the for loop. That way, each interval will occur 50 m/s after the other.
See example below:
const button = document.querySelector("button");
const number = document.querySelector("#number");
button.addEventListener("click", function() {
for (let i = 0; i < 5; i++) {
setTimeout(textC, 50 * i);
}
});
function textC() {
number.textContent = Math.floor(Math.random() * 1000) + 1;
}
<p id="number"></p>
<button>Generate</button>
Don't use a loop (why not?). Just nest setTimeout and call it until a predefined threshold is reached. It gives you maximum control.
var button = document.querySelector("button");
var number = document.querySelector("#number");
const nRuns = 12;
const timeout = 100;
let iterator = 0;
button.addEventListener( "click", textC);
function textC(){
number.textContent = `${Math.floor(Math.random() * 1000) + 1}\n`;
iterator += 1;
if (iterator < nRuns) {
setTimeout(textC, timeout)
} else{
iterator = 0;
// you control the loop, so it's time for some extra text after it
number.textContent += ` ... and that concludes this series of random numbers`;
}
}
<p id="number"></p>
<button>Generate</button>
I wrote this code in JS:
function startFunction() {
p1 = document.getElementById('p1').innerHTML;
for (var i=1; i<=p1.length; i++) {
alert(p1.slice(0, i));
}
}
I call the function with onload event in html:
<body onload="startFunction()">
And thi is the paragraph with p1 id:
<p id="p1">Hi, I'm</p>
How can I make a delay for the for loop. I want my program to write the p1 text letter by letter.
You can not and should not delay anything inside a loop, because that is how the nonresponsive pages are made: the browser does not react to user actions or do anything visible until the JavaScript code returns.
Instead, you can use some timer, like setInterval():
function startFunction() {
var p1 = document.getElementById('p1');
var txt = p1.innerHTML;
var i=0;
var timer = setInterval(function() {
p1.innerHTML = txt.slice(0,i++);
if(i>txt.length) {
clearInterval(timer);
}
},500);
}
startFunction();
<p id="p1">Hi, I'm</p>
var alertEachLetter =function(p1, i){
setTimeout(function(){
alert(p1.slice(0, i));
},1000);
};
function startFunction() {
p1 = document.getElementById('p1').innerHTML;
for (var i=1; i<=p1.length; i++) {
alertEachLetter(p1, i);
}
}
why create this alertEachLetter function. for that you need to check this link
setTimeout in for-loop does not print consecutive values
You don't need a loop, you need an interval. Javascript's interval feature will call your function at (approximately) the requested interval. So, for example:
function startFunction() {
var p1 = document.getElementById('p1').innerHTML
var count = 1
var finished = p1.length
var iv = setInterval(function() {
alert(p1.slice(0,count++))
if (count > finished) {
clearInterval(iv) // stops the interval from firing once we finish our task
}
}, 1000) // 1000 ms, or every second.
}
Here's a quick example using setTimeout instead of setInterval. There's not much difference except you don't have to clear the timeout - you simply don't run it if it doesn't meet a condition.
// cache the elements
const p1 = document.getElementById('p1');
const out = document.getElementById('out');
// make the text content from p1 iterable and split it into
// the head (first element), and tail (everything else)
const [head, ...tail] = [...p1.textContent];
const loop = function loop(head, tail) {
// update the output text content with the result of head
out.textContent = head;
// if there's anything left of the tail array
if (tail.length) {
// remove the first element of tail and
// add it to head
head += tail.shift();
// call the function again with the new head and tail
setTimeout(loop, 200, head, tail);
}
// pass in the head and tail to the function
}(head, tail);
#p1 { display: none; }
<p id="p1">Content written letter by letter</p>
<p id="out"></p>
Below is an approach I think may help you achieve what youre trying to do. This approach uses setInterval (instead of a loop) to execute a function multiple times. See the comments to understand the code logic:
//Grab our DOM elements
var p1 = document.getElementById('p1').innerHTML;
var copy = document.getElementById('copy');
//Execute a function every 250 milliseconds
var intervalId = setInterval(onInterval, 250);
//nextLetter is a function that will return the character at a particular index in the string. The function will increase the index each time it is called. The function will return null once it exceeds the innerHTML length. c is a "private" variable that can't be modified elsewhere in the program.
var nextLetter = (function(i, limit) {
var c = i;
return function() {
var idx = c++;
if (idx > limit) {
return null;
}
return p1.charAt(idx);
};
})(0, p1.length);
//The function we will execute at each interval
function onInterval() {
var letter = nextLetter();
if (letter) {
copy.innerHTML += letter;
} else {
console.log('End of content reached - removing interval');
clearTimeout(intervalId);
}
}
<p id="p1">Make sure to read the in-code comments</p>
<p id="copy"></p>
<canvas id="ctx" width="500" height="500" style="border:1px solid #000000;">
<script type="text/javascript">
window.onload = function() {
var ctx = document.getElementById("ctx").getContext("2d");
function createText(words) {
var LENGTH = words.length;
var LOOPS = LENGTH;
var reader = 0;
position = 10;
while( LOOPS > 0) {
letter = words.substr(reader,1);
setTimeout(ctx.fillText(letter,position,10),100);
position += 6;
reader += 1;
LOOPS -= 1;
}
}
createText("Hello, how are you?");
}
</script>
</canvas>
I want it to do kind of like a typing animation where it paused for a fraction of a second before each letter is printed, but instead it loads all at the same time. What am I doing wrong?
So there were a few things making this not work for you, for setTimeout your ctx.fillText was being called right away, as soon as the loop hit it. To stop that you need to wrap it in a function so it will be called during the timeout.
setTimeout(function(){
// your logic here.
}, 100);
However if you do that you will run into the common issue where you will only get the last letter due to the way variable scoping works in JavaScript. To fix that you need to wrap your function in a closure and pass the values to it.
// loop start
a++;
b++;
setTimeout(
(function (a,b) {
return function () {
// some logic that uses a and b
}
})(a, b), 100);
// loop end
The last thing that happens is your timeout is set to 100.. so it will all still happen at once. Meaning every timeout is going to fire after 100ms since the creation loop is so fast. In order to solve this you need to save the delay somewhere and increase that in the loop so they happen after one another. For example the first one will be delayed 100ms, and the next one 200ms, then 300, ect.
// loop start
a++;
b++;
// Increase the time delay each loop iteration
timeDelay += 100;
setTimeout(
(function (a,b) {
return function () {
// some logic that uses a and b
}
})(a, b), timeDelay);
// loop end
Full working code and demo
Live Demo
window.onload = function () {
var ctx = document.getElementById("ctx").getContext("2d");
function createText(words) {
var LENGTH = words.length;
var LOOPS = LENGTH;
var reader = 0;
var timeDelay = 100;
position = 10;
while (LOOPS > 0) {
letter = words.substr(reader, 1);
setTimeout((function (letter, position) {
return function () {
ctx.fillText(letter, position, 10);
}
})(letter, position), timeDelay);
position += 6;
reader += 1;
LOOPS -= 1;
timeDelay += 100;
}
}
createText("Hello, how are you?");
}
Is it possible to increase the time out limit for JavaScript?
If I have a script that executes for more than 20/30 seconds chrome will pop-up with the unresponsable page dialog.
Making a more efficient script won't help me because the script sometimes need to iterate through a function for a million or billion times
To split the function on steps/chunks and run those inside setInterval(function(){}).
This way page will be responsive, you will be able to notify user about progress of execution and you will get your job done.
UPDATE: Here is simple function that takes
worker function executing each iteration,
chunksz - number of iteration running in single chunk
maxit - total number of iterations.
function task(worker, chunksz, maxit)
{
var idx = 0;
var xint = null;
function exec_chunk()
{
for(var n = 0; n < chunksz; ++n)
{
if(idx >= maxit) { return; }
worker(idx++);
}
setTimeout(exec_chunk,1);
}
exec_chunk();
}
Here is an example : http://jsfiddle.net/Ed9wL/
As you see you get all iterations in order.
UPDATE2:
Say you have a loop:
for(var i=0; i<100000; ++i) { ... do something ... }
then you need to wrap body of the loop into a function and call the task above with it like this:
task(function(i){ ... do something ... },100, 100000);
or like this:
function loopBody(i){ ... do something ... }
task(loopBody,100, 100000);
When you have lots of processing to do client side, you need to split out your work into separate threads. The browser only has a single thread for handling user input (events) and for processing JS. If you're processing too much JS, without yielding, the UI becomes unresponsive and the browser is not happy.
How can you allow your script to yield? The new way is to use web workers http://www.whatwg.org/specs/web-workers/current-work/ . This works by creating a separate thread to run your JS, thread thread does not access to the DOM and can be run concurrently.
However, this newer technology doesn't exist in all browsers. For older browsers, you can split up your work by having the script call itself through timeouts. Whenever a timeout occurs, the script is yielding to the browser to run its events, once the browser is done, your next timeout will be triggered.
Example http://jsfiddle.net/mendesjuan/PucXf/
var list = [];
for (var i = 0; i < 500000; i++) {
list.push(Math.random());
}
function sumOfSquares(list) {
var total = 0;
for (var i = 0; i < list.length; i++) {
total += list[i] * list[i];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Sync temp value = " + total;
document.body.appendChild(node);
}
return total;
}
function sumOfSquaresAsync(arr, callback) {
var chunkSize = 1000; // Do 1000 at a time
var arrLen = arr.length;
var index = 0;
var total = 0;
nextStep();
function nextStep() {
var step = 0;
while (step < chunkSize && index < arrLen) {
total += arr[index] * arr[index];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
index++;
step++;
}
if (index < arrLen) {
setTimeout(nextStep, 10);
} else {
callback(total);
}
}
}
sumOfSquaresAsync(list, function(total) {console.log("Async Result: " + total)});
//console.log("Sync result" + sumOfSquares(list));
The example on jsfiddle has the synchronous call commented out, you can put it back in to see the browser come to a crawl. Notice that the asynchronous call does take a long time to complete, but it doesn't cause the long running script message and it lets you interact with the page while calculating (select text, button hover effects). You can see it printing partial results on the pane to the bottom right.
UPDATE http://jsfiddle.net/mendesjuan/PucXf/8/
Let's try to use c-smile's task function to implement sum of squares. I think he's missing a parameter, a function to call back when the task is finished. Using task allows us to create multiple chunked functions without duplicating the work of calling setTimeout and iteration.
/**
* #param {function} worker. It is passed two values, the current array index,
* and the item at that index
* #param {array} list Items to be traversed
* #param {callback} The function to call when iteration is finished;
* #param {number} maxit The number of iterations of the loop to run
* before yielding, defaults to 1000
*/
function task(worker, list, callback, maxit)
{
maxit = maxit || 1000;
var idx = 0;
exec_chunk();
function exec_chunk()
{
for(var n = 0; n < maxit; ++n)
{
if(idx >= list.length) {
callback();
return;
}
worker(idx, list[idx]);
idx++;
}
setTimeout(exec_chunk,1);
}
}
function sumOfSquaresAsync(list, callback)
{
var total = 0;
// The function that does the adding and squaring
function squareAndAdd(index, item) {
total += item * item;
// DOM manipulation to make it take longer and to see progress
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
}
// Let the caller know what the result is when iteration is finished
function onFinish() {
callback(total);
}
task(squareAndAdd, list, onFinish);
}
var list = [];
for (var i = 0; i < 100000; i++) {
list.push(Math.random());
}
sumOfSquaresAsync(list, function(total) {
console.log("Sum of Squares is " + total);
})
If your goal is to suppress "Kill-Wait" message as quick temporary fix for your slow JavaScript then the solution is to open Tools/Developer Tools in Google Chrome and keep it open and minimized somewhere on your desktop while browsing .