Best ajax questions in September 2011

Never-ending "Connecting" message after AJAX form submit

8 votes

I have a class which enables forms with a file-type input to be submitted via AJAX. It creates a hidden IFRAME element, changes the form target property so that it submits to the IFRAME, submits the form, then changes the target back to what it was. It also adds an onLoad event to the IFRAME so I can get a callback. The onLoad function also removes the IFRAME from the page before firing my callback function.

The class works perfectly, I get the callback as expected. In Firebug's Net panel, I see the request, I see the response, all is well. But, as soon as the submit starts, the browser tab for the page changes to "Connecting" with the loading spinner and never changes back. It makes the tab appear to be loading, this will go on for days if I leave the browser open.

The never-ending "Connecting" message that plagues me

The question, then, is: Is there any way for me to stop this manually, or is there some other way that I can prevent it from starting?

Here are the Response Headers as taken from the Net panel:

Date    Fri, 02 Sep 2011 15:23:15 GMT
Server  Apache/2.2.10 (Win32) PHP/5.3.1
X-Powered-By    PHP/5.3.1
Expires Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control   no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma  no-cache
Set-Cookie  PHPSESSID=agncdnha86mtci7dmuvriobak2; path=/
Content-Length  165
Keep-Alive  timeout=5, max=100
Connection  Keep-Alive
Content-Type    text/html

And a capture of the Net panel below. The POST is from the submission of the form, the GET is caused by the callback function, it is changing the source of an image on the page.

Screenshot of Firebug's Net panel

This is not specific to this page, it happens anywhere I use this technique to submit a file/image via an IFRAME. This affects my current version of Firefox (6.0), but also affected previous versions (5.x, 4.x, 3.x). The fact that the IFRAME is removed from the page after it loads makes this especially baffling - even if the request never finished, the removal of the element should effectively kill/stop and "connecting" that the browser thinks is happening.

UPDATE Per the answer from @Sidnicious, I added a timeout to the callback function to introduce a delay in removal of the IFRAME element. I experimented with the length of the delay, even a 1ms delay is adequate. This certainly qualifies as a work-around, but I'd still like to know if anyone can shed some light on to the why of it, preferably leading to an avoidance of using the timeout all together. I've included the modified code (with the timeout) below, in case it is helpful. This is the onLoad event for the IFRAME (io us a reference to the frame element):

        var obj={};
        var success = true;
        try{
            obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
            obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
        }
        catch(e){ success = false; }
        if( success ){
            this.fireEvent('onSuccess', obj.responseText );
        }else{
            this.fireEvent('onFailure', obj );
        }
        this.fireEvent('onComplete', obj );
        io.removeEvent('load', uploadCallback );
        setTimeout(function () { // <--- this timeout prevents the issue
            io.dispose();
        }, 1);

I’ve run into this too and would love to see the root cause fixed.

  • I talked to a couple of Firefox developers (mbrubeck and gavin), and they think that it’s a bug! The same issue was reported, and fixed, in 2005 for Firefox 1.9. Then, bug 489259 was opened in 2009. mbrubeck has graciously moved it out of the “unconfirmed” pile.

  • Safari behaves better than Firefox, but an error message (“One error in opening the page…”) shows up in the status bar if you remove the iframe during the load event. I found two similar WebKit bugs which have been open since 2007: 15485 and 13281.

This appears to happen when you remove an iframe from the document during the load event. JavaScript events fire synchronously — that is, in series with the browser’s own handling of the web page. That’s why it's possible to prevent a form from being submitted or prevent a keypress or mouse click from being registered from within an event handler.

The last time this bug was fixed in Firefox, the cause was that the removing an iframe from the page makes it forget which page owned it, but the iframe notifies the page that it was finished loading after the load event.

Anything you schedule with setTimeout happens after the current cycle of the event loop — after the browser finishes what it’s doing right now. So, using setTimeout to remove the iframe, even with a timeout of zero, lets it finish:

iframe.onload = function(){
    // Do work with the content of the iframe…

    setTimeout(function(){
        iframe.parentNode.removeChild(iframe);
    }, 0);
}

You can find this technique in use in the jQuery form plugin.


Update: the Firefox bug is now marked as “fixed”, it will make it into a near future version of Firefox.

What differentiates a REST web service from a RPC-like one?

8 votes

