Run jQuery after setInterval function is complete - javascript

I have a basic count function and i wish to run some code only after the count function is complete. Is there a way of doing this simply?
DEMO http://jsfiddle.net/vyN6V/243/
Current Code
var number = 1500;
var aValue = 300;
function count(Item) {
var current = aValue;
Item.val(aValue += 1);
if (current < number) {
setTimeout(function () {
count(Item)
}, 0.1);
}
}
count($(".input"));
// code here should only run when count function is complete
I tried this, to no avail
var number = 1500;
var aValue = 300;
function count(Item) {
var current = aValue;
Item.val(aValue += 1);
if (current < number) {
setTimeout(function () {
count(Item)
}, 0.1);
}
}
count(function$(".input"){
// code here should only run when count function is complete
});

FIDDLE
var number = 1500;
var aValue = 300;
function count(Item) {
var current = aValue;
Item.val(aValue += 1);
if (current < number) {
setTimeout(function () {
count(Item)
}, 0.1);
} else {
$('span').text('code here should only run when function count is complete');
}
}
count($(".input"));

Related

Image slideshow timer speed getting increased when we click on next or previous buttons, using JavaScript

I have written simple image slider using JavaScript. In that I have set 5000 ms timer to slide automatically. but it is getting increased when we click on next or previous button. I have pasted the code below
HTML Code:
<section id="intro">
<div id="prev"><br/><br/><br/><br/><img onclick="rotateImages(0);" src="./images/slider/prev1.png"/></div>
<div id="next"><img onclick="rotateImages(1);" src="./images/slider/next1.png"/></div>
</section>
JavaScript Code:
<script type="text/javascript">
var num = 0;
var temp = 0;
var speed = 5000;
var preloads = [];
preload(
'1.png',
'2.jpg',
'3.jpg',
'4.jpg',
'5.png'
);
function preload() {
for (var c = 0; c < arguments.length; c++) {
preloads[preloads.length] = new Image();
preloads[preloads.length - 1].src = arguments[c];
}
}
function rotateImages(flag) {
if (flag == 0)
{
num = num - 1;
if (num < 0)
num = preloads.length;
}
else
{
num = num + 1;
if (num >= preloads.length)
num = 0;
}
if (num == temp) {
rotateImages(1);
} else {
var str = preloads[num].src;
var resArr = str.split("/");
var picName = resArr[resArr.length - 1];
document.getElementById("intro").style.backgroundImage = 'url(./images/slider/' + picName + ')';
temp = num;
setTimeout(function () {
rotateImages(1)
}, speed);
}
}
if (window.addEventListener) {
window.addEventListener('load', function () {
setTimeout(function () {
rotateImages(1)
}, speed)
}, false);
} else {
if (window.attachEvent) {
window.attachEvent('onload', function () {
setTimeout(function () {
rotateImages(1)
}, speed)
});
}
}
</script>
You need to clear the previous timer. Otherwise there will be new timers added with every call to rotateImages(), Save the reference returned from setTimeout() and then use clearTimeout() to clear the timer if it's set
var num = 0;
var temp = 0;
var speed = 5000;
var preloads = [];
var timeout;
preload(
'1.png',
'2.jpg',
'3.jpg',
'4.jpg',
'5.png'
);
function preload() {
for (var c = 0; c < arguments.length; c++) {
preloads[preloads.length] = new Image();
preloads[preloads.length - 1].src = arguments[c];
}
}
function rotateImages(flag) {
if (flag == 0) {
num = num - 1;
if (num < 0)
num = preloads.length;
} else {
num = num + 1;
if (num >= preloads.length)
num = 0;
}
if (num == temp) {
rotateImages(1);
} else {
var str = preloads[num].src;
var resArr = str.split("/");
var picName = resArr[resArr.length - 1];
document.getElementById("intro").style.backgroundImage = 'url(./images/slider/' + picName + ')';
temp = num;
if (!!timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
rotateImages(1);
}, speed);
}
}
if (window.addEventListener) {
window.addEventListener('load', function() {
setTimeout(function() {
rotateImages(1)
}, speed)
}, false);
} else {
if (window.attachEvent) {
window.attachEvent('onload', function() {
setTimeout(function() {
rotateImages(1)
}, speed)
});
}
}

