Asynchronously updating a Bootstrap progress bar with jQuery's $.ajax - javascript

I have a script that loops through IPs on my local network, checking if anything is there. Every iteration, I submit an AJAX request to get the HTTP status code using cURL, which is returned to my Javascript. I already have built functions to calculate where the progress bar should be, however it only updates the progress bar when the entire script is finished executing.
Here's what I have so far (I'm only using 0-23 in this example because I'm on 199.235.130.22 and I return '200')
function updateProgress(percentage){
document.getElementById('progressBar').style.width = percentage+'%';
$('#progressText').html(percentage+'%');
}
for(host = 0; host <= 23; host++){
ipToCheck = network_addr+'130.'+host;
updateProgress(100/host);
$.ajax({
type: 'GET',
url: 'js/scanhelper.php',
data: {
ip: ipToCheck
}
}).done(function(msg) {
updateProgress(100/host);
if(msg!=0){
logSuccess(ipToCheck);
}
});
pausecomp(200); //Just a sleep function to simulate actual processing
}
My Bootstrap HTML is simply
<div class="progress progress-striped active" style="height:44px;">
<div id="progressBar" class="bar" style="width:1%;"></div>
</div>
And, if it matters, my cURL PHP script is here: http://pastebin.com/JRZckdVb
What this should do is, on every iteration, update the progress bar's width to 100 (as in 100%) divided by the current iteration. It may not be the correct math, but the point is it's only updating after all iterations are complete, freezing the page while it's running.
How can I fix this?

aren't you dividing by zero here when host = 0 in the for loop?
updateProgress(100/host);
you can use a variable hosts to keep track of the number of hosts you have.Then the progress will be as below.
var hosts = 23;// total number of hosts
updateProgress((host/hosts)*100);
The other thing is the ajax you're firing is asynchronous, so what's happening is it fires and doesn't wait for the results. You can either "scan" each host serially one at a time updating the progress bar or scan all of them simultaneously having the progress bar update as the asynch results come back. Can you specify which behavior you're trying to achieve?
[UPDATE]
toggle async flag in the ajax call below for what you want.
function updateProgress(percentage){
if(percentage > 100) percentage = 100;
$('#progressBar').css('width', percentage+'%');
$('#progressBar').html(percentage+'%');
}
var hosts = 23;
var hostsDone = 0;
for(host = 0; host <= hosts; host++){
ipToCheck = network_addr+'130.'+host;
$.ajax({
type: 'GET',
url: 'js/scanhelper.php',
async:true,
data: {
ip: ipToCheck
}
}).done(function(msg) {
hostsDone++;
updateProgress((hostsDone/hosts)*100);
if(msg!=0){
logSuccess(ipToCheck);
}
});
}
if you're looking for visuals you should set the height of '#progressBar' to something non-zero and maybe background green.
<div class="progress progress-striped active" style="height:44px;">
<div id="progressBar" class="bar" style="height:44px;width:1%;background-color:green"></div>
</div>

The answer from #zerObit are onli 2/3 of the thrue! You have also to set aria-valuenow.
In some cases, the progress bar would be hidden.
You can do this by:
$('#progressBar').attr('aria-valuenow', percentage);

Related

Refreshing a div every 3 seconds

