ShoeLace1291
ShoeLace1291

Reputation: 4698

Why is my HTML5 progress bar not changing with jQuery AJAX?

I am trying to make a progress bar for my jQuery/AJAX upload script. Everything in the script works fine, except the progress bar's value never changes. Also, in console, the percentComplete variable is always just 1 and it logs twice. I've tried every different thing I can find, but nothing seems to work. The progress bar itself is added to my div, but that's about it.

$.ajax({
    beforeSend : function(){
        $(settings.message_div).html('<progress id="#upload-progress" value="0" max="100"></progress>');
    },
    xhr: function()
    {
        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(percentComplete);
                $('#upload-progress').val(percentComplete);
            }
        }, false);
        //Download progress
        xhr.addEventListener("progress", function(evt){
            if (evt.lengthComputable) {
                var percentComplete = evt.loaded / evt.total;
                //Do something with download progress
                console.log(percentComplete);
                $('#upload-progress').val(percentComplete);
            }
        }, false);
    return xhr;
     },
    type: "POST",
    dataType: "xml",
    data: formData,
    url: settings.ajax_url,
    processData: false,
    contentType: false,
    cache: false,
    success: function(xml){
        //console.log('ajax called');
    },
    error: function(xhr){
        $(settings.message_div).html(xhr.responseText);
        //console.log(xhr.responseText);
    }
});

This was taken from here

Upvotes: 1

Views: 2924

Answers (2)

cforcloud
cforcloud

Reputation: 589

onprogress should work recently. The tutorial you have seen is a little bit outdated. http://www.html5rocks.com/en/tutorials/file/xhr2/
http://www.w3.org/TR/progress-events/#introduction

xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
  var progressBar = $('#upload-progress').get(0);
  progressBar.value = (e.loaded / e.total) * 100;
  progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
}

Upvotes: 1

Vivek Pradhan
Vivek Pradhan

Reputation: 4847

The tutorial you've followed is really old and was probably relevant around Jquery 1.5. But as of that version, the old XHR object has been hidden and is replaced by the JQXhr object with a higher level of abstraction:

The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of the browser's native XMLHttpRequest object. For example, it contains responseText and responseXML properties, as well as a getResponseHeader() method. When the transport mechanism is something other than XMLHttpRequest (for example, a script tag for a JSONP request) the jqXHR object simulates native XHR functionality where possible.

The attributes for this object are not very well documented and I am not sure if it still supports upload and progress functionality like you try to exploit in your code.

Even in the comments of that blog, people suggested using the onprogress attribute in the xhrFields option but again this SO Post confirms that it's not possible in the newer versions of Jquery as it has been deprecated. In that post only, the answer suggests :

The xhr option parameter must be a function that returns a native XmlHttpRequest object for jQuery to use.

Code snippet from the answer:

 $.ajax({
async: true,
contentType: file.type,
data: file,
dataType: 'xml',
processData: false,
success: function(xml){
    // Do stuff with the returned xml
},
type: 'post',
url: '/fileuploader/' + file.name,
xhr: function(){
    // get the native XmlHttpRequest object
    var xhr = $.ajaxSettings.xhr() ;
    // set the onprogress event handler
    xhr.upload.onprogress = function(evt){ console.log('progress', evt.loaded/evt.total*100) } ;
    // set the onload event handler
    xhr.upload.onload = function(){ console.log('DONE!') } ;
    // return the customized object
    return xhr ;
} ;
});

Another relevant Answer on similar lines where he uses a deferred object's progress attribute and defines the upload progress event handlers inside it.

TLDR- It is not established if you can use the native XHR object and exploit it's upload and progress events like you mention in the latest versions of Jquery.

You should probably look at the code for some HTML5 and jquery file upload plugins. I was just going through one on github and I'm just copy-pasting some code from fileupload.js in the project:

        _onProgress: function (e, data) {
        if (e.lengthComputable) {
            var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
                loaded;
            if (data._time && data.progressInterval &&
                    (now - data._time < data.progressInterval) &&
                    e.loaded !== e.total) {
                return;
            }
            data._time = now;
            loaded = Math.floor(
                e.loaded / e.total * (data.chunkSize || data._progress.total)
            ) + (data.uploadedBytes || 0);
            // Add the difference from the previously loaded state
            // to the global loaded counter:
            this._progress.loaded += (loaded - data._progress.loaded);
            this._progress.bitrate = this._bitrateTimer.getBitrate(
                now,
                this._progress.loaded,
                data.bitrateInterval
            );
            data._progress.loaded = data.loaded = loaded;
            data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
                now,
                loaded,
                data.bitrateInterval
            );
            // Trigger a custom progress event with a total data property set
            // to the file size(s) of the current upload and a loaded data
            // property calculated accordingly:
            this._trigger(
                'progress',
                $.Event('progress', {delegatedEvent: e}),
                data
            );
            // Trigger a global progress event for all current file uploads,
            // including ajax calls queued for sequential file uploads:
            this._trigger(
                'progressall',
                $.Event('progressall', {delegatedEvent: e}),
                this._progress
            );
        }
    },

    _initProgressListener: function (options) {
        var that = this,
            xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
        // Accesss to the native XHR object is required to add event listeners
        // for the upload progress event:
        if (xhr.upload) {
            $(xhr.upload).bind('progress', function (e) {
                var oe = e.originalEvent;
                // Make sure the progress event properties get copied over:
                e.lengthComputable = oe.lengthComputable;
                e.loaded = oe.loaded;
                e.total = oe.total;
                that._onProgress(e, options);
            });
            options.xhr = function () {
                return xhr;
            };
        }
    }

This definitely works as the project is very well maintained and has working demos. I think it should be a good starting point for you. Hope it helps.

Upvotes: 0

Related Questions