Update progressbar in each loop

I have a progress bar that I update in a loop of many iterations.
https://jsfiddle.net/k29qy0do/32/
(open the console before you click the start button)
var progressbar = {};
$(function () {
progressbar = {
/** initial progress */
progress: 0,
/** maximum width of progressbar */
progress_max: 0,
/** The inner element of the progressbar (filled box). */
$progress_bar: $('#progressbar'),
/** Set the progressbar */
set: function (num) {
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
console.log('percent: ' + this.progress + '% - ' + num + '/' + this.progress_max);
this.$progress_bar.width(String(this.progress) + '%');
}
},
fn_wrap: function (num) {
setTimeout(function() {
this.set(num);
}, 0);
}
};
});
$('#start_button').on('click', function () {
var iterations = 1000000000;
progressbar.progress_max = iterations;
var loop = function () {
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i); //only updates the progressbar in the last iteration
//progressbar.fn_wrap(i); //even worse, since no output to the console is produced
}
}
}
//setTimeout(loop, 0);
loop();
});
The console is updated iteratively as expected.
However, the progressbar is not updating.
The problem is that the browser window seems to 'hang' until the loop finishes.
Only the console is updated, not the progressbar.
I have tried to add the setTimeout, as suggested below, in several places.
But that just makes things worse, because I then do not even get the console to output the progress while executing the loop.
Okay, I found a solution in the answer to this question:
Javascript: How to update a progress bar in a 'for' loop
var i = 0;
(function loop() {
i++;
if (iterations % i === 100) {
progressbar.set(i); //updates the progressbar, even in loop
}
if (i < iterations) {
setTimeout(loop, 0);
}
})();
My solution:
https://jsfiddle.net/ccvs4rer/3/
Lets break this down to steps
Step 1: Clean up HTML
Assuming the purpose of your question is to understand how to work the progress bar and not the styles or the labels (loading, please be patient, etc.). Lets just have the progress bar and the start button.
<div id='progressbar-outer' style="">
<div id='progressbar' style=""></div>
</div>
<button id="start_button">Start</button>
Step 2: The Styles
Lets make the progress bar visible to the user
#progressbar-outer {
height:2em;
border:5px solid #000;
width:15em;
}
#progressbar {
width:0%;
background-color:#F00;
height:100%;
}
Step 3: Using setTimeout where it belongs
In your code, you have used setTimeout to set the value of your progress bar. However, the for loop is still active.
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i); //only updates the progressbar in the last iteration
//progressbar.fn_wrap(i); //even worse, since no output to the console is produced
//setTimeout(function() {
// progressbar.set(i);
//}, 0);
}
}
The use of setTimeout does not affect the rest of the code. Hence, the UI was held hostage till the loop ended. Try the following code.
$('#start_button').on('click', function () {
var iterations = 100;
progressbar.progress_max = iterations;
var loop = function (value) {
progressbar.set(value);
if (value < iterations) setTimeout(function () {
loop(value + 1)
}, 30);
else $('#progressbar').css('background-color', '#0F0');
}
loop(1);
});
Preview
Try this fiddle: https://jsfiddle.net/Ljc3b6rn/4/
What you really want is an Asynchronous loop to allow the browser to update the DOM in between iterations.
JSFiddle: http://jsfiddle.net/u5b6gr1w/
function delayedLoop(collection, delay, callback, context) {
context = context || null;
var i = 0,
nextInteration = function() {
if (i === collection.length) {
return;
}
callback.call(context, collection[i], i);
i++;
setTimeout(nextInteration, delay);
};
nextInteration();
}
Some HTML:
<div class="progress-bar"><div style="width: 0"></div></div>
A splash of CSS:
.progress-bar {
border: 1px solid black;
background-color: #f0f0f0;
}
.progress-bar div {
background-color: red;
height: 1.25em;
}
And some JavaScript to wire things together:
var progressBar = document.querySelector(".progress-bar div"),
items = [1,2,3,4,5,6,7,8,9,10];
delayedLoop(items, 500, function(item, index) {
var width = (item / items.length * 100) + "%";
progressBar.style.width = width;
progressBar.innerHTML = width;
});
My guess would be that all your progress updates are running in the same call stack. While JavaScript code is running, the DOM cannot update. Maybe this question will help you come up with a work-around.
What do you wnat to do? Why do you need it? You should only use a progressbar when you have to wait for something to finish. But we don't know what you do on your page.
If you want to display the progress of an ajax upload:
$.ajax({
...
xhr: function() {
var xhr = $.ajaxSettings.xhr();
$(xhr.upload).bind("progress", function(event) {
var e = event.originalEvent;
var percent = 0;
if (e.lengthComputable)
percent = Math.ceil(e.loaded/e.total*100);
$("#progress").width(percent+"%");
});
return xhr;
}
...
});
For images, you need an ajax call:
$.ajax({
method: "GET",
url: "http://example.com/path/image.jpg",
xhr: function() {/* see the code above*/ }
...
});
For getting the content of an uploaded file:
var reader = new FileReader();
reader.readAsText(uploadedFile);
$(reader).bind("progress", function(e) {
var percent = 0;
if (e.lengthComputable)
percent = Math.ceil(e.loaded/e.total*100);
$("#progress").css("width", percent+"%");
});
For large around of process, like math or appending a lot of divs that will take 10+ secons:
Main.js:
var worker = new Worker("Worker.js");
$(worker).bind("message", function(data) {
$("#progress").width((data*100)+"%");
});
Worker.js:
var total = 43483,
finished = 0,
doStuff = function() {
++finished;
return 1+1;
};
setInterval(function()
{
self.postMessage(finished/total);
}, 100);
for (var i = 0; i < total; ++i)
setTimeout(doStuff, i*10);
Because it's nice, and you want to tell the user there's a progress when there isn't, just animate the div:
$("#progress").animate({width: "100%"}, 3000);
You can use promises to wait until the width is set before continuing the loop.
Updating the progress bar for 1000000000 iterations will be slow if you go 1 by 1, so you might find it useful to decrease the update frequency.
Instead of a for loop, I used a recursive function that loops when the promise has been fulfilled.
set: function (num) {
var deferred = $.Deferred();
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
var self = this;
self.$progress_bar.animate({"width": String(this.progress) + '%'}, "fast", function() {
deferred.resolve();
});
return deferred;
}
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
var i = 0;
progressbar.progress_max = iterations;
var loop = function(){
i+=100000000;
if(i <= iterations){
progressbar.set(i).then(function(){
loop();
}); ;
}
};
loop();
});
https://jsfiddle.net/k29qy0do/34/
You have to use window.requestAnimationFrame, otherwise the browser will block until your loop is finished. The callback passed to requestAnimationFrame will get a timestamp as a parameter which you might be able to use for calculations of the progress.
This are my 2 takes on the question:
Using a web worker. The webworker blob code comes from here
Web worker code:
<script type="text/ww">
function loop(e) {
var data = JSON.parse(e.data);
var i = parseInt(data.i, 10);
var iterations = parseInt(data.iterations, 10);
while (iterations % ++i !== 100 && i <= iterations);
if(i <= iterations) {
self.postMessage(JSON.stringify({ i: i, iterations: iterations }));
}
}
self.onmessage = function(e) {
loop(e);
};
</script>
The code:
var ww = document.querySelector('script[type="text/ww"]'),
code = ww.textContent,
blob = new Blob([code], {type: 'text/javascript'}),
blobUrl = URL.createObjectURL(blob),
worker = new Worker(blobUrl);
worker.onmessage = function(e) {
var data = JSON.parse(e.data);
var i = parseInt(data.i, 10);
var iterations = parseInt(data.iterations, 10);
progressbar.set(i);
worker.postMessage(JSON.stringify({ i: i, iterations: iterations }));
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
progressbar.progress_max = iterations;
worker.postMessage(JSON.stringify({ i: 0, iterations: iterations }));
});
The other idea hangs the UI thread, but changes the width visually, as I use requestAnimationFrame to break the counting, change width of the progressbar, and then continue the count.
function loopFrame(i, iterations) {
requestAnimationFrame(function() {
if (iterations % i === 100) {
progressbar.set(i);
}
if(i < iterations) {
loopFrame(i + 1, iterations);
}
});
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
console.log(iterations);
progressbar.progress_max = iterations;
loopFrame(0, iterations);
});
Maybe this will be usefull.
var service = new Object();
//function with interrupt for show progress of operations
service.progressWhile = new Object();
service.progressWhile.dTime = 50; //step ms between callback display function
service.progressWhile.i = 0; //index
service.progressWhile.timer = 0; //start time for cycle
//#parametr arr - array for actions
//#parametr actionCallback - The function for processing array's elements
//#parametr progressCallback - function to display the array index
function progressWhile(arr, actionCallback, progressCallback) {
try {
var d = new Date();
service.progressWhile.timer = d.getTime();
log(service.progressWhile.i);
if (service.progressWhile.i >= arr.length) {
service.progressWhile.i = 0;
return;
}
while (service.progressWhile.i < arr.length) {
actionCallback(arr[service.progressWhile.i++]);
d = new Date();
if (d.getTime() - service.progressWhile.timer > service.progressWhile.dTime) {
break;
}
}
if (progressCallback != undefined)
progressCallback(service.progressWhile.i);
} catch (er) {
log(er);
return;
}
setTimeout(function () {
progressWhile(arr, actionCallback, progressCallback);
}, 0);
}
Here's updated fiddle
I used animate to make it a progress bar like look and feel.
Hope this will help you.
var progressbar = {};
$(function() {
progressbar = {
/** initial progress */
progress : 0,
/** maximum width of progressbar */
progress_max : 0,
/** The inner element of the progressbar (filled box). */
$progress_bar : $('#progressbar'),
/** Method to set the progressbar.*/
set : function(num) {
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
console.log('percent: ' + this.progress + '% - ' + num + '/' + this.progress_max);
$('#progressbar').animate({
width : String(this.progress) + '%',
}, 500, function() {
// Animation complete.
});
}
},
fn_wrap : function(num) {
setTimeout(function() {
this.set(num);
}, 0);
}
};
});
$('#start_button').on('click', function() {
$('#progressbar').css('width', '0%');
var iterations = 1000000000;
progressbar.progress_max = iterations;
var loop = function() {
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i);
//only updates the progressbar in the last iteration
}
}
}
loop();
});
Fiddler
[1]: https://jsfiddle.net/k29qy0do/21/

