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').onload=function(){
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 ofdisplay = "none";
to hide the image, everything works fine. The problem with usingvisibility
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’stop
orleft
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.