Javascript/AJAX Asynchronous Loading Spinners - javascript

I am trying to make a basic enough page that allows the user to execute a php script by clicking a button. Each button will have a loading spinner popup on clicking.
My problem is, on clicking one button and then clicking another, both spinners close at the exact same time even though the second may still be processing.
Does anyone know how to make these spinners truly asynchronous ? Thanks so much in advance, its killing me.
JS:
function test(element){
var append = "#";
var test = append.concat(element);
document.getElementById(element).style.visibility='visible';
$.ajax({url:"test.php",success:function(result){
hide(element);
}
});
};
function hide(element){
document.getElementById(element).style.visibility='hidden';
};
</script>
HTML:
<html>
<?
$index = 0;
$myArray = array ("1", "2", "3", "4", "5");
for($index = 0; $index < 5; $index++){?>
<button onclick="test('<?echo $myArray [$index];?>')">Start</button>
<img id="<?echo $myArray [$index];?>" src="images/loader.gif"
style="visibility:hidden"/>
<br><br>
<?}?>
</html>

I would implement a counter. Each time you show the loading indicator, add one to the counter and each time you want to hide it, subtract one. Then monitor the counter and whenever it is above zero show the loading indicator and when at zero hide it. Make sense?

Something like the following (untested) code might do the trick and it neatly means you can avoid worrying about the spinner at all in ajax requests:
var spinningAjax = (function() { // use of the closure created by an immediate function gives us the scope to create a persistant counter variable
var counter = 0;
$(document).ajaxComplete(function() {
counter--;
if (counter === 0) {
showSpinner(false);
}
});
return function(settings) {
counter++;
showSpinner(true);
$.ajax(settings);
}
})();
var showSpinner(bool) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};
EDIT: Ok, having seen the comments to another answer, I realise this doesn't quite solve the situation you're in. I'll have a think and see if I can do better
EDIT2: I think this (still untested, unfortunately) code may be what you require. Please let me know in the comments if you have any issues.
var spinningAjax = (function() { // closure of immediate function lets us create a persistant array of the counters for each spinner
var counter = []; // an array to hold the counters for each spinner
$(document).ajaxComplete(function(event, xhr, settings) { // called whenever any ajax request is completed
if (typeof settings.ajaxGroup !== 'undefined') { // only update the counters if an ajaxGroup has been provided
counter[settings.ajaxGroup]--;
if (counter[settings.ajaxGroup] === 0) {
showSpinner(false, settings.ajaxGroup); // hide spinner when all requests connected with the spinner have been completed
}
}
});
return function(settings) { // this is the function actually assigned to the variable spinningAjax as a result of the immediate function
counter[settings.ajaxGroup] = counter[settings.ajaxGroup] ? counter[settings.ajaxGroup]+1 : 1; // can't just use the ++ operator as this property might not be defined yet
showSpinner(true, settings.ajaxGroup);
$.ajax(settings);
}
})();
var showSpinner(bool, spinnerIdentifier) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};

Related

issues with clearInterval

I cannot seem to get clearInterval to work in the manner that I believe it should. I have been going through so many other examples and questions. In each case I seem to be doing what everyone else is doing, excepting the use of onkeypress. I have also tried various other keypress options with no luck. And I have also flipped around the code a bunch of different times to no avail.
Below is the basic pieces of code I have been working with, much simplified from the larger project. As you might be able to discern, I want it to keep printing "poop " until a keypress stops it. If I hit a button before it prints "poop" the first time, it will stop and print "stopped pooping." If I wait until after it has printed "poop" once, I cannot get it to stop with a keypress.
Please excuse the scatological nature of it.
function pooping() {
document.write("poop ");
}
var pooper = setInterval(pooping, 1000);
document.onkeypress = function() {
clearInterval(pooper);
pooper = 0;
document.write("stopped pooping");
}
Same code, without the document.write() works fine:
'use strict';
let target = document.getElementById('target');
function pooping() {
// Normally would use console.log() here, but wanted the result visible
// in the snippet.
target.textContent += "poop ";
}
var pooper = setInterval(pooping, 1000);
document.onkeypress = function() {
clearInterval(pooper);
pooper = 0;
target.textContent += "stopped pooping";
}
<div id="target"></div>