I'm trying to refresh this AnyChart function div every 3 seconds, so that when data is entered using the (hopefully) interactive wordcloud on a wesbite, it refreshes automatcially. My code currently duplicates the div once, 3 seconds after the page loads. Thanks in advance for any help.
anychart.onDocumentReady(function() {
anychart.data.loadGoogleSpreadsheet({
key: '14jdnD4rzxPdZfLqpeh4gjtoN6ERyZuovBOoFOwuZQAM',
sheet: '2'
}, function(data) {
var chart = anychart.tagCloud();
// configure a color scale here
var customColorScale = anychart.scales.linearColor();
customColorScale.colors(["#FEBF57", "#428A46", "#21C5B4", "#E7438A", "#21295E"]);
// set the color scale as the color scale of the chart
chart.colorScale(customColorScale);
// add a color range
chart.colorRange().enabled(true);
chart.normal().fontFamily("Rubik", "sans-serif");
// set data
chart.data(data);
chart.container('wordcloud-container');
chart.draw();
});
});
$(document).ready(function() {
function refresh() {
var div = $('#wordcloud-container'),
divHtml = div.html();
div.html(divHtml);
}
setInterval(function() {
refresh()
}, 3000);
})
<h1>TEST WordCloud with colours</h1>
<div id='wordcloud-container'>
The issue is because your refresh() function simply gets the existing HTML of the container and then sets that same HTML within the container again, duplicating the content.
To actually refresh the chart you need to call loadGoogleSpreadsheet() from within the interval handler.
An important change to make here is to use setTimeout() within the chart callback instead of setInterval(). The latter can end up queueing requests if the response is slow, which can lead to your site becoming unresponsive due to too many in-progress requests. The setTimeout() approach will only start the timer when the previous request was successfully handled.
let loadChartData = () => {
console.log('loading data...');
anychart.data.loadGoogleSpreadsheet({
key: '14jdnD4rzxPdZfLqpeh4gjtoN6ERyZuovBOoFOwuZQAM',
sheet: '2'
}, function(data) {
var chart = anychart.tagCloud();
// configure a color scale here
var customColorScale = anychart.scales.linearColor();
customColorScale.colors(["#FEBF57", "#428A46", "#21C5B4", "#E7438A", "#21295E"]);
// set the color scale as the color scale of the chart
chart.colorScale(customColorScale);
// add a color range
chart.colorRange().enabled(true);
chart.normal().fontFamily("Rubik", "sans-serif");
// set data
chart.data(data);
$('#wordcloud-container').empty(); // important change here to stop duplicating the chart
chart.container('wordcloud-container');
chart.draw();
setTimeout(loadChartData, 3000);
});
}
$(document).ready(function() {
loadChartData();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.8.0/js/anychart-bundle.min.js"></script>
<link rel="stylesheet" href="https://cdn.anychart.com/releases/8.8.0/css/anychart-ui.min.css" />
<h1>TEST WordCloud with colours</h1>
<div id='wordcloud-container'></div>
One big issue to note here is that your approach of AJAX polling is not at all scalable. If this is implemented in a production system you will most likely hit rate limits on the Google docs API, or even be blocked for a potential DDOS risk.

How can I process the ajax request only when needed, not every amount of sec

Basically, I am developing an ajax chat (php, mysql, javascript, ajax)
there is part of the code where I fetch all chat messages inside a div by using ajax, the ajax function is running every 2 sec.
My main problem is that the div is scrolling down every 2 sec, I need it to scroll down only when there is new entry (scroll down for all chat users not me only)
function loadLog(){
var oldscrollHeight = $("#chatbox").attr("scrollHeight") - 20;
$.ajax({
url: "ajaxchat.php",
cache: false,
success: function(html){
$("#chatbox").html(html); //Insert chat log into the #chatbox div
var newscrollHeight = $("#chatbox").attr("scrollHeight") - 20;
if(newscrollHeight > oldscrollHeight){
$("#chatbox").animate({ scrollTop: newscrollHeight }, 'normal'); //i need to scroll if only there is new entry not every 2.5 sec
}
},
});
}
setInterval (loadLog, 2500); //Reload file every 2.5 seconds
A simple solution would be to check the new HTML is different to the old HTML before updating the chatbox. This will ensure that the chatbox is only updated when new content is fetched from the server. However if you response HTML is inconsistent this will not work.
var previousChatHTML = '';
function showChatMessages(html) {
if(previousChatHTML === html){
return;
}
var oldscrollHeight = $("#chatbox").attr("scrollHeight") - 20;
$("#chatbox").html(html); //Insert chat log into the #chatbox div
previousChatHTML = html;
var newscrollHeight = $("#chatbox").attr("scrollHeight") - 20;
if(newscrollHeight > oldscrollHeight){
$("#chatbox").animate({ scrollTop: newscrollHeight }, 'normal'); //i need to scroll if only there is new entry not every 2.5 sec
}
}
function loadLog(){
$.ajax({
url: "ajaxchat.php",
cache: false,
success: showChatMessages,
});
}
setInterval (loadLog, 2500); //Reload file every 2.5 seconds
Also when creating pollers in JS you may want to use setTimeout (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) instead of setInterval as on slow connections you can get odd results (if a request takes longer than 2.5s it can resolve in the wrong order).
You can't do this with LAMP
You need response to events from server.
You can do this with this stack
NodeJS: Javascript on serverside / https://nodejs.org/es/
Socket.IO: Realtime server/client comunication: https://socket.io/
Firebase: BaaS Database for realtime comunications https://firebase.google.com/
Your server need response to database events.
PHP response to Client events.

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.

Any references how to load pictures in jquery on swift not lagging?

I don't have any problems such as an error but it's too lagging on the page. Here is my js:
$(document).ready( function() {
photosdone();
});
function photosdone() {
setTimeout( function() {
photosupdates();
photosdone();
}, 800);
}
function photosupdates() {
$.getJSON("check_photos.php", { id: $("#id").val() },function(data) {
$("#photos").empty();
$.each(data.result, function(){
$("#photos").append("<li class='col-lg-3 col-md-3 '><img class='img-responsive' src='data:"+this['MIME']+";base64,"+this['img']+"' width=150px height=150px></li>");
});
});
}
and here is my page:
<ul id="photos" class="row bar">
</ul>
While the page doesn't have picture, it's running as well. But when I upload some pictures more than 2, I think my page is stuck although it occurs in several times.
In photosdone() you call photosdone() again, it is ok ?
Recursive photosdone calls makes your page laggy
Instead of returning all images within the JSON response, every 800ms, you should just return their url instead. This will not only decrease network traffic a lot, it will also allow the browser to cache the images. Also, base64 encoded strings are much larger than binary image data (about 33%).
$("#photos").append("<li class='col-lg-3 col-md-3 '><img class='img-responsive' src='"+this['url']+"' width=150px height=150px></li>");

Synchronise(!) 60000 ms Loop on every device. NO INACCURACY

this goes out to the most most professionals among you.
I am planing a Never-Ending-Loop of an (Javascript)-one-minute-animation.
The complete Animation ends after 59.999 Milliseconds.
-> Tween 1 starts at 0 ms
-> Tween 2 starts at 1000 ms
-> Tween 3 starts at 2000 ms
-> Tween 4 starts at 3000 ms
-> Tween 5 starts at 4000 ms
-> Tween 6 starts at 5000 ms
...loop
so far, so good.
I want, that the Loop is Synchronised on every device, on every operating system, any Browser, from anywhere arround the world exactly in Milliseconds. NO INACCURACY.
My try was: get the Seconds (to know the tween-no.) and Milliseconds (to know, when the next tween starts) of Server-Time via Ajax. And then, let the animation start at Tween(x) after x Milliseconds. But it is not synchronised. There are up to 2 Seconds difference.
How do i have to fix it? Please dont say, it is not possible, it has to be possible anywhere!
If its not possible with javascript, tell me something else.
Thanks a lot.
The most brutal would be to implement NTP but if half a second difference is enough you can implement a simplified version.
The main method is to start a communication between a time server and the client to get a) the time and b) the difference between the client time and server time. With a small serverprogram that is nothing more than a stopwatch you can do the following:
server -> client: server time (or start with client->server)
client: sets own time to server time
client -> server: client time
(you may repeat it a couple of times to get an average)
The serve can calculate the lag time now and send it to the client and the client corrects its time accordingly. You canot set the computer time of the client in Javascript, you can only set the time for the actual session, so you need it to repeat for every reload and in between, too, once in a while (not too often, I would say that once every hour is more than sufficient).
The overhead should be as small as possible, it would be a good idea to use a smaller protocol than HTTP if possible
A small and hastily knocked up example with PHP on the server side:
<?php
$client_ts = $_GET["date"];
// UTC timestamp in milliseconds
$server_ts = round(microtime(true) * 1000);
// send back that timestamp and the lag
echo $server_ts."|".($client_ts - $server_ts);
?>
The HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>LAG test</title>
<style type="text/css">
table {
margin-left: 1em;
}
td,th {
border: 2px solid black;
padding: .5ex .5em .5ex .5em;
text-align: center;
}
input {
margin: 3ex 1em 0 4em;
}
p {
max-width: 40em;
}
</style>
<script type="text/javascript">
// everything is highly simplified and crude here! No checks, no balances!
// Simple AJAX. Websockets would be better, of course, but also way more complex
var ajax_request;
function get_data(){
var ret, lag, diff,client_time, server_time;
if (ajax_request.readyState === 4){
if (ajax_request.status === 200){
ret = ajax_request.responseText;
}
else{
ret = null;
}
}
else {
ret = null;
}
if(ret){
// only one check here, more are better especially
// if 'diff' is very large
client_time = Date.now();
server_time = ret.split("|");
lag = server_time[1];
diff = Math.abs(client_time - server_time[0]);
document.getElementById("ctos").textContent = lag;
document.getElementById("stoc").textContent = diff;
document.getElementById("diff").textContent = (lag - diff);
}
else {
document.getElementById("ctos").textContent = "Nope";
document.getElementById("stoc").textContent = "Nada";
document.getElementById("diff").textContent = "Rien";
}
}
function sendTo(text,uid,url){
ajax_request = new XMLHttpRequest();
ajax_request.open("GET", url + "?date=" + text + "u=" + uid, true);
ajax_request.send(null);
ajax_request.onreadystatechange = get_data;
}
function testLAG(){
// cheap pseudo-UID to avoid caching
var unifier = Math.floor(Math.random() * 0xffffffff).toString();
// date.now() returns UTC time
var client_time = Date.now();
sendTo(client_time,unifier ,"phpdate.php");
}
</script>
</head>
<body>
<h1> To test lag, press LAG-Button</h1>
<p>The difference in "Diff." is the probablity that it was the same route both ways. You need to allow for some milliseconds for the HTTP overhead but in general: the lower the better.</p>
<table id="out" caption="latencies">
<tr><th>Client to Server</th><th>Server to Client</th><th>Diff.</th></tr>
<tr><td id="ctos">n/a</td><td id="stoc">n/a</td><td id="diff">n/a</td>
</table>
<input type="button" value="LAG-Button" onclick="testLAG()" /> <- press here and press often. But only if you don't mind.
</body>
</html>

Categories

Resources