plain javascript - increment number using function like $.animate with comma

I am developing a game that will animate the money that is added or subtracted.
The jquery solution is here:
How to increment number using animate with comma using jQuery?
How can I do this in plain javascript?
I want to avoid using jquery
In this solution, no jQuery is needed
var from=40000, to=50000;
function increment() {
setTimeout(function() {
from++;
if(from <= to) {
document.getElementById("test").innerHTML = from;
increment();
}
}, 10);
}
increment();
Please visit the jsfiddle. http://jsfiddle.net/TTaA4/
var counter = 0;
var timer = setInterval(function () {
if (counter >= 100 ) {
clearInterval(timer);
}
counter++;
// call numberWithCommas(counter) to get no.s with comma..
}, 100);
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
the above code for comma function is shamelessly copied from https://stackoverflow.com/a/2901298/2466168
http://jsfiddle.net/2MwSv/
Try it out in your javascript console:
var a = 40000;
var b = 45000;
console.log(commaSeparateNumber(a));
function animate(opts) {
var start = new Date;
var id = setInterval(function () {
var timePassed = new Date - start
var progress = timePassed / opts.duration
if (progress > 1) progress = 1
var delta = progress;
opts.step(delta)
if (progress == 1) {
clearInterval(id)
}
}, opts.delay || 10)
}
function commaSeparateNumber(val) {
while (/(\d+)(\d{3})/.test(val.toString())) {
val = val.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
}
return val;
}
animate({
delay: 10,
duration: 3000,
step: function (progress) {
var difference = b - a;
console.log(commaSeparateNumber(a + Math.round(progress * difference)));
}
});
Sources:
http://javascript.info/tutorial/animation
How to increment number using animate with comma using jQuery?

