I have two functions that are supposed to run when I click on a button. The first function is a setInterval function that increments the variable num by 0.01 and displays it in a div id called result. The second function generates an object literal into a json string and displays it in a div id called output. When I click on the button, only the JSON string is outputted, but it seems as though the setInterval doesn't run and I don't know why. Here is the link for example
HTML
<button>
click
</button>
<div id="result">
</div>
<div id="output">
</div>
Javascript
var timer,
result = document.getElementById("result"),
num,
link ={
name:"link",
weapon:"master sword",
mute:true,
race:"hyrulian",
age:16
};
/* ---------- GENERATE JSON OBJECT --*/
function generate(){
var data = {};
for(var prop in link){
data[prop] = link[prop];
}
data = JSON.stringify(data);
document.getElementById("output").innerHTML = data;
}
/* ---------- CLICK BUTTON --*/
document.querySelector("button").onclick = function(){
num = 0;
function callBack(){
num += 0.01;
result.innerHTML = num.toFixed(2);
}
timer = setInterval(callBack,10);
generate();
clearInterval(timer);
}
Updated My Answer: You have to wrap your callBack(); function in the setInterval with an anonymous function. This is because setInterval expects a reference to a Function that should be executed every few milliseconds (the interval you specify). You should use it like this setInterval( function(){ callBack(); }, 10);. This should give you the desired result.
Same code, I just updated the setInterval( function(){ callBack(); }, 10); line:
var timer,
result = document.getElementById("result"),
num,
link ={
name:"link",
weapon:"master sword",
mute:true,
race:"hyrulian",
age:16
};
/* ---------- GENERATE JSON OBJECT --*/
function generate(){
var data = {};
for(var prop in link){
data[prop] = link[prop];
}
data = JSON.stringify(data);
document.getElementById("output").innerHTML = data;
}
/* ---------- CLICK BUTTON --*/
document.querySelector("button").onclick = function(){
num = 0;
function callBack(){
num += 0.01;
result.innerHTML = num.toFixed(2);
}
timer = setInterval( function(){ callBack(); },10); // I modified this line
// timer = setInterval(callBack,10);
generate();
clearInterval(timer);
// setTimeout( function(){ clearInterval(timer); }, 1000); // This can be used to test that the timer has indeed started, you can wait 1 second before you clear the timer
}
It's because you setInterval(callBack,10);, but then you clear the interval using clearInterval(timer);.
remove the clearInterval(timer); and you'll get the timer working.
JavaScript is single threaded. In order for the callBack function which you have set up to run in an interval of 10ms with setInterval(callBack,10) to run, your JavaScript code has to stop running (i.e. complete). Until your code relinquishes the processor, no call back functions (of any type) will execute.
Thus, in your code:
timer = setInterval(callBack,10); //Set up the interval timer
generate(); //generate(): Even if this takes 10 minutes to complete
// the callBack function will not be executed from the interval timer.
clearInterval(timer); //Clear the interval timer. While the interval may have expired
// (depending on how long the generate() function took), the
// the callBack function will have never run because the processor
// has been busy continuing to run the code that came after calling
// setInterval. Clearing the interval timer here results in
// callBack never being executed.
Thus, if you want callBack to execute even once, you need to not call clearInterval(timer) prior to the code you are executing at that time completing.
If you wanted callBack to only be executed once, then you could have used setTimeout().
Timing how long something takes:
If you are attempting to time how long something takes:
Get the current time prior to what you are wanting to time
Use window.performance.now() which is "measured in milliseconds, accurate to one thousandth of a millisecond".
Do the thing you want to time
Get the time after you are done (Use window.performance.now() again).
Compute and display the difference between the two times.
So, modifying your code:
var timer,
result = document.getElementById("result"),
link = {
name: "link",
weapon: "master sword",
mute: true,
race: "hyrulian",
age: 16
};
/* ---------- GENERATE JSON OBJECT --*/
function generate() {
var data = {};
for (var prop in link) {
data[prop] = link[prop];
}
data = JSON.stringify(data);
document.getElementById("output").innerHTML = data;
}
/* ---------- CLICK BUTTON --*/
document.querySelector("button").onclick = function() {
function showResult(time, precision, units) {
result.innerHTML = time.toFixed(precision) + ' ' + units;
}
var startTime = window.performance.now();
generate();
var endTime = window.performance.now();
//These times are floating point numbers in milliseconds
var elapsedMs = endTime - startTime;
showResult(elapsedMs,3,'milliseconds');
//If you want to show the time in microseconds:
//var elapsedUs = elapsedMs * 1000;
//showResult(elapsedUs,0,'microseconds');
}
<button>
click
</button>
<div id="result">
</div>
<div id="output">
</div>
However, if your real goal is to determine how long, on average, it takes to run generate() then you need time how long it takes to run generate a large number of times, not just once.
var result = document.getElementById("result");
var link = {
name: "link",
weapon: "master sword",
mute: true,
race: "hyrulian",
age: 16
};
/* ---------- GENERATE JSON OBJECT --*/
function generate() {
var data = {};
for (var prop in link) {
data[prop] = link[prop];
}
data = JSON.stringify(data);
document.getElementById("output").innerHTML = data;
}
/* ---------- CLICK BUTTON --*/
document.querySelector("button").onclick = function(event) {
//Should not do long things in an event handler.
//Thus, instead of actually performing the tests here, we set
// a timeout which will execute after the event has processed.
this.style.display = 'none'; //Only get to click once
setTimeout(performTimedTests,0,generate,result);
}
/*
* Perform multiple runs of an increasing number of iterations of a specified function.
*
* functionToTest = The function which is being timed
* resultsElement = An element in the DOM, usually a <div>, which will
* have its innerHTML replaced with a table of time results.
* start = The number of iterations to start with. (default = 1)
* end = The number of iterations to not exceed. (default = 1000000)
* multiplier = The amount multiply the current number of iterations to find
* the number of iterations for the next run. (default = 10)
*/
function performTimedTests(functionToTest,resultsElement,start,end,multiplier){
start=start?start:1;
end=end?end:1000000;
multiplier=multiplier?multiplier:10;
var textColors =['red','orange','yellow','green','blue','purple']
function timeAfunction(functionToTest, iterations){
var i, startTime, endTime;
startTime = window.performance.now();
for(i=0;i<iterations;i++){
functionToTest();
}
endTime = window.performance.now();
return (endTime - startTime);
}
function addResultToTable(table, elapsedTime, iterations, precision1, units1
, precision2, units2, scale2, name) {
var perCall = (elapsedTime/iterations)*scale2;
var newRow = table.insertRow(-1);
newRow.insertCell(-1).textContent = iterations;
newRow.insertCell(-1).textContent = elapsedTime.toFixed(precision1) + units1;
newRow.insertCell(-1).textContent = perCall.toFixed(precision2) + units2;
newRow.insertCell(-1).textContent = name;
}
function performNextTest(functionToTest,resultsElement,iterations,maxIterations
,multiplier,isFirstIteration){
var elapsedTime = timeAfunction(functionToTest, iterations);
var processing;
if(isFirstIteration) {
result.innerHTML = '<div></div>'
+'<table><tr><th>Iterations</th><th>Total time</th>'
+'<th>Time per<br/>Iteration</th>'
+'<th>Function<br>Tested</th></tr></table>';
processing = resultsElement.querySelector('div');
processing.textContent = 'Processing';
processing.style.color = textColors[0];
}
processing = resultsElement.querySelector('div');
var table = resultsElement.querySelector('table');
addResultToTable(table,elapsedTime,iterations,3,'ms',3,'us',1000
,functionToTest.name);
processing.textContent += '.';
//Cycle colors of 'Processing..' text
var colorIndex=textColors.indexOf(processing.style.color);
colorIndex++;
colorIndex = colorIndex>=textColors.length?0:colorIndex;
processing.style.color = textColors[colorIndex];
iterations *= multiplier;
if(iterations<=maxIterations) {
//Let the browser redraw
setTimeout(performNextTest,0,functionToTest,resultsElement,iterations
,maxIterations,multiplier,false);
}else{
processing.textContent = '';
processing.style.display = 'none';
}
}
performNextTest(functionToTest,resultsElement,start,end,multiplier,true);
}
<button>Start</button>
<div id="result"></div>
<div id="output"></div>
Related
I want the speed of my loop to be influenced by a variable (x). This will influence how fast a value (t) is increased, when a button is clicked. I wrote my code, and it should work but it doesn't. Help.
var timeCounter = document.getElementById("time");
var x;
var t;
function timeOnLoad(){
time = 0;
x = 1000;
}
setInterval(function(){
t++;
timeCounter.innerHTML = t;
},x)
function changeSpeed(){
x = 2000;
}
function valueSpeed(){
alert(x);
}
body{
background-color:white;
}
<div onload="timeOnLoad()" id="time"></div>
<button onclick="changeSpeed()">Change x by 1 sec</button>
<button onclick="valueSpeed()">Get value of x</button>
That happens because interval uses x variable only on initialization. It's not dynamic. You have to stop the timer and reinstantiate it in order for it to work. Here's an example:
var timeCounter = document.getElementById("time");
var t = 0, x = 1000, interval;
function changeSpeed(){
x = x + 1000;
restartInterval();
}
function valueSpeed(){
alert(x);
}
function restartInterval() {
clearInterval(interval);
interval = setInterval(function(){
t++;
timeCounter.innerHTML = t;
}, x);
}
restartInterval();
body{
background-color:white;
}
<div id="time"></div>
<button onclick="changeSpeed()">Change x by 1 sec</button>
<button onclick="valueSpeed()">Get value of x</button>
Also, div doesn't have an onLoad event. You have to put this code elsewhere.
You need to cancel and re-instantiate the setInterval when you want to update it. The value set is not dynamic and once it is set it remains at that value.
var timeCounter = document.getElementById("time");
var x = 1;
var t = 0;
function timeOnLoad(){
time = 0;
x = 1000;
}
// Create a function to init the timer. Additionally, create a var to track the actual timeout.
var timer = null;
function setTimer() {
console.log("Setting time to: " + x);
timer = setInterval(function(){
t++;
timeCounter.innerHTML = t;
},x);
}
// Clear the timeout and update the value. Then call your timeout again.
function changeSpeed(){
clearTimeout(timer);
x = Math.floor(Math.random() * 5000);
setTimer();
}
function valueSpeed(){
alert(x);
}
setTimer();
<p id="time"></p>
<button id="updatetime" onclick="changeSpeed()">Cickme</button>
So basically what I did above was create an actual variable to track the timeout obj and then create a function to call it.
Next, inside of your changeSpeed function, clear the timeout to stop it from running and then set your new value. Finally call your setTimer again to restart it with the new value.
I made the x value a random number so you could more easily see the change multiple times.
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>
I want to create a 'random selector' behaviour where the function iterates through an array for a period of time (example: 3 seconds, 5 seconds) while displaying all of array elements fast throughout the iteration until the iteration ends. Just imagine seeing all the elements displayed in a label one after another until it finally stops at an element.
My code so far:
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function () {
place_label.innerText = selected_place;
}, 400);
}
}
This runs through the iteration and displays an element when the loop is done but it doesn't show each iteration's element. How can I amend this?
EDIT
Even if there's 3 elements, the animations must re-iterate through the array until the duration is completed
Your for finishes iterating before the setTimeout runs, then the function passed to setTimeout runs 100 times using the last value of selected_place.
More about this behavior in setTimeout in for-loop does not print consecutive values
Another problem that I noticed is that your setTimeout will trigger after 400ms, since the for loop will finish in about 1 ms, the function passed to setTimeout will trigger 100 times one after another with no delay in between, for this, I replaced the delay argument of setTimeout from 400 to 400 * i.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
(function(selected_place){
setTimeout(function () {
place_label.innerText = selected_place;
}, 400 * i);
})(selected_place);
}
}
execute_randomizer();
<span id="place_label"></span>
You could use a closure over the value and a different time for each timeout.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 10; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function (value) {
return function () {
place_label.innerText = value;
};
}(selected_place), i * 100);
}
}
execute_randomizer();
<div id="place_label"></div>
For a first run through, you could show each element and then take a random element at last value.
function execute_randomizer() {
function cb (value) {
return function () {
place_label.innerText = value;
};
}
var place_label = document.getElementById("place_label");
place_label.innerText = '';
for (var i = 0; i < places.length; i++) {
setTimeout(cb(places[i]), 200 + i * 200);
}
setTimeout(cb(places[Math.floor(Math.random() * places.length)]), 200 + places.length * 200);
}
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer();
<div id="place_label"></div>
You should change your loop because right now you go in loop 100 times in maybe one millisecond and order 100 of change text but once again in the same time.
So better is wait for time out 400 ms and then make next iteration.
Please remember time out is async.
I think it might be better if you would put the whole function in a timeout. (I'm not sure and didn't test, but that's what I would try).
What I mean is that you just build a function that creates your randomizer and fills in your field. After that you put a timeout to call the same function again + keep a counter that you pass as parameter.
An example of what I mean below: (didn't test it)
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer(0); /* Start for the first time */
function execute_randomizer(times) {
if(times == 100)
return; /* stop execution */
var place_label = document.getElementById("place_label");
var selected_place = places[Math.floor(Math.random() * places.length)];
place_label.innerText = selected_place;
setTimeout(function () {
execute_randomizer(times+1)
}, 400);
}
Hi I want to use 3 different counters that show me 3 different increasing numbers starting from a certain date. I've tryed this:
<script>
var START_DATE_1 = new Date("July 18, 2016 10:30:00"); // put in the starting date here
var INTERVAL_1 = 5; // in seconds
var INCREMENT_1 = 1; // increase per tick
var START_VALUE_1 = 0; // initial value when it's the start date
var count_1 = 0;
window.onload = function()
{
var msInterval_1 = INTERVAL_1 * 1000;
var now_1 = new Date();
count_1 = parseInt((now_1 - START_DATE_1)/msInterval_1) * INCREMENT_1 + START_VALUE_1;
document.getElementById('counter_1').innerHTML = count_1;
setInterval("count_1 += INCREMENT_1; document.getElementById('counter_1').innerHTML = count_1;", msInterval_1);
}
</script>
<script>
var START_DATE_2 = new Date("July 18, 2016 10:30:00"); // put in the starting date here
var INTERVAL_2 = 5; // in seconds
var INCREMENT_2 = 1; // increase per tick
var START_VALUE_2 = 0; // initial value when it's the start date
var count_2 = 0;
window.onload = function()
{
var msInterval_2 = INTERVAL_2 * 1000;
var now_2 = new Date();
count_2 = parseInt((now_2 - START_DATE_2)/msInterval_2) * INCREMENT_2 + START_VALUE_2;
document.getElementById('counter_2').innerHTML = count_2;
setInterval("count_2 += INCREMENT_2; document.getElementById('counter_2').innerHTML = count_2;", msInterval_2);
}
</script>
<script>
var START_DATE_3 = new Date("July 18, 2016 10:30:00"); // put in the starting date here
var INTERVAL_3 = 5; // in seconds
var INCREMENT_3 = 1; // increase per tick
var START_VALUE_3 = 0; // initial value when it's the start date
var count_3 = 0;
window.onload = function()
{
var msInterval_3 = INTERVAL_3 * 1000;
var now_3 = new Date();
count_2 = parseInt((now_3 - START_DATE_3)/msInterval_3) * INCREMENT_3 + START_VALUE_3;
document.getElementById('counter_3').innerHTML = count_2;
setInterval("count_3 += INCREMENT_3; document.getElementById('counter_3').innerHTML = count_3;", msInterval_3);
}
</script>
<div id="counter_1"></div>
<div id="counter_2"></div>
<div id="counter_3"></div>
This doesn't work as expected and only the 3td div is populated but now working as expected too (as soon as I load the page it show me a number then after few seconds restart from 1).
What's wrong? How should I do it without using JQuery also?
Thanks in advance.
I don't do a code analysis - this would be something for SE-CodeReview (highly recommended to post there too once you got it working), but your error is, that you are assigning something to a function more than once - hence, only the last assignment is actually called onload.
So -- you are assigning window.onload = function() in your first <script> tag, then you are overriding it in your second; and you're overriding the second assignment again in your third <script> tag.
You can't do that if you want to assign all 3 functions to a single (onload) event.
If you want to add multiple listeners for a single event, use
document.addEventListener("DOMContentLoaded", function() {});
Now inside that function, you can add all your code that you want to have executed onload of your page; in addition to the code you wanted to have executed onload in another function!
Here's a simple demonstration using the same techniques as described above, but with click listeners. Notice the first function will never be called.
document.getElementById("a").onclick = function() {
alert("I will never alert, because of the next function");
}
document.getElementById("a").onclick = function() {
alert("I will alert, because I'm not overriden");
}
document.getElementById("a").addEventListener('click', function() {
alert("I will alert too");
});
document.getElementById("a").addEventListener('click', function() {
alert("And a third alert. Disturbing");
});
<div id="a">Click me!</div>
I am trying to run a loop that will continuously change the color by randomly generating hex codes. I tried to search on here but couldn't find anything doing this.
I can't figure out how to get a loop to run and change the color continuously (until the end of a loop). I am new to JavaScript.
Here's my JSFiddle.
HTML
<body>
<div id="outer">
<div id="test">Generate colors.</div>
</div>
</body>
JS
for ( i = 0; i < 20000; i++ ) {
var t = document.getElementById('test');
var z = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
t.style.color = z
}
You can't change colors in a loop, the color of the element won't change until you exit the code and return control to the browser.
You can use an interval to run code and return the control to the browser each time:
window.setInterval(function(){
var t = document.getElementById('test');
var z = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
t.style.color = z
}, 100);
Demo: http://jsfiddle.net/et3qtr3t/
You were right with the commented setInterval you have on fiddle. It will make the colors change periodically (according to the milliseconds defined).
But you have to remove the for loop, because it will run instantly and you won't even see the changes... You'll have to manage your own variable counter, and clear the interval after it:
http://jsfiddle.net/kkfnjpsh/5/
var i = 0;
var runner = setInterval(function(){
if(i < 20000) {
var t = document.getElementById('test');
var z = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
t.style.color = z;
i++;
}
else {
clearInterval(runner);
}
}, 3000);
I know it's already been answered, but mine includes the cleartimeout to set a timer.
var myVar = setInterval(function(){changeColor()}, 1000);
setTimeout(function(){clearInterval(myVar)}, 5000);
The second argument in the call to setTimeout could serve as your timer, so that the animation stops afterwards, in this case, it's set to 5 seconds.
function changeColor() {
var t = document.getElementById('test');
var z = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
t.style.color = z;
console.log(z);
}
Result: Result
You don't loop - you interval:
var target= document.getElementById('test'),
colorChange = function() {
target.style.color = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
};
// run interval
var d = setInterval(function() {
colorChange();
}, 500);
// clear after 5s
setTimeout(function() {
clearInterval(d);
}, 5000);
Working JSFiddle: http://jsfiddle.net/046q6ohf/