AJAX banner rotation

February 13th, 2006. Tagged: Ajax

So here's the case: rotate ads on a website, even when the page is not reloaded. If the chances are that the visitor will spend more time on a page, it makes sense to opt-in for displaying more than one ad at one page load. I've done this in the past using iframes, but hey, it's 21st century (the last time I checked) and we have AJAX! ;)

I also wanted to have the flexibility to display any ad type, which means I should be able to rotate pieces of HTML, rather than just images. Using HTML chunks, the ad can be an image, or formatted text, or swf, etc.

Demo

If you're curious to see the demo, go straight ahead (nothing impressive, I must admit, just rotating stuff ;) ).

Getting busy

The "architecture" is fairly trivial - one HTML page (demo.html) and one JavaScript (ajax-banner.js) that makes XMLHTTP requests to a server-side script (ajax-banner.php). The server side (that has all the logic to figure out the next advertisement to show) returns XML response which is received by the caller JS and then displayed in a div on the HTML page.

<div id="ajax-banner"></div>

The server-side script

For the purposes of the example, the ad logic is simple - an array of HTML chunks, we pick a random one and return it as part of the XML response. In addition to the HTML chunk, as part of the XML response, the server-side script also produces an "instruction" as to how long should the ad be displayed.

Here's how the script looks like:

<?php
// an array of banners
$banners = array (
    '<em>ban</em>ner 1',
    'banner <strong>2.0.</strong>',
    '<h1>big banner</h1>',
    'Inline JS won\'t run :( <script>alert("boom!")</script>',
    'External JS won\'t run :(( <script src="test.js"></script>',
    '<img src="phpied.gif" />'

);
// pick a random one
$html = $banners[array_rand($banners)];

// send XML headers
header('Content-type: text/xml');
echo '<?xml version="1.0" ?>';

// print the XML response
?>
<banner>
    <content><?php echo htmlentities($html); ?></content>
    <reload>5000</reload>
</banner>

The javascript

The javascript consists of three functions. The main "dispatcher" is nextAd() which loads the next ad in the div. nextAd() calls the makeHttpRequest() function to send a request to the server-side script and then passes the response to loadBanner() which actually updates the banner div.

makeHttpRequest()

I've been using this simple function I came up with for a few projects (like theInvisibleAd.com) No AJAX libraries or toolkits are necessary as the task is pretty straightforward.

This function makes an HTTP request and passes the response to another function, specified on-the-fly. The function is the bare basics of the AJAX thing and if you're interested in the details you can check my article on SitePoint where I've used it to create a simple web console application.

nextAd()

This function makes the request and passes the response to loadBanner(). The HTTP request uses the current time in milliseconds to try to prevent the client browser from using a cached copy from the previous request.

function nextAd()
{
    var now = new Date();
    var url = 'ajax-banner.php?ts=' + now.getTime();
    makeHttpRequest(url, 'loadBanner', true);
}

loadBanner()

loadBanner() accepts the XML response generated by the server-side script. The XML has two tags - content (the HTML to be displayed) and reload (the allocated time for this ad to be displayed). The banner div is updated with the new HTML content using innerHTML the simplest, yet questionable by some purists, including myself, way of updating an already loaded page with JS.

Once the ad is updated, old timeout is cleared (if any) and a new timeout is set, the time in milliseconds after which we'll call nextAd() again to make the next HTTP request.

function loadBanner(xml)
{
    var html_content = xml.getElementsByTagName('content').item(0).firstChild.nodeValue;
    var reload_after = xml.getElementsByTagName('reload').item(0).firstChild.nodeValue;
    document.getElementById('ajax-banner').innerHTML = html_content;

    try {
        clearTimeout(to);
    } catch (e) {}

    to = setTimeout("nextAd()", parseInt(reload_after));
}

Conclusion

There's one drawback to this method though - any javascripts (inline or external) in the ad's body won't get executed. I have a remedy for this - including JavaScripts on the fly, outlined in this posting, but in this case I need to know something about the HTML returned by the server-side script, which can be passed as another tag in the XML, saying something like <is-this-js> -> you bettcha! In this case the solution will become less of a one-size-fits-all, but still a viable option.

Overall, if you don't plan to use JS in your ads, the solution outlined in this post is very simple and seems like ideal for replacing iframe-based banner rotations using sexier AJAX.

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov