Image source swapping, CSS, and Safari

Last week I was putting the finishing touches on a small website I created for a friend. Specifically, I was jazzing up the image gallery with an ‘Image loading’ animation, so that visitors knew to hang around while a new image loaded. In the process I made an interesting discovery about the way Safari (Safari 1.2 at any rate) handles javascript image source swapping.

The image gallery was controlled by a Javascript which swapped the src parameter of a placeholder img tag each time the user selected a new image.

In my HTML markup I had a basic image tag with an ID applied to it:

<img id="placeholderimage" alt="" />

When the user selected a new image (by clicking ‘next’ or ‘prev’ in my image gallery navigation), a javascript was triggered to load a jpeg into the placeholder:

document.getElementById('placeholderimage').src = "mynewpic.jpg";

The problem with this method is that while the new image loads into the placeholder, the old image remains on the page. To the user it appears as if nothing is happening. The solution is to hide the image while it loads, and show the user some sort of load indicator while they wait. When the image has completed loading, reveal it again and hide the load indicator.

The specifics of hiding/revealing the load indicator are beyond the scope of this article, so I won’t bore you with them here. Instead I’ll demonstrate the method by which I hid and revealed the image, as that is what caused problems in Safari.

To hide the image, I used javascript to apply the CSS style display:none before loading the new jpeg. I also created an onload event handler for my image, to detect when it had finished loading. Inside my onload function I revealed the image again:

        document.getElementById('placeholderimage').style.display = "block";
document.getElementById('placeholderimage').style.display = "none";
document.getElementById('placeholderimage').src = "mynewpic.jpg";

Easy peasy. The image is removed from the document flow when it starts to load, and returned when it finishes.

Everything seemed to be rocking nicely, until I fired up the site Safari 1.2. The image was hiding as expected, but my onload function was never evoked. After some head scratching I traced the problem to my use of display:none when hiding the image. I think Safari’s logic is that because the image is no longer part of the document flow, there is no point bothering to load it. Unlike other browsers, Safari never initiated the image load, and that’s why the onload never fired.

Armed with this new knowledge, we have a couple of “Safari friendly” options at our displosal:

  • Use visibility not display

    If we use visibility = "hidden"; instead of display = "none"; to hide the image, everything works fine. The problem with using visibility is that the image is not removed from the document flow, meaning a gap is left on the page where the image used to be. Depending on the layout of your image gallery that may not be an issue for you – it may in fact be desirable. If it is an issue, you can set the height of the image (or it’s containing element if there is one) to zero, then use the onload handler to reset it to it’s correct height. Fiddly, but it works.

  • Move the image outside the viewport

    If your image has the CSS property position:absolute, then you should be able to move it off screen by setting either it’s top or left property to a suitable low value, for example: left: -1000em; That ought to place the image off screen out of sight.

There may be other alternatives, and you can choose the one that suits you best. The important thing to remember is don’t use display:none to hide images while you swap their image source, or risk the wrath of Safari users!

For anyone interested to see the final implementation of my image gallery, here it is.