XHR2 Progress Fallback - javascript

I have the following example which uses the progress events in XHR2 to display a progress bar to the user when performing an AJAX request:
$.ajax({
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener('progress', function(evt){
if (evt.lengthComputable) {
var percentComplete = (evt.loaded / evt.total) * 100;
if(percentComplete >= 100){
$('#loading-bar').find('.bar').css({'width': percentComplete + '%'});
$('#loading-bar')
.find('.bar')
.on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', function() {
$('#loading-bar').fadeOut(function(){
$('#loading-bar').find('.bar').css({'width': ''});
});
});
} else {
$('#loading-bar').find('.bar').css({'width': percentComplete + '%'});
}
}
}, false);
return xhr;
},
type: 'GET',
url: 'Test.html',
success: function (response) {
},
error: function (jqXHR, textStatus, errorThrown) {
}
});
However for browsers that don't support XHR2, the bar won't animate nor will the loading-bar element be faded out from the DOM.
How can I implement a fallback? As I don't want to just fade the loading bar out on success as that will then conflict with the progress events for browsers that DO support it.
Or even better is it possible to get the progress by alternate means?

If it's download progress, we can do it when xhr.readyState >= 3. Just read the xhr.responseText.length, and then divide it by parseInt(xhr.getResponseHeader('Content-Length')) (assuming the server provides this header and it's same origin or with correct CORS header).
If it's upload progress, I don't know any method to polyfill it purely within the client-side. We can create an id for every upload session, and use another xhr to pull the upload receiving progress from the server. However that requires complicated implementation on the server-side. There are many such implementations in common languages, for example for PHP or .NET. You can look into them and implement your own.

Try using .onreadystatechange() with .readyState:
getter.onreadystatechange = function() {
if (loading) { //If the loading element has been loaded...
if (this.status === 200) { //If this.status is already 200...
loading.style.width = this.readyState*25+"%"; //This sets the width of loading according to this.readyState
if (this.readyState === 4) {
//We hide loadingBar and do stuff when we're done.
loadingBar.style.display = "none";
[do stuff]
}
}
}
};
If you're accepting 100 Continue statuses, you might also want to account for that into your loading bar. The way this is now, it's at a standstill for a while, but then zooms to 100% when it gets to the status 200 OK and the readyState property goes from 2 to 4.
Here's the fiddle: http://jsfiddle.net/NobleMushtak/QRuU6/

Related

Progress bar for multiple ajax requests with stages of completion. Is it possible?

I have a simple form which when submitted validates via an ajax request. If the form checks out ok, then another ajax request is made to process the data originally submitted.
I want to build a progress bar for this. Ive found that adding this code to each ajax request returns the progress for each call separately. That makes the progress bar load to 100%, twice, quickly.
Is it possible for example for two ajax request to each fill 50% of the progress bar?... So ajax request 1 will fill up to 50% and the second will fill from 51% to 100%? Or is that crazy?
Or if three ajax calls each being responsible for 33.33% of the total percentage?
I guess we are more looking at stages of completion as well as progress.
Any ideas how this could be achieved without too much faking it?
var xhr = new window.XMLHttpRequest();
//Upload progress
xhr.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
//Do something with upload progress
console.log('percent uploaded: ' + (percentComplete * 100));
}
}, false);
//Download progress
xhr.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
//Do something with download progress
console.log('percent downloaded: ' + (percentComplete * 100));
}
}, false);
return xhr;
Well the way i had created such a progress bar was, since you want each of your function to be called one after another, that is completion of one should trigger the other, XMLHttpRequest has onreadystate change event. you can use that event to confirm that the first request got executed successfully or not, and then trigger the second one, at each change you will have progress bar incremented by whatever % you want to.
function postdata()
{
var xhr = new XMLHttpRequest();
xhr.open
(
"POST",
Url,
true
);
xhr.send();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
//Call next function here and increment progressbar
}
}
}
hope this helps
Yes, it is possible. You can create an array containing each ajax call. Set <progress> element max attribute to 100/array.length. Divide evt.loaded / evt.total of individual progress event by array .length to set value of <progress> element. You could also use Promise.all(), .then() to process array of functions returning a Promise from ajax call and update <progress> element.
html
<label></label>
<progress value="0" min="0" max="100"></progress>
javascript
var progress = document.querySelector("progress");
var url = "/echo/html/";
function request(url) {
var len = arr.length;
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentComplete = ((evt.loaded / evt.total) * (progress.max / len))
/ len;
progress.value += percentComplete;
console.log(progress.value, percentComplete);
if (evt.total === evt.loaded) {
requests += 1;
}
if (progress.value == progress.max && requests === len) {
progress.previousElementSibling.innerHTML = "upload complete";
// you could call `resolve()` here if only interested in
// `upload` portion of request
alert("upload complete");
}
}
}, false);
xhr.onload = function() {
resolve(this.responseText)
};
xhr.onerror = reject;
xhr.open("POST", url, true)
xhr.send("html=" + Array(100000).fill(1).join(""));
})
}
var arr = [], requests = 0;
arr.push(request, request, request);
Promise.all(arr.map(function(req) {
return req(url)
}))
.then(function(data) {
console.log(data);
})
.catch(function(err) {
console.log(err)
})
jsfiddle https://jsfiddle.net/v2msL7hj/3/

