Reputation: 740
I wrote the following function to process an AJAX request to fetch data:
var xhr = createCORSRequest('GET', url);
if (!xhr) {
alert('CORS not supported');
return;
}
xhr.onload = function() {
var txt1 = xhr.responsetxt1;
var heading = getheading(txt1);
if (heading == 'PASS') {
var file = "My_URL" + ".js";
downloadFile(file);
//My code to display a progress bar here?
} else {
//Logic to handle failure to load
}
};
Here is my downloadFile
function to download the file. But, I don't understand how to:
If you can add a description of how it works, that'd be great. Thanks.
function downloadFile(fileName) {
(function(d) {
var ref = d.getElementsByTagName('script')[0];
var js = d.createElement('script');
js.src = fileName;
ref.parentNode.insertBefore(js, ref);
// My code to display a progress bar here?
}(document));
}
Upvotes: 5
Views: 7824
Reputation: 1191
AFAIK, script elements don't have progress events. Your best bet is to use an XHR to get the script's body, then count on the browser cache for a second fetch. The problem is that your script then needs to be parsed by the browser, and there doesn't seem to be events for that.
My solution is pure JS, so you can adapt it to whatever framework you're using. It assumes that actual download will be about 70% of the total time, and allocates 20 % to the browser parsing. I use a non-minified versionof the awesome three.js 3D library as a biggish source file.
because it is in another sandbox, progress callculation is inaccurate, but if you serve your own script that shouldn't be a problem.
keep in mind that this is a fairly stripped down implementation. I used a simple HR as a progress bar, for example.
//this is a rough size estimate for my example file
let TOTAL_ESTIMATE = 1016 * 1024;
// I use a hr as a
let bar = document.getElementById("progressbar");
let button = document.getElementById("dlbtn");
var js; // to hold the created dom element
var fileName; // to hold my cacheBusted script adress
/* this function will be called several times during (the first) download, with info about how much data is loaded */
function onProgress(e) {
var percentComplete = e.loaded / TOTAL_ESTIMATE;
if (e.lengthComputable) {
percentComplete = e.loaded / e.total;
}
p = Math.round(percentComplete * 100);
console.log("progress", p + "%,", e.loaded, "bytes loaded")
bar.style = "width: " + (5 + .6 * p) + "%"; // I just assume dl will be around 60-70% of total time
}
/* this function is called when info comes. at the end of the initial download, the readystate will be 4 so we then set the file's src attribute, triggering a re-download but taking advantage of the browser's cache. It's not ideal, and simply `eval` ing the data would probably yield better results. I just assumed you wanted a <script> tag on your page, and for it to be evaluated. */
function onReadyState(e) {
let r = e.target;
//this is lifted almost verbatim from http://vanilla-js.com/ ;)
if (r.readyState != 4 || r.status != 200)
return;
let l = r.responseText.length;
console.log("Success !", l, "bytes total (" + Math.round(l / 1024) + " KB )");
bar.style = "width: 70%";
//just add / to next line to toggle ending methods
/* you could speed up the proces by simply eval()ing the returned js. like so (please be aware of security concerns) :
eval.bind(window)(r.responseText);
onScriptLoaded();
/*/
js.src = fileName;
bar.style = "width: 80%";
var ref = document.getElementsByTagName('script')[0];
ref.parentNode.insertBefore(js, ref);
//*/
};
//this is called when the script has been evaluated :
function onScriptLoaded() {
bar.style = "width: 100%; background-color: lightgreen;";
button.disabled = false;
console.log("script has been evaluated ?", THREE ? "yes" : "no"); // the demo file exposes window.THREE
}
function downloadFile(file) {
button.disabled = true;
(function(d) {
// this helps to test this script multiple times. don't keep it
fileName = file + "?bustCache=" + new Date().getTime();
console.log("inserting new script");
js = d.createElement('script');
js.type = "text/javascript";
js.defer = "defer";
js.async = "async";
var r = new XMLHttpRequest();
bar.style = "width: 5%"; //always react ASAP
r.addEventListener("progress", onProgress);
r.open("GET", fileName, true);
r.onreadystatechange = onReadyState;
js.onload = onScriptLoaded;
r.send();
// My code to display a progress bar here?
}(document));
}
#progressbar {
height: 6px;
border-radius: 3px;
width: 0%;
border-color: green;
background-color: green;
}
<button onclick="downloadFile('https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.js', this)" id="dlbtn">Download</button>
<script id="dummy" type="text/javascript">
console.log("dummy script ready")
</script>
<hr id="progressbar" align="left" />
Upvotes: 3
Reputation: 3833
DISCLAIMER: I did not test this on Android
. I tested ONLY in Chrome
(desktop). You should see progress events writing to the browser console
var url = 'url/to/file/';
var request = new XMLHttpRequest();
request.responseType = "blob"; // Not sure if this is needed
request.open("POST", url);
var self = this;
request.onreadystatechange = function () {
if (request.readyState === 4) {
var file = $(self).data('file');
var anchor = document.createElement('a');
anchor.download = file;
anchor.href = window.URL.createObjectURL(request.response);
anchor.click();
}
};
request.addEventListener("progress", function (e) {
if(e.lengthComputable) {
var completedPercentage = e.loaded / e.total;
console.log("Completed: ", completedPercentage , "%");
}
}, false);
request.send();
Hope this helps.
Upvotes: 1
Reputation: 2892
Well, it depends much more on the programming language instead of client side. For example, PHP has http://php.net/manual/en/session.upload-progress.php
As for client side, Boris answer is a good example! Hope it helps.
Upvotes: 0