views:

74

answers:

4

Issue:

  1. Attach a click event on a link on a page (say link to http://google.com/)

  2. Try doing an AJAX or getJSON (JQuery specific call) from the function bound to that click event

  3. The AJAX or getJSON call is never completed (even if we add an explicit half-second pause)

Sample HTML page: http://paraschopra.com/temp/bah.html

Sample Server logger (to check counts): http://paraschopra.com/temp/count.txt

The bah.html file contacts a simple PHP logger via getJSON if you click on ABC link. The logger increments the number in count.txt. You will realize on Chrome 6 (and Safari), the server never gets contacted while on FF, IE, Chrome 5 the server gets contacted. Is it a bug or what? Is there a workaround right now?

By the way, works on all other browsers: FF, IE, etc.

+3  A: 

It's a bug -- but not in the new browsers. :-) What you're doing is setting up an asynchronous Ajax call, after which the page is being torn down because a link has been clicked that takes you to a new page. Your call never completes because the page is gone.

You can't reliably start asynchronous Ajax calls when the page is being torn down (well, you can reliably start them, but not have them reliably complete). Your only real options as far as I know are:

  1. Make the Ajax call synchronous (blech). This makes for a noticeable delay for the user.

  2. Cancel the link's default action and then change the window location yourself when the call completes (blech). Still a noticeable delay for the user AND it interferes with "open in new window", "open in new tab", etc.

  3. Approach the entire problem differently. The usual way is to have the link go to a page which then redirects to the actual target location. (That's what Google does, for instance, in Google Groups and various other places.) This is what I'd do for a general web-facing page (intranet apps are different), not least because it's totally reliable and works even if the user has JavaScript disabled in their browser.

You'll find a lot of questions and answers on this topic here on StackOverflow if you search for questions about doing things during the unload and beforeunload events.

I wouldn't be entirely surprised to find out there was a way to bring web workers to bear on this problem, but I expect many of the trade-offs would be the same, and web workers are only supported by a small number of browsers at present (including Chrome).

T.J. Crowder
You mean it originally was a bug which is being "fixed" in new browsers? I'd love some more pointers elucidating this. Clearly because lots of web analytics tools (including Google Analytics) rely on this kind of code to track external downloads or external links. Moreover, I am OK with synchronous calls but is there anyway I can do synchronous cross-domain call? Will an image (beakon) download work?
Paras Chopra
I can't do the redirect thing because it would be unacceptable to my customers. A synchronous call would be OK though. I'm not sure which synchronous call would work here because server logger will be on different domain. As far as I know image download, JS download and most so-call AJAX methods are really asynchronous.
Paras Chopra
@Paras: You can do a synchronous Ajax call if the server supports CORS and the browser the user is using supports it. FF and Chrome currently do, as does IE8 but only if you code for it explicitly. (More about CORS: http://www.w3.org/TR/cors/) I don't think IE7 and earlier do. Redirecting really is your most reliable way of doing this, not least because it works when JavaScript is turned off. If your client wants an unreliable mechanism, and one that fails if JavaScript is turned off, well...that's up to them. :-)
T.J. Crowder
@TJ: CORS looks scary :) Wish there were a simpler way.
Paras Chopra
@Paras: :-) CORS is transparent on the client except in IE (*sigh*) and easy enough on the server once you get to grips with it, for what you want to do. It only looks complicated at first. Basically, you respond to an OPTIONS verb (OPTIONS is an HTTP verb like GET or POST), look at the headers the browser sent you, and if you're okay with the request you set certain headers when you respond -- which tell the browser it's okay to go ahead and send the original GET or POST. But again, sadly, browser support is still a bit thin in the wild...
T.J. Crowder
A: 

you should better do something like:

<a href="counter.php?url=http%3A%2F%2Fwww.google.de%2F">klick</a>

then use the counter.php to log the url and the amount of klicks and forward to the given url by simply setting the header of the file with php f.e.

fast and failsave ;)

choise
My only issue is that it looks dirty (in the sense of not being acceptable by my customers). They will suspect that my script is doing session hijacking or stuff. I really wish this could be solved without requiring to do a re-direct. Anyone, please?
Paras Chopra
A: 

By the way, I just find this bug report: https://bugs.webkit.org/show_bug.cgi?id=26817 where it is reported:

I think this bug is WONTFIX.

Why? How is this even a bug? It should be a feature.

Also found https://bugs.webkit.org/show_bug.cgi?id=30457 which suggests loading an image outlives the page unload. Anyone having experience whether this really works? If that's the case, looks like we have a solution here.

Paras Chopra
A: 

what you need to do is change your code to this:

$('a').live('click', function()
{
     var location = $(this).attr('href');
     $.getJSON("http://paraschopra.com/temp/count.php",function(){ /*Point 1*/
         document.location = location; /*point 2*/
     });
     /*Point 3*/
     return false;
});

If you look at my comments inside you will see that when a link is clicked the function from live is activated, so the processor goes to point one and and initializes the json function, BUT it does not wait for for the json call to be success and carries on to Point 3, meaning that the callback for click ahas finished and then the link is followed.

in my version what we have done is added a callback to geJSON and returned false so that when Point 2 is skipped before the click callback has finished we return false to stop the browser from going to google.com... then when the json data has finally been successfully ping the called at POINT 2 is initiated.. and we programmatically send the browser to the url of the link...

and another point is you do not need the live function because the your not tracking new elements added to the dom.

RobertPitt
I implemented your method here: http://paraschopra.com/temp/bah_hack.html It never follows the google.com link
Paras Chopra
Thats because you have removed `var location = $(this).attr('href');`
RobertPitt
Yes, I got it. But even with that the solution is not exactly usable because I need to attach many more events such as 'submit' (for which the page unloads). Thanks anyway, the approach is very interesting.
Paras Chopra