Asynchronous Image Updater Using jQuery

In some applications there may be an image on the server that is dynamic. I want the image rendered on my web page to reflect the changes on the server. However, the browser caching mechanism work against us and fetch the stale cached image. In this case, we must ‘trick’ the browser into thinking the image is new. In addition, I want this update to happen asynchronously (without the need for a full page refresh). The post will provide one solution to this problem.

HTML

Here is some mark up that contains an image:

<span class="screen_shot">
    <img src="<%= Url.Action("Thumb", "Image", new { agentId = item.AgentId }) %>" alt="screen shot" />
</span>

The relevant mark up for this example is the image tag. Don’t get too caught up in the ASP.NET MVC mark up that defines the ‘src’ attribute. This is an MVC image handler that I am using to serve up images. It simply allows me to inject a controller into the image request so I can enforce policy (authentication / authorization). The resulting ‘src’ attribute will look something like:

http://your_domain.com/monitor/Image/Thumb?agentId=fa9ca2df-eb39-4e8f-a8ff-58583c9fedb4

In my particular application I may have many of these span elements on the page. For instance, I may have up to 30 of these images on the page.

JavaScript & jQuery

The ‘trick’ that we will use to force the browser to re-fetch the image is to add another query string parameter to the image URL. The new parameter will make the image URL unique and the browser will fetch what it thinks is a new image. Here is the code that does this:

<script language="javascript" type="text/javascript">
    // globals
    var currentIndex = 0;

    // do this as soon as possible
    $(document).ready(function() {

        // call this every second
        setInterval(reloadScreen, 1000);
    });

    // update the url of the current image object
    function reloadScreen() {

        // get a list of current objects
        var screens = $("span.screen_shot");

        // exit early if none exist
        if (screens == undefined
                || screens == null
                || screens.size() == 0) {
            return;
        }

        // get the current object
        var span = screens[currentIndex];

        // increment to the next or first
        currentIndex++;
        if (currentIndex >= screens.size()) {
            currentIndex = 0;
        }

        // get the current url
        var url = $(span).find("img").attr("src");


        // split off any extra parameters
        var parts = url.split("&");

        // add a unique extra parameter
        var now = new Date();
        var urlImg = parts[0] + "&t=" + now.getTime();

        // update the image to the new url
        $(span).find("img").attr("src", urlImg);
    }
</script>

The JavaScript first uses the jQuery ‘ready’ function to start an interval timer as soon as the DOM has loaded. Recall that I may have up to 30 of these images on the page and I want them all to be refreshed regularly. My first attempt was to refresh them all every 15 seconds. However, that implementation ran into the browser’s limit on concurrent requests. Many of the images simply were never updated. The ‘reloadScreen’ function refreshes one image at a time from the first to the last and then cycles back to the beginning to start again with the first.

This ‘reloadScreen’ function begins with a jQuery selector to get all the spans that contain images that need refreshing. For a bit of optimization, this query can probably (I never tested this) be cached as a global and re-used. The next bit of code validates we have at least one span. Then the current span element is selected and the ‘currentIndex’ is incremented. Once we have the current span, the code extracts the ‘src’ attribute from the image tag and splits off any previously added parameters. A unique parameter is then created using the current time and added to create a new URL.

Once you set the ‘src’ attribute in the final line of the ‘reloadScreen’ function, the browser will perform a request and update the image. Because this is done asynchronously, this provides an ‘ajax-like’ experience without using the XmlHttpRequest (XHR) API.

The query string parameter should not match any property names in any object the controller has as input parameters. Otherwise, the MVC model binder will attempt to bind this parameter to that property. In my case the additional ‘t’ parameter is ignored by the model binder and is not used by my image handler controller.

Summary

I did look at using HTML meta-tags to tell the browser not to cache the image. Various sources indicated that this was not a reliable method as some browsers will still cache. The method described in this post of using a unique URL provides an easy way to force the browser to by-pass the cache and re-fetch the image from the server. JavaScript & jQuery make it easy to dynamically create these unique URLs. If you have improvements or other ways that you use to accomplish this same task, I would be interested. Please leave a comment.

Comments
  1. my feeds

Leave a Reply

Your email address will not be published. Required fields are marked *

*