JQuery progress bar pauses when browser tab is changed - javascript

I'm trying to display a progress bar on a html page using javascript. However,
when the browser tab containing the code becomes inactive, the progress bar stops updating,
being resumed when the tab is active again.
How can I prevent the browser from stopping/pausing the execution of javascript code when the window is inactive?
Although it may be irrelevant, here is the code:
Object.progressBar = function(){
$( "#question-progress-bar" ).progressbar({
value: false,
complete: function(event, ui) { ... }
});
var seconds = 15.0,
progressbar = $("#question-progress-bar"),
progressbarValue = progressbar.find(".ui-progressbar-value");
progressbarValue.css({
"background": '#c5b100',
"opacity" : '0.8'
})
var int = setInterval(function() {
var percent = (15-seconds)/15*100;
seconds=seconds-0.1;
progressbar.progressbar( "option", {
value: Math.ceil(percent)
});
$("#question-progress-bar-seconds").html((seconds).toFixed(1)+"s");
if (seconds <= 0.1) {
clearInterval(int);
}
}, 100);
}

Instead of using setInterval and assuming a certain amount of time has passed between calls (even when it's up front, setInterval has hit or miss accuracy) use the Date object to get a time when the bar starts, and compare that to the current time at each iteration.
<html>
<head>
<script>
function go()
{
var pb = new ProgressBar(5, "targ");
}
window.onload = go;
function ProgressBar(l, t)
{
var start = Date.now();
var length = l * 1000;
var targ = document.getElementById(t);
var it = window.setInterval(interval, 10);
function interval()
{
var p = 100 * (Date.now() - start) / length;
if(p > 100)
{
p = 100;
window.clearInterval(it);
alert("DONE"); // alternatively send an AJAX request here to alert the server
}
targ.value = (Math.round(p) + "%");
}
}
</script>
</head>
<body>
<input type="text" id="targ" />
</body>
</html>
I've made an example object, here, that immediately starts a countdown when instantiated and calls an alert and kills the interval timer when done. Alternatively an AJAX call, or any other sort of call can be done upon completion.
It should be noted that this will NOT complete the call if the browser stops Javascript all together. It will, however, complete it as soon as the tab has been given focus again if enough time has passed in the interim. There is no way for a website to alter this sort of browser behavior from the scripting side.
Hope that helps!

Related

Show block (with page breaks) for fixed time

I want to set up a block with a number of questions (and page breaks), which after exactly 2 minutes will progress to the next block, regardless of what the subject has accomplished / clicked / performed in that block.
I set variables in the embedded data: TimeLeft1 = 120, TimeFlag1 = 0, and wrote the following code in each question in the block-
Qualtrics.SurveyEngine.addOnload(function()
{
var timeleft1 = parseInt("${e://Field/TimeLeft1}");
var timeflag1 = parseInt("${e://Field/TimeFlag1}");
var timer = setInterval(function(){
if (timeleft1<=0){
clearInterval(timer);
timeflag1 = 1;
$('NextButton').click();
}
timeleft1--;
}, 1000);
$('NextButton').onclick = function(event){
Qualtrics.SurveyEngine.setEmbeddedData('TimeLeft1',timeleft1);
Qualtrics.SurveyEngine.setEmbeddedData("TimeFlag1",timeflag1);
}
});
after that, I insert display logic, which display "if flag = 1".
Unfortunately, the survey doesn't proceed after 2 minutes, and the block that should appear, not appear..
If anyone can help, I would be very grateful!

ajax undefined after a thousand iterations

When creating an animation I try to use javascript for additional effects, namely snow being piled up and falling off the edges of the foreground. I figured that the javascript could do the "calculations" on a <canvas> that had the image, then send the "snow" to a php script that would create the png images. This is all placed on my local "server" because that is the only one that can write files.
<html>
<head>
<title>Making Snow</title>
</head>
<body bgcolor="black">
<canvas id="canvas" width="1920px" height="1080px"></canvas>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
var canvas;
var ctx;
var frame=-530;
var simg = new Image()
var dimg = new Image()
onload = function()
{
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
simg.src = "prodB/C/DGB.0530.png"
}
simg.onload = function()
{
var ipo=3;
// Initialize all pixels on the screen/page
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(simg,0,0);
document.title = "Making Snow " + (frame+530) + " / 7000";
snowdraw();
}
dimg.onerror = function()
{
dimg.src = "../imagewriting/snow" + zeroFill(frame+530,4) + ".png";
}
dimg.onload = function()
{
frame++;
if(frame<530)
simg.src = "prodB/C/DGB.0530.png"
else if(frame>4400)
simg.src = "prodB/C/DGB.4400.png"
else
simg.src = "prodB/C/DGB." + zeroFill(frame,4) + ".png"
}
var snowdraw = function()
{
var temp;
var canvasData = "";
// console.log(screen);
// Animation
// Choose a random pixel at the top of the screen
if(frame<7000)
{
for(ny=canvas.height-2; ny>=0; ny--)
{ // Iterate through all the pixels starting from the bottom right corner.
for(nx=canvas.width-2; nx>=0; nx--)
{
canvasData=canvasData+"1";
}
}
$.ajax({
method: "POST",
url: "makesnow.php",
data:{ imgData: canvasData, frame: (frame+530) }
})
.done(function( msg ) {
dimg.src = "../imagewriting/snow" + zeroFill(frame+530,4) + ".png";
});
}
else
{
document.title = "COMPLETE";
}
}
// http://stackoverflow.com/questions/1267283/how-can-i-create-a-zerofilled-value-using-javascript
// by Peter Bailey http://stackoverflow.com/users/8815
function zeroFill( number, width )
{
width -= number.toString().length;
if ( width > 0 )
{
return new Array( width + (/\./.test( number ) ? 2 : 1) ).join( '0' ) + number;
}
return number + ""; // always return a string
}
</script>
</html>
However, on the 1640th frame (or more precisely frame=1110) ajax is suddenly undefined. The image snow1640.png is created, but the browser tells me ajax is not defined and won't advance passed Making Snow 1640 / 7000. Because of the small random nature for each "snow flake" I can't just pick it up from where I left off, as the snow would jump from one frame to the next. Though I did try that at one point and ajax still stopped after that frame.
I first ran it on the local machine running Firefox (http://127.0.0.1/...) then moved onto another machine on the network which is more powerful running Chrome and both died on that same frame. Thought it might be a memory or file limit so I moved the complete frames out of there. Still nothing.
EDIT: Code now snippit of just the problem.
Also, console.log for data and ajax.responseText seem to be generally empty, both during successful "renders" and when it starts iterating ajax is not defined (every other line empty, every other error).
EDIT: New development! Turns out that the error ReferenceError: ajax is not defined anyways gets called between frames (and the 404 when asking dimg if image was created).
EDIT: By typing in console.log($.ajax) after onload and commenting out simg.src I got function ajax().
Error persists, this time I expanded it to reveal the following:
success http://127.0.0.1/ag/makesnowBC.html 197:7
j http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 2:27131
fireWith http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 2:27949
x http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 4:22242
b http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 4:26298
EDIT: Changed the code to now use Synchronous ajax. This time there are no error messages what so ever, but it still stops at 1640.

Using javascript/jQuery, wait 3 seconds for click, then proceed

I've been trying to figure out how to run an infinite loop while pausing for user click, then allow for a break out.
When the loop starts, the user is presented with an image, and must choose the identical image from one of 4 displayed. If they successfully click the match within 5 seconds, they are presented another image, and the game goes on.
If they either choose an incorrect image, or 5 seconds elapses, the game ends.
I've got all of the functionality worked out, except this pause while waiting for a click or the time to expire.
Ideally, I'd also like the time to be adjustable on each iteration. Say start at 5 seconds, then shorten the time slightly (10ms) on each loop.
I believe it must be solvable using setTimeout() or setInterval(), but just can't wrap my head around it.
Here is a minimal concept of what I'm trying to accomplish.
$('#playnow').on('click',function(){
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
/* create array of images */
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
var runnow = setInterval(
function(){
//get random image from loaded theme
rand_img = imgs[Math.floor(Math.random() * imgs.length) ];
//display chosen image
$('#goal_image').html('<img src="'+theme_dir+rand_img+'" />');
// wait up to 5 seconds for user to click or time to expire
if(*clicked and matched*){
//get new random image and reset timer (less 10ms)
}
if(*time expired*){
//bail out and game ends
}
/* reduce time */
speed -= speed_reduce;
},
speed);
});
You'll want something like this I think:
var speed = 5000, // the initial time
currentimage,
timer,
gamerunning;
function newimage(){
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
currentimage=Math.floor(Math.random() * imgs.length);
$('#goal_image').html('<img src="'+theme_dir+imgs[currentimage]+'" />');
timer = setTimeout(speed, lost)
}
function answer(id){
if(!gamerunning){return}
clearTimeout(timer)
if(id==currentimage){
speed -= 10; // time decrease every time.
newimage();
}else{
lost()
}
}
function lost(){
gamerunning=0;
speed=5000;
// what to do when lost.
}
$("#puppy").on("click",function(){answer(0)}); // here #puppy is the id of the answer image, and 0 the index in the imgs array.
$("#kitten").on("click",function(){answer(1)});
$("#bunny").on("click",function(){answer(2)});
$("#fish").on("click",function(){answer(3)});
$("#gamestartbutton").on("click",function(){gamerunning=1})
One way to solve this problem is to use setTimeout() and clearTimeout() rather than setInterval. Also, you need some event for the successful button click (I've pretended you have a special "#successfulmatch" button):
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
var myTimeout;
function runNow(speed){
rand_img = imgs[Math.floor(Math.random() * imgs.length) ];
$('#goal_image').html('<img src="'+theme_dir+rand_img+'" />');
// Keep track of the timeout so we can cancel it later if the user clicks fast enough.
myTimeout = window.setTimeout(function(){
game_running = false;
gameEnds();
},speed);
}
$('#successfulmatch').on('click',function(){
if(game_running){
// Cancel the timeout because the user was fast enough
window.clearTimeout(myTimeout);
// Give the user less time than before
runNow(speed - speed_reduce);
}
else{
// Throw an error: you forgot to hide the clickable buttons when the game ended.
}
}
$('#playnow').on('click',function(){
runNow(speed);
}
Looks like you are mixing the logic for checking "has the user clicked the image? was it correct?" with the one for checking "has time expired?"
You can listen for onclick events on the images
and set a timeout event for the game over
so the user has to cancel that timer, to cancel imminent game over, by clicking on the images
if the right image is clicked the timer is reset
if not, it's game over
you can cancel a timeout event before it runs with cancelTimeout()
see W3C here for a reference.
here is a quick prototype:
$('#playnow').on('click', function() {
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
/* create array of images */
var imgs = ['puppy.png', 'kitten.png', 'bunny.png', 'goldfish.png'];
// function that ends the game if it's called
function gameover() {
alert("GAME OVER");
game_running = false;
}
// in order to use clearTimeout() you must store the timer in a global variable
// setting a timeout that will end the game if it's not cleared before
window.timer = setTimeout(gameover, speed);
// function that is called whenever the user clicks on a image
function onclickimage(event) {
if (!game_running) return;
if ( /*clicked right img*/ ) {
// get random image from loaded theme
var rand_img = imgs[Math.floor(Math.random() * imgs.length)];
// display chosen image
$('#goal_image').html('<img src="' + theme_dir + rand_img + '" />');
// delete timer, user now has one more opportunity
clearTimeout(timer);
// speed is less 10ms
speed -= speed_reduce;
// launch timer again
window.gametimer = setTimeout(loop, speed);
} else { // if click did not match correct image
gameover();
}
}
});
Well, firstly, you need to clearInterval() when they either click or fail in order to stop the current interval. Then, you can restart an interval with the new speed. The interval seems to be working for.
Every 5 seconds a new picture is displayed. So, you want an onclick event for the picture that clears the interval and starts a new one. So, you may want to use setTimeout instead of setInterval since it is only a single iteration at a time.
You could use setInterval, I suppose, but there's no real benefit to it. This way also makes it relatively easy to reduce the speed each time.

Combine ASP.net AJAX timer with javascript countdown

I am currently working on porting a vb.net winforms program over to a web based version, and one of the functions in the original program has be stumped.
In the original program, every 5 minutes, a form pops up for user input. There is also a label control on the main form which counts down to the next popup. This is accomplished with a single timer control with a 1 second duration. every tick, it decrements the countdown, and when the countdown reaches 0, it pops up the form and then resets. Simple enough, but in my web app, I can't afford to be doing a postback every second, so what I am attempting is to combine a javascript countdown widget with an AJAX timer. Essentially, what should happen is that when the page loads, the countdown begins decrementing from 300 seconds, and the AJAX timer begins with a duration of 300 seconds. My idea is that when the timer ticks, it will run my function, as well as reset the countdown to 300 seconds again.
My problem, is that I am not able to reset the countdown with the code that I have, and I know that I am doing something (likely very simple) wrong, but I don't know enough Java to know what.
If I hardcode the Timer var to 300, the countdown works, and the timer ticks (fires the additional functons), but the countdown just keeps counting down (into negative numbers). How do I reset the countdown variable from code behind?
Here is the countdown function
var Timer = <%= CountDown %>;
function updateClock() {
// Update Countdown
Timer -= 1;
var TimerMin = Math.floor(Timer / 60);
var TimerSec = Timer - (TimerMin * 60);
TimerSec = (TimerSec < 10 ? "0" : "") + TimerSec;
var TimerFormat = TimerMin + ":" + TimerSec;
// Update the countdown display
document.getElementById("javaCountdown").firstChild.nodeValue = TimerFormat
}
Here is the body code
<body onload="updateClock(); setInterval('updateClock()', 1000 )">
And the Code Behind
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Countdown = 300
End Sub
PProtected Sub Timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer1.Tick
Countdown = 300
'Additional Functions
End Sub
This solution uses jQuery.
<script>
var intervalSecond = null;
var interval5minutes = null;
$(document).ready(function() {
// enable both intervals
enableIntervals();
// onsubmit event for your form
$("#popupForm").submit(function() {
// hide the form again
$("#popupForm").css("display", "none");
// enable intervals again.
enableIntervals();
});
});
function enableIntervals() {
// every second interval
intervalSecond = setInterval(function() {
$("#updateMeEverySecond").html(new Date());
}, 1000);
// every 5 minutes interval
interval5minutes = setInterval(function() {
// display form and shut off the interval timers
$("#popupForm").css("display", "block");
clearInterval(intervalSecond);
clearInterval(interval5minutes);
}, 5 * 60 * 1000);
}
</script>
<div id="popupForm" style="display:none;">
<form>
<input type="text" />
</form>
</div>
<label id="updateMeEverySecond">Test</label>

Asset.images slow? How do I execute functions so they don't freeze the browser?

I recently downloaded a nice mootools plugin to provide a rating system for search results on my site: MooStarRating.
It works quite well, but it is very slow to initialize. Here are the execution times I have logged (for pulling back 50 search results).
======== starrating ========
init: 0.06ms 50
img: 5.40ms 50
str: 0.54ms 50
each: 3.04ms 50
inject: 0.86ms 50
end: 1.52ms 50
subtotal: 11.42ms 50
-----------------
total: 571.00ms
Here is the initialize function these logs refer to (just for reference):
initialize: function (options) {
lstart("starrating");
// Setup options
this.setOptions(options);
// Fix image folder
if ((this.options.imageFolder.length != 0) && (this.options.imageFolder.substr(-1) != "/"))
this.options.imageFolder += "/";
// Hover image as full if none specified
if (this.options.imageHover == null) this.options.imageHover = this.options.imageFull;
lrec("init");
// Preload images
try {
Asset.images([
this.options.imageEmpty,
this.options.imageFull,
this.options.imageHover
]);
} catch (e) { };
lrec("img");
// Build radio selector
var formQuery = this.options.form;
this.options.form = $(formQuery);
if (!this.options.form) this.options.form = $$('form[name=' + formQuery + "]")[0];
if (this.options.form) {
var uniqueId = 'star_' + String.uniqueID();
this.options.form.addClass(uniqueId);
this.options.selector += 'form.' + uniqueId + ' ';
}
this.options.selector += 'input[type=radio][name=' + this.options.radios + "]";
// Loop elements
var i = 0;
var me = this;
var lastElement = null;
var count = $$(this.options.selector).length;
var width = this.options.width.toInt();
var widthOdd = width;
var height = this.options.height.toInt();
if (this.options.half) {
width = (width / 2).toInt();
widthOdd = widthOdd - width;
}
lrec("str");
$$(this.options.selector).each(function (item) {
// Add item to radio list
this.radios[i] = item;
if (item.get('checked')) this.currentIndex = i;
// If disabled, whole star rating control is disabled
if (item.get('disabled')) this.options.disabled = true;
// Hide and replace
item.setStyle('display', 'none');
this.stars[i] = new Element('a').addClass(this.options.linksClass);
this.stars[i].store('ratingIndex', i);
this.stars[i].setStyles({
'background-image': 'url("' + this.options.imageEmpty + '")',
'background-repeat': 'no-repeat',
'display': 'inline-block',
'width': ((this.options.half && (i % 2)) ? widthOdd : width),
'height': height
});
if (this.options.half)
this.stars[i].setStyle('background-position', ((i % 2) ? '-' + width + 'px 0' : '0 0'));
this.stars[i].addEvents({
'mouseenter': function () { me.starEnter(this.retrieve('ratingIndex')); },
'mouseleave': function () { me.starLeave(); }
});
// Tip
if (this.options.tip) {
var title = this.options.tip;
title = title.replace('[VALUE]', item.get('value'));
title = title.replace('[COUNT]', count);
if (this.options.tipTarget) this.stars[i].store('ratingTip', title);
else this.stars[i].setProperty('title', title);
}
// Click event
var that = this;
this.stars[i].addEvent('click', function () {
if (!that.options.disabled) {
me.setCurrentIndex(this.retrieve('ratingIndex'));
me.fireEvent('click', me.getValue());
}
});
// Go on
lastElement = item;
i++;
}, this);
lrec("each");
// Inject items
$$(this.stars).each(function (star, index) {
star.inject(lastElement, 'after');
lastElement = star;
}, this);
lrec("inject");
// Enable / disable
if (this.options.disabled) this.disable(); else this.enable();
// Fill stars
this.fillStars();
lrec("end");
return this;
},
So, the slowest part of the function is this:
// Preload images
try {
Asset.images([
this.options.imageEmpty,
this.options.imageFull,
this.options.imageHover
]);
} catch (e) { };
Which is strange. What does Asset.images do? Does the script block until these images have been loaded by the browser? Is there a way to preload images that runs faster?
How can I make the scripts on my page execute faster? It is a big problem for them to take 800ms to execute, but 200ms is still quite bad. At the moment, my search results all pop into existence at once. Is it possible to make it so that individual search results are created separately, so that they don't block the browser while being created? Similarly, is it possible to do this for the individual components of the search results, such as the MooStarRating plugin?
no. Asset.images is non-blocking as each of these gets loaded separately and a singular event is being dispatched when all done.
the speed for loading will be dependent on the browser but it be will multi-threaded to whatever capability it has for parallel downloading from the same host.
https://github.com/mootools/mootools-more/blob/master/Source/Utilities/Assets.js#L115-129
immediately, it returns an Element collection with the PROMISE of elements that may still be downloading. that's fine - you can use it to inject els, attach events, classes etc - you just cannot read image properties yet like width, height.
each individual image has it's own onload that fires an onProgress and when all done, an onComplete for the lot - i would advise you to enable that, remove the try/catch block and see which image is creating a delay. you certainly don't need to wait for anything from Asset.images to come back.
you also seem to be using it as a 'prime the cache' method, more than anything - as you are NOT really saving a reference into the class instance. your 'each' iteration can probably be optimised so it uses half the time if objects and functions are cached and so are references. probably more if you can use event delegation.
To answer your questions about not freezing the browser due to the single-threaded nature of javascript, you defer the code via setTimeout (or Function.delay in mootools) with a timer set to 0 or a 10ms due to browser interpretations. You also write the function to to exec a callback when done, in which you can pass the function result, if any (think ajax!).

Categories

Resources