jquery issue : any css changes before an each loop apply only after

I have some code like following :
LoadingImage.show("#contentpage", urlStk.LoadImg);
var errors = 0;
var ComponentToUpdate = new Array();
var storedItems = JSON.parse(localStorage.getItem("Components"));
$(".myDataGridRow").each(function () {
errors += validateInput(this);
component = {
FamilyCode: $.trim($("td[name=FamilyCode]", this).text())
}
ComponentToUpdate.push(component);
});
LoadingImage.hide("#contentpage");
The $(".myDataGridRow").each()) loop can be a little bit slow. So I try to display some waiting animated gif that overlays on the data grid and its rows (myDataGridRow).
LoadingImage.show() and LoadingImage.hide() methods do work fine when the executed code between is some ajax call to a remote server.
The problem is that the animated gif never appears in this case (the each() loop is only going through HTML elements and performing simpls validations), nor its parent DIV container...
After many tests, it seems that any javascript code written before the each() loop seems to be executed after (I have not tried the alert() case, but any css changes on other elements are blocked till the each() loop finishes, timers declared before are triggered after... ) ??
Forcing the display of the waiting image inside the each loop does not work.
Any help idea will be welcome.
At a guess, your animation isn't playing because the JavaScript is running in the main thread. If you use setTimeout() or setImmediate(), you can run your expensive code in a separate thread, which will allow the browser itself to handle displaying the animation.
Example below:
LoadingImage.show("#contentpage", urlStk.LoadImg);
setImmediate(function () {
var errors = 0;
var ComponentToUpdate = new Array();
var storedItems = JSON.parse(localStorage.getItem("Components"));
$(".myDataGridRow").each(function () {
errors += validateInput(this);
component = {
FamilyCode: $.trim($("td[name=FamilyCode]", this).text())
}
ComponentToUpdate.push(component);
});
LoadingImage.hide("#contentpage");
});

setTimeout in methods of multiple objects of the same type

I need help with the use of "setTimeout" in the methods of the objects of the same type. I use this code to initiate my objects:
function myObject(param){
this.content = document.createElement('div');
this.content.style.opacity = 0;
this.content.innerHTML = param;
document.body.appendChild(this.content);
this.show = function(){
if(this.content.style.opacity < 1){
this.content.style.opacity = (parseFloat(this.content.style.opacity) + 0.1).toFixed(1);
that = this;
setTimeout(function(){that.show();},100);
}
}
this.hide = function(){
if(this.content.style.opacity > 0){
this.content.style.opacity = (parseFloat(this.content.style.opacity) - 0.1).toFixed(1);
that = this;
setTimeout(function(){that.hide();},100);
}
}
}
Somewhere I have 2 objects:
obj1 = new myObject('Something here');
obj2 = new myObject('Something else here');
Somewhere in the HTML code I use them:
<button onclick="obj1.show()">Something here</button>
<button onclick="obj2.show()">Something else here</button>
When the user presses one button, everything goes OK, but if the user presses one button and after a short time interval he presses the other one, the action triggered by the first button stops and only the action of the second button is executed.
I understand that the global variable "that" becomes the refence of the second object, but I don't know how to create an automatic mechanism that wouldn't block the previously called methods.
Thank you in advance and sorry for my English if I made some mistakes :P
If you need something cancellable, use window.setInterval instead of setTimeout. setInterval returns a handle to the interval which can then be used to cancel the interval later:
var global_intervalHandler = window.setInterval(function() { ... }, millisecondsTotal);
// more code ...
// later, to cancel this guy:
window.clearInterval(global_intervalHandler);
So from here I'm sure you can use your engineering skills and creativity to make your own self expiring operations - if they execute and complete successfully (or even unsuccessfully) they cancel their own interval. If another process intervenes, it can cancel the interval first and hten fire its behavior.
There are several ways to handle something like this, here's just one off the top of my head.
First of all, I see you're writing anonymous functions to put inside the setTimeout. I find it more elegant to bind a method of my object to its scope and send that to setTimeout. There's lots of ways to do hitching, but soon bind() will become standard (you can write this into your own support libraries yourself for browser compatibility). Doing things this way would keep your variables in their own scope (no "that" variable in the global scope) and go a long way to avoiding bugs like this. For example:
function myObject(param){
// ... snip
this.show = function(){
if(this.content.style.opacity < 1){
this.content.style.opacity = (parseFloat(this.content.style.opacity) + 0.1).toFixed(1);
setTimeout(this.show.bind(this),100);
}
}
this.hide = function(){
if(this.content.style.opacity > 0){
this.content.style.opacity = (parseFloat(this.content.style.opacity) - 0.1).toFixed(1);
setTimeout(this.hide.bind(this),100);
}
}
}
Second, you probably want to add some animation-handling methods to your object. setTimeout returns handles you can use to cancel the scheduled callback. If you implement something like this.registerTimeout() and this.cancelTimeout() that can help you make sure only one thing is going on at a time and insulate your code's behavior from frenetic user clicking like what you describe.
Do you need that as global variable ? just change to var that = this; you will use variable inside of the function context.

