How to Make Simple Cross-Domain Ajax Requests With Responses
For Read It Later for the iPhone, I built a ‘Tap to Save’ bookmarklet for Mobile Safari. When using Tap to Save, instead of opening a link when clicked, the link is saved to a user’s reading list. This makes it easy to save a few links from a web page without having to open each one individually. The problem I came across however was I could not do an ajax request to send the link to the Read It Later server because cross-domain browser restrictions. I could use an iframe to send the request, but again because of cross-domain issues, I would not be able to retrieve a response from inside of it. After the request was sent, there were 3 possible outcomes: Success, the user needs to login, or there was a problem saving. The method that eventually dawned on me is rather hackish, but it works none-the-less. It provides a way to send a request to any domain and get a basic response. The solution: Images are not subject to cross domain restrictions. So load an ‘image’ with a get string that hits a script on the server and then returns an different sized image per possible outcome. If that makes absolutely no sense, let me try to illustrate with an example that provides a response with two options: success/failure.Starting the Request: (Javascript: Client Side)
Handling the Request (Server Side):
The image is now loading this script:
<?php
/* ... do your request processing here ... */
// Based on the result of the script, we either return a
// 1px wide image (in the case of success) or
// 2px wide image (in the case of failure)
// you'll need to create these images yourself
if ($success) {
$file = file_get_contents( '1x1.gif' );
} else {
$file = file_get_contents( '2x1.gif' );
}
// output the image
$length = strlen($file);
header('Last-Modified: '.date('r'));
header('Accept-Ranges: bytes');
header('Content-Length: '.$length);
header('Content-Type: image/gif');
print($file);
?>
Handling the Response: (Javascript: Client Side)
Back on the javascript side of things, we have our ajaxTimer checking to see if the image is loaded. Once the image loads, we’ll check the width.
function isImageLoaded() {
if (ajaxImg.complete) {
var w = ajaxImg.width;
if (w == 1) {
alert('Success!');
} else if (w == 2) {
alert('Failed!');
}
// Hide the image (even though you probably won't see it
ajaxImg.style.display = 'none';
// Stop the timer
clearInterval(ajaxTimer);
}
}
Simple Works Best
As you can see, this works well if you only have a few possible outcomes of the transaction or if you do not need to return a literal response like generated markup.IE Woes
Surprisingly, this DOES work in IE6 and IE7, however there is a little bit of extra work. The url of the request you load into the src of the image has to have .gif in it. For example: ‘script.php’ won’t work, but ‘script.gif’ will. So to make this work you’ll need to add an Htaccess rewrite rule to redirect the script.gif to your script.php file.When dynamically generating iFrames in IE6 and SSL, their location must be preloaded
I had a script that was creating empty iFrames serving as an Ajax method. But in IE6, I kept receiving a ‘mixed secure and insecure content’ warning. Combing through my source code I couldn’t find what element wasn’t being loaded without https. Well I finally Google’d the problem and found the answer. When I was creating the iFrames, I created them first and then set the src value. I discovered however you need to set the iFrame’s src value upon creation with a secure page. If you need a temporary location to point to before loading without an extra page request, you can enter ‘javascript:false’.
Where I learned about it:
http://crazybob.org/2005/06/ajax-ie-iframe-bug.html