XMLHttpRequest: need to use success and error functions from ajax

I have this ajax post function run when an upload button is clicked, the files get uploaded to the server, the server sees for any errors and if there are erros, it notifies the user with the req.end(); function. The problem is, since then, I've moved to XMLHttpRequest() (to use the onprogress functions that it provides) but I still need to use those success and error functions from ajax. Is there a way to use them somehow with XMLHttpRequest? Thank you!
This is the code I have so far:
var xhrVideo = new XMLHttpRequest();
xhrVideo.open('POST', '/uploadVideo', true);
xhrVideo.upload.onprogress = function(e) {
if (e.lengthComputable) {
$('.videoProgress').html(((e.loaded / e.total) * 100).toFixed(0)+'%');
}
};
var videoForm = new FormData($('.videoUploadForm')[0]);
xhrVideo.send(videoForm);
And the ajax code:
var videoData = new FormData($('.videoUploadForm')[0]);
$.ajax({
url: '/uploadVideo',
type: 'POST',
data: videoData,
cache: false,
contentType: false,
processData: false,
mimeType:"multipart/form-data",
success: function(data){
switch(data){
case '1':
alert('Wrong Video File Extension');
break;
case '2':
alert('Wrong Image File Type');
break;
}
},
error: function(data){
alert('Something went wrong! Please reload this page')
}
});
Use listeners for load and error events.
Example adding a listener for success event.
xhrVideo.addEventListener('load', function(e) {
NOTE: the listeners must be added before the send() function
Also, I advise you to read the entire article about using XMLHttpRequest().
Personally , I like to use jquery ajax instead of pure javascript .. so you can use xhr with ajax and catch the progress and load event as well
xhr (default: ActiveXObject when available (IE), the XMLHttpRequest
otherwise) Type: Function() Callback for creating the XMLHttpRequest
object. Defaults to the ActiveXObject when available (IE), the
XMLHttpRequest otherwise. Override to provide your own implementation
for XMLHttpRequest or enhancements to the factory.
in your code you can use it like this
$.ajax({
xhr: function(){
var xhr = new window.XMLHttpRequest();
//Upload progress
xhr.upload.addEventListener("progress", function(evt){
if (evt.lengthComputable) {
var percentComplete = (evt.loaded / evt.total) * 100;
//Do something with upload progress
$('.videoProgress').html(Math.round(percentComplete)+"% uploaded");
}
}
}, false);
xhr.upload.addEventListener("load", function(evt){
evt.preventDefault();
},false);
return xhr;
},
// reset of your ajax code

JQuery $.get() failing - readyState 0

I'm trying to track the progress of an archive compression through requesting a txt file made, and updated by PHP via JQuery $.get().
The process works except for 1 or 2 requests that fail at random intervals and return the following:
readyState: 0
responseText:
status: 0
statusText: error
In Chrome and Opera, there is also this next to the failed requests: "net::ERR_CONTENT_LENGTH_MISMATCH"
Here is the code in question:
function check_compression(archive_name, progress_file, unique_compression_id) {
$.ajaxSetup ({
cache: false
});
var request = $.get($.trim(progress_file));
request.success(function(data) {
var percent = parseInt(data);
if(percent != 100) {
setTimeout(function() {
check_compression(archive_name, progress_file, unique_compression_id);
}, 100);
} else {
// Not finished
}
});
request.error(function(jqXHR, textStatus, errorThrown) {
// Just some error reporting
setTimeout(function() {
check_compression(archive_name, progress_file, unique_compression_id);
}, 500);
});
}
I have done a bit of research and ruled out possible situations:
Same Origin Policy issue: All requests are done to a file on the same server. The progress_file var will be in a format such as: tmp_file.txt
AdBlock is not enabled.

Track basic HTML form post progress

I have a basic HTML form that submits normally, no ajax at all. This form submits to the same file using regular post. I don't use AJAX because the form has 12 text fields and a minimum of 1 image but possibility of up to 26 images and ajax can't do both forms and images at once, I have to save to a DB and it's a lot of extra work over AJAX.
The problem is I need to track form upload progress somehow. Most programmers know to look in the lower left or right corner of their browsers to see form submission progress. But most people don't know this.
So I want to display a progress bar. The problem is all progress bars I have found use XHR requests by ajax. Since the form isn't ajax I can't seem to find a way to track the progress.
So is there a way to intercept the browsers internal submission progress to see the percentage of upload completed for the form?
EDIT
I've tried the following code at the start of the page but it doesn't work. I guess because either XHR is for AJAX or the browser has it's own that needs Hijacking, but I have no clue what that would be called or how to get to it if so:
xhr = new XMLHttpRequest();
xhr.upload.addEventListener( "progress", function ( evt )
{
if( evt.lengthComputable )
{
var progressPercent = ( evt.loaded / evt.total ) * 100;
console.log( value );
}
}, false );
This is some type of progressive enhancement i sometimes use. It will still make an ajax request but in the most transparent way as possible both for the user and the developer.
The key is to post the exact same data as posted by a regular form thanks to FormData, and to replace the whole document when we receive the full page reponse form the server. This is an untested simplified version :
function enhanceFormWithUploadProgress(form, progress) {
//form : the HTML form element to enhance.
//progress : an HTML element that will display upload progress.
//testing browser support. if no support for the required js APIs, the form will just be posted naturally with no progress showing.
var xhr = new XMLHttpRequest();
if (!(xhr && ('upload' in xhr) && ('onprogress' in xhr.upload)) || !window.FormData) {
return;
}
form.addEventListener('submit', function(e) {
//prevent regular form posting
e.preventDefault();
xhr.upload.addEventListener('loadstart', function(event) {
//initializing the progress indicator (here we're displaying an element that was hidden)
progress.style.display = 'block';
}, false);
xhr.upload.addEventListener('progress', function(event) {
//displaying the progress value as text percentage, may instead update some CSS to show a bar
var percent = (100 * event.loaded / event.total);
progress.innerHTML = 'Progress: ' + percent.toFixed(2) + '%';
}, false);
xhr.upload.addEventListener('load', function(event) {
//this will be displayed while the server is handling the response (all upload data has been transmitted by now)
progress.innerHTML = 'Completed, waiting for response...';
}, false);
xhr.addEventListener('readystatechange', function(event) {
if (event.target.readyState == 4 && event.target.responseText) {
//we got a response from the server and we're replacing the whole current document content with it, simulating a page reload
var newDocument = document.open('text/html', 'replace');
newDocument.write(event.target.responseText);
newDocument.close();
} else {
throw new Error('Error in the response.');
}
}, false);
//posting the form with the same method and action as specified by the HTML markup
xhr.open(this.getAttribute('method'), this.getAttribute('action'), true);
xhr.send(new FormData(this));
});
};
HTML:
<form method="post">
<button type="submit">Submit</button>
</form>
JavaScript:
/*
Show a progress element for any form submission via POST.
Prevent the form element from being submitted twice.
*/
(function (win, doc) {
'use strict';
if (!doc.querySelectorAll || !win.addEventListener) {
// doesn't cut the mustard.
return;
}
var forms = doc.querySelectorAll('form[method="post"]'),
formcount = forms.length,
i,
submitting = false,
checkForm = function (ev) {
if (submitting) {
ev.preventDefault();
} else {
submitting = true;
this.appendChild(doc.createElement('progress'));
}
};
for (i = 0; i < formcount; i = i + 1) {
forms[i].addEventListener('submit', checkForm, false);
}
}(this, this.document));
Demo: http://jsfiddle.net/jsbo6yya/
Credit: https://gist.github.com/adactio/9315750

Cross Browser solution to show progress bar while loading javascript files

I was looking for a way that I can show a (gmail-like) progress bar when the page is loading js file. reading this post I wrote a code that helped me to show the progress bar in mozilla FireFox as this other post states!
var oReq = new XMLHttpRequest();
oReq.addEventListener("progress", updateProgress, false);
oReq.addEventListener("load", transferComplete, false);
oReq.addEventListener("error", transferFailed, false);
oReq.addEventListener("abort", transferCanceled, false);
oReq.open();
// ...
// progress on transfers from the server to the client (downloads)
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total;
// ...
} else {
// Unable to compute progress information since the total size is unknown
}
}
function transferComplete(evt) {
alert("The transfer is complete.");
}
function transferFailed(evt) {
alert("An error occurred while transferring the file.");
}
function transferCanceled(evt) {
alert("The transfer has been canceled by the user.");
}
I would like to write a cross browsers solution
How to do it?

Categories