Extend timeout function when button is clicked

This piece of code works, but I am trying to get the timeout function to reset to '0' every time the button is clicked.
var running = false,
count = 0,
run_for = 700;
var end_counter = function() {
if (running) {
running = false;
$("#status").text("Not Running");
alert(count);
started_at = 0;
}
};
$('button').click(function() {
if (running) {
count++;
} else {
running = true;
$("#status").text("Running");
count = 1;
setTimeout(end_counter, run_for);
}
});
Just cancel and restart it:
var timerId,
count = 0;
function end_counter() {
$("#status").text("Not Running");
alert(count);
count = 0;
}
$('button').click(function() {
$("#status").text("Running");
count++;
clearTimeout(timerId);
timerId = setTimeout(end_counter, 700);
});
var running = false,
count = 0,
run_for = 700;
var timer;
var end_counter = function() {
if (running) {
running = false;
$("#status").text("Not Running");
alert(count);
started_at = 0;
}
};
$('button').click(function() {
clearTimeout(timer);
if (running) {
count++;
timer = setTimeout(end_counter, run_for);
} else {
running = true;
$("#status").text("Running");
count = 1;
timer = setTimeout(end_counter, run_for);
}
});
Set timer to a variable - then clear it on click and restart it after count.

Adding new function objects to array and then calling function method