I have a web application that uses AJAX to grab JSON data from the server. It requires that the user first log in with their browser so that a cookie can be set. Only the GET and POST verbs are used, where GET is for retrieving data and POST is for any operation that modifies data.

From what I understand, REST differs from the above method in that the user authentication information is sent with every request and the PUT and DELETE verbs are used as well.

My question is, what benefits does a REST web service have over the RPC-like method, if the end point is only meant to be a user's browser? I can understand how REST is beneficial when the client is unknown, but when I'm only using jQuery ajax calls, are the benefits still worth it over an RPC-like method?

One of the big differences between REST and RPC is that REST is all about resources, and RPC is more about actions. For example, with a truly RESTful service you would never call something like http://domain.com/service/User/jason/add or http://domain.com/service/User/addUser?username=jason. With RESTful service you only ever reference the resource in the URL and then you define what to do with that resource using HTTP verbs and the body of the request. So a GET request to http:/domain.com/service/jason should return information about the resource (the jason user). You could get more specific and say http://domain.com/service/user/jason but the result should be the same. If you were adding a user named jason you would use the exact same URL http://domain.com/service/user/jason but you would use the PUT verb and the body of the request would contain additional data. To delete the jason resource you would, again, use the exact same URL (http://domain.com/service/user/jason) and use the DELETE verb. To update you would use the POST verb.

REST is great for public-facing APIs that you intend for other developers to use. They can be made to be very standard so that they don't require a ton of preexisting knowledge about the service to use. No WSDL calls, etc. Because of the statelessness it can also makes them more stable during partial network failures.

From what you are describing, I do not think you need a truly RESTful service. But you might want to consider, going forward, if you would ever need a more standard API. I made a REST service for a project that I only use for internal use, but that is because I intended to access that service from, potentially, dozens of other service, and in the future possibly from other developers. So even though at first I was only using it for a couple projects, the ultimate goal required a more standard interface.

Add to innerHTML without destroying form contents

5 votes

I have a form that is generated via ajax based of a multi-file uploader (swfupload). It adds more form elements to a given dom element upon the completion of each file uploaded. This thus gives me a problem. If i selected 5 files to upload, the first file will generate a form which I will start entering data in, however, when the 2nd file is completed uploading, it clears the previous entered data in the form elements and also appends the 2nd form elements.

I think this is because im using:

document.getElementById('test').innerHTML = document.getElementById('test').innerHTML + newformelements;

I think doing the above doesn't keep any entered data in the forms, just the HTML itself.

So, how can I append to this element without destroying what has been put into form fields already? The number of possible children is dynamic based of the multi-uploader.

Are you open to use jquery? If yes then You can easily do something like this

$("#divid").append("html you want to append");

my own long polling implementation compared to facebook and gmail

5 votes

For days I have been experimenting with long polling/comet implementation for my site. I got the basic idea of how it works, this is where i run some tests.

On the other hand, I have been observing(firebug) how gmail and facebook implement long polling. What I noticed with gmail is that the ajax request does not continuously follow right after the current request expires, but it waits for several seconds/minutes before it fires the next one.

I played with it some more. I tried to login with gmail account A in firefox and gmail account B in chrome. I waited when the current ajax poll finishes and then I sent an email from account B to A. I was expecting that account A won't receive it until the next poll, but to my surprise Account A directly received it right after I hit the submit button.

How does gmail do this with long polling ?

If you try to visit my site and click on the Run button and open firebug, you can see that ajax spinner is always running. when the server responds with data, it requests the server again.

I think your assumption that Gmail uses (only) long polling is incorrect. According to this question (and the answer) it uses forever frame, and forever XHR. See also BrowserChannel, which they use for Gmail Chat.

how to output file to browser after jquery ajax call

4 votes

I have a link on the website to a php file that generates native excel file on a fly ant outputs it directly to browser via headers for user to to open/save. Since it takes some time for the file to be generated I'd like to use jQuery Ajax to make the call and use some loading animation in the mean while.

The only thing I'm not sure how to do is how to output the file into the browser after Ajax call? Is it even possible?