Javascript Recursion

I have an ajax call and would like to recall it once I finish parsing and animating the result into the page. And that's where I'm getting stuck.
I was able to recall the function, but it seems to not take into account the delays in the animation. i.e. The console keeps outputting the values at a wild pace.
I thought setInterval might help with the interval being the sum of the length of my delays, but I can't get that to work...
function loadEm(){
var result=new Array();
$.getJSON("jsonCall.php",function(results){
$.each(results, function(i, res){
rand = (Math.floor(Math.random()*11)*1000)+2000;
fullRand += rand;
console.log(fullRand);
$("tr:first").delay(rand).queue(function(next) {
doStuff(res);
next();
});
});
var int=self.setInterval("loadEm()",fullRand);
});
}
});
use setTimeout. setInterval will call again... and again... and again...
use var fullRand at the top of loadElm (or inside the JSON callback). Without it you will increment the same global variable each time loadElm is called. In fact, you should use var with all your local variables -- that includes rand here. The Jibbering JavaScript Closure Notes covers variables and much more.
use setTimeout(loadElm, fullRand) -- don't use a string in the first parameter :) See the MDC setTimeout documentation.
Check for JavaScript errors (keep firebug/IE developer tools/the error console handy)
Instead of using setTimeout, consider keeping count of how many of the animations have finished vs. how many total have finished! Imagine this:
===
// closure in JSON callback. see Jibbering notes.
var count = 0
$.each(results, function(i, res) {
var rand = (Math.floor(Math.random()*11)*1000)+2000
count++ // have to do action
$("tr:first").delay(rand).queue(function(next) {
...
count-- // action done
if (!count) { // means count == 0 here :-) then...
loadElm() // ...all actions done
}
})
})
if (!count) {
// err... no animations started :(
}
Happy coding.

Icefaces and javascript bridge

I'm facing a problem with Icefaces and it's javascript bridge.
I don't know what are the changes which made by this bridge after i made a changes in the server-side.
For example: I have a ice:panelPopup component in my page with the visible attribute = "#{bean.customPopUp}". If i changed the "bean.customPopUp" to be "true" the popup is displayed correctly, but what i need to know : what happened in the client, in other word, i need to know if the popup is displayed i need to do some client processing using javascript
I've been trying to find a solution for component level callbacks also. There doesn't appear to be a good solution to this problem. I've resorted to initiating a recursive polling function in Javascript that handles my task after it detects an update to my component. My backing bean starts the poller() and it runs every 500ms until the component update has occurred.
var pollingCount = 0;
var previousValue;
function poller() {
// Kill poller after 30 seconds
if (pollingCount >= 60) {
pollingCount = 0;
return;
}
var currentValue = document.getElementById('myInputElement').value;
if (previousValue != currentValue) {
previousValue = currentValue;
pollingCount = 0;
myFunction();
}
else {
pollingCount++;
setTimeout('poller()', 500);
}
}
My backing bean:
updateDataModel(); // Causes 'myInputElement' component to update
FacesContext fc = FacesContext.getCurrentInstance();
JavascriptContext.addJavascriptCall(fc, "poller();");
I don't like this solution very much, but there don't appear to be any great answers at this time.

Categories

Resources