I am not sure if I used the correct terminology exactly in the question but I am triggering a series of timed function calls using a loop in my playSequence() function and the setTimeOutFunction. This much works, but then I want to have a pause function that will pause all timers and a resume function that will resume all timers. The problem is that when I try to call the function object's pause method in the pauseAllTimers() function it gives error 'Uncaught TypeError: Object 0 has no method 'pause'. Any ideas?
var timers = new Array();
function Timer(callback, delay) {
var timerId, start, remaining = delay;
this.pause = function() {
window.clearTimeout(timerId);
remaining -= new Date() - start;
};
this.resume = function() {
start = new Date();
timerId = window.setTimeout(callback, remaining);
};
this.resume();
}
function pauseAllTimers()
{
for (var timer in timers)
{
timer.pause();
}
}
function resumeAllTimers()
{
for (var timer in timers)
{
timer.resume();
}
}
function playSequence()
{
var totaltimeout = 0;
for (var lesson_step_str in lesson_step)
{
var splitarr = lesson_step[lesson_step_str].split("|||");
var element = splitarr[0];
var txt = splitarr[1];
var timeout = splitarr[2];
totaltimeout += (timeout*1);
console.log(totaltimeout);
console.log(txt);
(function(a,b){
var timer = new Timer(function(){ displayText( a, b); }, totaltimeout * 1000);
timers.push(timer);
})(element, txt);
}
}
Well in for loops timer is returning the index instead of Timer object so you need to do it like this:
timers[timer].pause();
Following is working code:
var timers = new Array();
var Timer = function (callback, delay) {
this.timerId, this.start, this.remaining = delay;
this.pause = function () {
window.clearTimeout(this.timerId);
this.remaining -= new Date() - this.start;
};
this.resume = function () {
this.start = new Date();
this.timerId = window.setTimeout(callback, this.remaining);
};
this.resume();
}
function pauseAllTimers() {
for (var timer in timers) {
timers[timer].pause();
}
}
function resumeAllTimers() {
for (var timer in timers) {
timers[timer].resume();
}
}
function playSequence() {
var totaltimeout = 0;
for (var i=1;i<6; i++) {
var txt = "this is part "+i,
element="#div"+i, timeout=2;
totaltimeout += timeout;
(function (a, b) {
var timer = new Timer(function () {
$("#divTxt").html(b);
}, totaltimeout * 1000);
timers.push(timer);
})(element, txt);
}
}
$(function(){
$("#pauseAll").click(function(){
pauseAllTimers();
});
$("#resumeAll").click(function(){
resumeAllTimers();
});
playSequence();
});

Categories