(N.B. This is a paraphrasing of @dmitry's answer, but just elaborated upon)

The problem you have is that there is no means of directly returning a file to the user via AJAX - the browser has to request the file using a normal, synchronous HTTP request.

To solve this, your PHP will need to:

  1. Generate the Excel file as normal.
  2. Instead of writing the file back to the user, save it somewhere on the server's filesystem (i.e. using file_put_contents() or similar).
  3. Return the file path to the user.

Your JS, on receiving this response will then need to:

  1. Read the Excel file path back from the PHP script.
  2. Open the Excel file in a new tab/window using window.open() (or redirect in the current tab/window by setting location.href).

Is there a more efficient way of displaying unread message number in page title?

4 votes

Currently I use this...

setInterval(function() {
    $.ajax({
        url: 'data.php',
        success: function(data) { document.title = data;},
        dataType: 'text'
    });
}, 15000);

But it seems to slow down the server due to the amount of queries to the server.

Is there another way of displaying the amount of unread messages in the title bar, without the overload?

These solutions come to my mind:

  1. Using Server-Sent Events (Which of course, reduces scalability due to open network connections)
  2. Using Page Visibility
  3. Comet programming
  4. Explicitly offering this feature on user's demand (so that many users won't even request for it)

jQuery: get JSON via ajax, but with POST instead of GET

4 votes

I'm using jQuery's $.ajax to make a request to a third-party server, using JSONP. I specify the method as POST, but it uses GET anyway:

    $.ajax({
        type: "POST",
        dataType: "json",
        url: other_server + "/run?callback=?",
        data: {
            code: $(code).val()
        },
        success: function(obj) {
            var res = obj.results;
            $(results).val(res);
        }
    });

Looking in the jQuery source, I see these two lines that seem to force all cross-domain requests to GET, but I don't understand why it needs to be so:

if ( s.crossDomain ) {
    s.type = "GET";

Is it possible to do this with a POST instead of a GET? Why does jQuery force the use of GET?

JSON-P works by inserting a <script> element into the document, hence it can only make GET requests.

If you want to make a POST request to a remote server then you need to look at XHR instead and set up CORS permissions. Note that this has limited browser support.

Alternatively, keep your requests to the same origin (and have your server make the request to the remote server).

Getting the height of an image with jQuery from Flickr without loading the image file?

3 votes

I'm currently working on a jQuery solution where I want to load images from a Flickr RSS-feed. I'm not satisfied with the default size I get from the feed though - I want the images I load to be equal to or higher than the height of the wrapper element I'm displaying them in. (Width is not a problem here.)

According to their API documentation, Flickr has an image size system that looks like this:

s   small square 75x75
t   thumbnail, 100 on longest side
m   small, 240 on longest side
-   medium, 500 on longest side
z   medium 640, 640 on longest side
b   large, 1024 on longest side*
o   original image, either a jpg, gif or png, depending on source format

* Before May 25th 2010 large photos only exist for very large original images

This means that there is no guaranteed height - I need to check the height for each image I want to load. An example: if an image is a panorama and I load the large size, the height is probably still not enough to fill out my wrapper element. This is because the longest side will be the width and it will be 1024.

To complicate things, the height of the wrapper can vary - not dynamically after the page has loaded, but from page to page. On some pages it's 300 pixels, on others it's 100 pixels and so on.

As far as I understand the only way to get the height of an image file is to actually load it. So I have written some recursive code that loads an image, look's at its height and if it's not sufficient it calls itself again to load the next size. In the current version it starts with the smallest size and works its way up until it finds a suitable image or the biggest image possible.

Here's the code:

$.getJSON(ajaxContentURL, function(data) {
        var flickrImages = [];

        // An array of object with data pertaining Flickr's image sizes
        var flickrImageSizes = [{ size: "small square", pixels: 75, letter: "_s" }, 
                                { size: "thumbnail", pixels: 100, letter: "_t" }, 
                                { size: "small", pixels: 240, letter: "_m" }, 
                                { size: "medium", pixels: 500, letter: "" }, 
                                { size: "medium 640", pixels: 640, letter: "_z" }, 
                                { size: "large", pixels: 1024, letter: "_b"}];

        $.each(data.items, function(index, item) {
            flickrImages.push(loadFlickrImage(item, 0));
        });

        function loadFlickrImage(item, sizeIndex) {
            var path = item.media.m;
            var imgSrc = path.replace("_m", flickrImageSizes[sizeIndex].letter);
            var tempImg = $("<img />").attr("src", imgSrc);

            tempImg.load(function() {
               // Is it still smaller? Load next size
               if (this.height < el.data("scrollableAreaHeight")) {
                    // Load a bigger image, if possible
                    if ((sizeIndex + 1) < flickrImageSizes.length) {
                        loadFlickrImage(item, sizeIndex + 1);
                    } else {
                        return this;
                    }
               else {
                    return this;
               }
            });
        }
});

It's a litte rough around the edges, but I hope you get the picture.

This works, but it just seems so wasteful to have to load so many versions of an image just to get the right size?

Do you have any suggestions for improvements? I'm thinking that it should start by making a qualified guess at which could be the best size and load it first. Then it would see if it fits or if it's too small or too big and load the next or previous image on the size scale. That way you could reduce the amount of images to load just to get the right size.

Even better: is there a way to find out the image sizes without loading the actual image files? Remember that I don't have access to the complete Flickr API - I'm just accessing a JSON-feed, like this one.

I'd appreciate any feedback!

Thanks in advance!

I've solved this by first making a qualified guess as to which image size might be the best one. This guess is based on the height of the element inside which the loaded images will be put. Then I load this plausible image size to see if it's tall enough. If it's taller than or equal to the desired height, I use it. Otherwise I load the next bigger size, check its height and so on.

Even though this probably means that I will be loading a larger amount of images compared to just loading the biggest size first I think that it's cheaper when it comes to the total amount of kilobytes loaded. Some preliminary tests show that from a standard feed of 20 Flickr images, 1 or 2 images need to be reloaded after the first load.

Let me know if you put this solution to the test and end up with a different result or think than my approach is wrong. I'm looking for the cheapest and best solution.

Here's the current code if anyone is interested:

$.getJSON(flickrJsonUrl, function(data) {
    // small square - size is 75x75
    // thumbnail -> large - size is the longest side
    var flickrImageSizes = [{ size: "small square", pixels: 75, letter: "_s" },
                            { size: "thumbnail", pixels: 100, letter: "_t" },
                            { size: "small", pixels: 240, letter: "_m" },
                            { size: "medium", pixels: 500, letter: "" },
                            { size: "medium 640", pixels: 640, letter: "_z" },
                            { size: "large", pixels: 1024, letter: "_b"}];
    var loadedFlickrImages = [];
    var startingIndex;
    var numberOfFlickrItems = data.items.length;
    var loadedFlickrImagesCounter = 0;

    // Determine a plausible starting value for the image height
    if (el.data("scrollableAreaHeight") <= 75) {
        startingIndex = 0;
    } else if (el.data("scrollableAreaHeight") <= 100) {
        startingIndex = 1;
    } else if (el.data("scrollableAreaHeight") <= 240) {
        startingIndex = 2;
    } else if (el.data("scrollableAreaHeight") <= 500) {
        startingIndex = 3;
    } else if (el.data("scrollableAreaHeight") <= 640) {
        startingIndex = 4;
    } else {
        startingIndex = 5;
    }

    // Put all items from the feed in an array.
    $.each(data.items, function(index, item) {
        loadFlickrImage(item, startingIndex);
    });

    function loadFlickrImage(item, sizeIndex) {
        var path = item.media.m;
        var imgSrc = path.replace("_m", flickrImageSizes[sizeIndex].letter);
        var tempImg = $("<img />").attr("src", imgSrc);

        tempImg.load(function() {
            // Is it still smaller? Load next size
            if (this.height < el.data("scrollableAreaHeight")) {
                // Load a bigger image, if possible
                if ((sizeIndex + 1) < flickrImageSizes.length) {
                    loadFlickrImage(item, sizeIndex + 1);
                } else {
                    addImageToLoadedImages(this);
                }
            } else {
                addImageToLoadedImages(this);
            }

            // Finishing stuff to do when all images have been loaded
            if (loadedFlickrImagesCounter == numberOfFlickrItems) {
                // DO YOUR FINISHING STUFF HERE, LIKE ADDING THE
                // LOADED IMAGES TO YOUR PAGE...
            }

        });
    }


    function addImageToLoadedImages(imageObj) {

         // YOU CAN DO OTHER STUFF HERE, LIKE CALCULATING
         // AND ADDING ADDITIONAL ATTRIBUTES TO THE IMAGE
         // ELEMENT

         // Add the image to the array of loaded images
         loadedFlickrImages.push(imageObj);

         // Increment counter for loaded images
         loadedFlickrImagesCounter++;


    }

});

I can't guarantee that this code is "copy/paste ready to run" since I've extracted it from a bigger context, but I hope you get the general idea.