18

I'd like to cancel a .load() operation, when the load() does not return in 5 seconds. If it's so I show an error message like 'sorry, no picture loaded'.

What I have is...

...the timeout handling:

jQuery.fn.idle = function(time, postFunction){  
    var i = $(this);  
    i.queue(function(){  
        setTimeout(function(){  
            i.dequeue();
            postFunction();  
        }, time);  
    });
    return $(this); 
};

... initializing of the error message timeout:

var hasImage = false;

$('#errorMessage')
    .idle(5000, function() {

        if(!hasImage) {
            // 1. cancel .load()            
            // 2. show error message
        }
    });

... the image loading:

$('#myImage')
     .attr('src', '/url/anypath/image.png')
     .load(function(){
         hasImage = true;
         // do something...
      });

The only thing I could not figure out is how to cancel the running load() (if it's possible).

Edit:

Another way: How do I prevent the .load() method to call it's callback function when it's returning?

Dirk
  • 1,871
  • 1
  • 19
  • 25

7 Answers7

11

If you want any custom handling such as this, you simply can't use the jQuery.load() function. You'll have to upgrade to jQuery.ajax(), which I recommend anyway since you can do so much more with it, especially if you need any kind of error handling, it will be necessary.

Use the beforeSend option for jQuery.ajax and capture the xhr. Then you can create callback which can cancel the xhr after your timeout, and the callbacks as necessary.

This code is not tested, but should get you started.

var enableCallbacks = true;
var timeout = null;
jQuery.ajax({
  ....
  beforeSend: function(xhr) {
    timeout = setTimeout(function() {
      xhr.abort();
      enableCallbacks = false;
      // Handle the timeout
      ...
    }, 5000);
  },
  error: function(xhr, textStatus, errorThrown) {
    clearTimeout(timeout);
    if (!enableCallbacks) return;
    // Handle other (non-timeout) errors
  },
  success: function(data, textStatus) {
    clearTimeout(timeout);
    if (!enableCallbacks) return;
    // Handle the result
    ...
  }
});
Community
  • 1
  • 1
aceofspades
  • 7,444
  • 1
  • 33
  • 47
  • fullware, thanks for your helpful answer! There is a small thing to correct: I had to replace the line "beforeSend(xhr) {" with "beforeSend: function(xhr) {" – Dirk May 15 '10 at 18:04
3

Your rephrased secondary question:
How do I cancel pending callback functions, created using the .load() method?

You can cancel all jquery callbacks, by using this 'nuclear' option:

$('#myImage').unbind('load');
Carver
  • 67
  • 9
2

I think the simplest thing to do would be to use $.ajax directly. That way you can start a timeout, and from the timeout set a flag that the handler on the ajax call can check. The timeout can also show a message or whatever.

Pointy
  • 371,531
  • 55
  • 528
  • 584
  • Hi Pointy, thanks for your answer. I had the same thought, but I don't want to break up these nice encapsulated handling of jQuery... – Dirk May 11 '10 at 18:13
  • Well, the `.load()` API is synchronous, and that's that. – Pointy May 11 '10 at 18:32
  • hmm, may I have to change my question to: how do I prevent the .load() method to call it's callback function when it's returning... – Dirk May 11 '10 at 18:48
  • I deleted the comment about `.load()` being synchronous - I don't think it actually is. Sorry about that. Nevertheless, I still don't know of any way to tell jQuery to abort the callback; the xhr object remains buried. – Pointy May 11 '10 at 19:50
  • Can ajax be used to load an image directly? Other articles suggest that only Base64 image data and REST image service will work with ajax. The jQuery ajax page makes no mention of images and the datatypes shown are: xml, html, json, jsonp, script, or text. So can ajax be used without creating a server side handler to assist in the process. – Claude Feb 03 '13 at 02:34
  • @Claude why would you use XHR to load an image? You can just create an Image object and set its "src" property to the URL. – Pointy Feb 03 '13 at 15:21
  • @Pointy The images are large. From what I have been reading setting the src to '' does not stop download. Load has no abort method. I was looking into using ajax so I could abort. Turns out that use of ajax for direct download of images does not have consistent implementation in browsers. So in my case, I did revert to the more standard $('').attr('src',url) trick. And when I want to abort I use windows.stop [link](http://stackoverflow.com/questions/930237/javascript-cancel-stop-image-requests) – Claude Feb 03 '13 at 20:11
2

If you load this code after JQuery has been loaded you will be able to call .load() with a timeout parameter.

jQuery.fn.load = function( url, params, callback, timeout ) {
    if ( typeof url !== "string" ) {
        return _load.call( this, url );

    // Don't do a request if no elements are being requested
    } else if ( !this.length ) {
        return this;
    }

    var off = url.indexOf(" ");
    if ( off >= 0 ) {
        var selector = url.slice(off, url.length);
        url = url.slice(0, off);
    }

    // Default to a GET request
    var type = "GET";

    // If the second parameter was provided
    if ( params ) {
        // If it's a function
        if ( jQuery.isFunction( params ) ) {
            if( callback && typeof callback === "number"){
                timeout = callback;
                callback = params;
                params = null;
            }else{
                // We assume that it's the callback
                callback = params;
                params = null;
                timeout = 0;
            }
        // Otherwise, build a param string
        } else if( typeof params === "number"){
            timeout = params;
            callback = null;
            params = null;
        }else if ( typeof params === "object" ) {
            params = jQuery.param( params, jQuery.ajaxSettings.traditional );
            type = "POST";
            if( callback && typeof callback === "number"){
                timeout = callback;
                callback = null;
            }else if(! timeout){
                timeout = 0;
            }
        }
    }

    var self = this;

    // Request the remote document
    jQuery.ajax({
        url: url,
        type: type,
        dataType: "html",
        data: params,
        timeout: timeout,
        complete: function( res, status ) {
            // If successful, inject the HTML into all the matched elements
            if ( status === "success" || status === "notmodified" ) {
                // See if a selector was specified
                self.html( selector ?
                    // Create a dummy div to hold the results
                    jQuery("<div />")
                        // inject the contents of the document in, removing the scripts
                        // to avoid any 'Permission Denied' errors in IE
                        .append(res.responseText.replace(rscript, ""))

                        // Locate the specified elements
                        .find(selector) :

                    // If not, just inject the full result
                    res.responseText );
            }

            if ( callback ) {
                self.each( callback, [res.responseText, status, res] );
            }
        }
    });

    return this;
};

Not sure if you are interested in overwriting any "standard" JQuery functions, but this would allow you to use .load() the way you described.

Falle1234
  • 4,876
  • 1
  • 20
  • 29
1

I don't think you can get there from here - as @Pointy mentioned, you need to need to get to the XmlHttpRequest object so you can access the .abort() method on it. For that, you need the .ajax() jQuery API which returns the object to you.

Barring the ability to abort the request, another method to consider is to add knowledge of the timeout into the callback function. You could do this two ways - first:

var hasImage = false;

$('#errorMessage')
    .idle(5000, function() {

        if(!hasImage) {
            // 1. cancel .load()            
            // 2. show error message
            // 3. Add an aborted flag onto the element(s)
            $('#myImage').data("aborted", true);
        }
    });

And your callback:

$('#myImage')
     .attr('src', '/url/anypath/image.png')
     .load(function(){
         if($(this).data("aborted")){
             $(this).removeData("aborted");
             return;
         }
         hasImage = true;
         // do something...
      });

Or you could bypass the idle for this particular functionality:

$('#myImage')
     .attr('src', '/url/anypath/image.png')
     .data("start", (new Date()).getTime())
     .load(function(){
         var start = $(this).data("start");
         $(this).removeData("start");
         if(((new Date()).getTime() - start) > 5000)
             return;

         hasImage = true;
         // do something...
      });

Neither approach is ideal, but I don't think you can directly cancel load as of jQuery 1.4 - might be a nice feature request to the jQuery team, though.

Emil Lerch
  • 4,226
  • 5
  • 31
  • 42
  • 1
    +1 But the `.attr('src', '/url/anypath/image.png')` immediately sends out the HTTP request to get the image, before the load event is registered. You should move the `attr()` to the end, after the `load()`. – Jose Rui Santos Oct 16 '12 at 18:42
0

You could simply set a timeout before loading the image as well as testing for a load error.

function LoadImage(yourImage) {

    var imageTimer = setTimeout(function () {
        //image could not be loaded:
        alert('image load timed out');
    }, 10000); //10 seconds

    $(yourImage).load(function (response, status, xhr) {
        if (imageTimer) {

            if (status == 'error') {
                //image could not be loaded:
                alert('failed to load image');

            } else {
                //image was loaded:
                clearTimeout(imageTimer);
                //display your image...
            }

        }
    });

}
George Filippakos
  • 14,863
  • 12
  • 75
  • 86
0

$('#myImage').load(function(){...}) is not a function call to load the image, it is actually shorthand to bind a callback to the onload event.

Therefore, adding a timeout parameter to the .load() method as suggested in other answers will have no effect.

It think your two options are:

  1. Continue on the path you are following and do something like $('#myImage').attr('src', ''); to cancel the image load after it times out, or

  2. Find some way to use $.ajax( { ... , timeout: 5000, ...} ); to load the image instead of letting the browser do it automatically via the <img src="..."> attribute.

Tom
  • 36,698
  • 31
  • 90
  • 98