Foreground Sprites
Please consider a better solution with Better Foreground Sprites.
Most rollovers have become obsolete because they can be performed on background images with CSS sprites. If there are hover effects today, they usually come as text on a button background image, or as a text link with an icon next to it.
However, there are those rare cases when there is just an icon without text, like a “play” or “pause” button, or a magnification glass on an image to signal it can be enlarged. If you want to swap images when the element receives focus or on mouseover, traditionally you are stuck with two options:
- JavaScript to change the image source, or
- CSS sprites to change the position of a background image behind a transparent
gif
image.
I like neither. Using JavaScript and two images with pre-loading seems like a waste of resources when it can be done with CSS and one image only. Transparent gifs are so 1998, besides the background usually doesn’t get printed. So what we need on these occasions are CSS sprites for foreground images.
Until recently I thought that can’t be done, but then I stumbled upon some code for checkbox widgets in Dojo. Naturally they use a lot of JavaScript for the task. Anyway I like my default checkboxes and don’t have any urge to make them prettier (I have no doubt our design overlords would enforce aqua icons everywhere), but that made me think. Here’s a suggestion:
First a simple sprite which is 50px wide:
The HTML code uses the dimensions of a single icon for width and height to allow fast page rendering without reflow:
<a href="/big/" class="icon">
<img src="/img/icon-enlarge.gif" id="magnifier" width="19" height="15" alt="enlarge image" />
</a>
The corresponding CSS sets the link to display: block
, uses the single icon dimensions for the container, prevents overflow, and resets the image dimensions to auto
to prevent distortion. The actual image swapping is achieved by a negative left margin on hover.
a.icon {
display: block;
width: 19px;
height: 15px;
overflow: hidden;
}
a.icon img {
width: auto;
height: auto;
border: none;
}
a.icon:hover img, a.icon:focus img, a.icon img.hover {
margin-left: −31px;
}
For the sake of simplicity I ignored how the link would be floated or positioned. Also borders, a background color, or padding to increase the clickable area can be added to the parent element.
For simplicity I chose a link in the example, but the same can be done with a div
surrounding an <input type="image" />
. Then of course you need to define cursor: pointer
, and for anything below IE7 bind the DOM event to the hovering element (get the source):
<!--[if lt IE 7]>
<script type="text/javascript">
oIcon = {
setHoverClass : function() {
this.className = 'hover';
},
resetHoverClass : function() {
this.className = '';
}
};
var elm = document.getElementById('magnifier');
if (elm) {
elm.onmouseover = elm.onfocus = oIcon.setHoverClass;
elm.onmouseout = elm.onblur = oIcon.resetHoverClass;
}
</script>
<![endif]-->
Here is a working example. You know how to adjust the code if you need more than one class on the image.
That was nice and easy. It’s tested in Firefox 2, Opera 9, Internet Explorer 5+, and Safari 2. Screen readers should be okay with the alt
text.
Discussion
The trouble begins when people start turning off browser features.
People with disabled images won’t be able to read the alternative text because the visible area is limited by the parent container. That can be detected by comparing the known sprite size with the alt
text size. Then the class is set to anything but icon
so that the parent container expands.
More severe is when people disable style sheets because then they will see a squeezed image with multiple icons. If you are hyper-correct, you could replace the crushed multi-icon image with the correct single icon (you could use the class
or rel
attribute to define the icon type), but nobody wants to slice images. As a compromise I would suggest to replace the icon with its alt
text.
See the script to detect disabled images or CSS. (Note: it isn’t implemented here because I couldn’t test it properly on IE6 with stuff turned off.)
If JavaScript is unavailable, the user is stranded with what we gave him. Also icons without text are inherently evil because they can’t be enlarged by zoom readers. Did I forget any politically correct disclaimer for obscure use scenarios?
So we ended up with a CSS only version, but still some JavaScript is needed for the extra effort to provide the same experience to users of IE 5-6, and some more for certain user scenarios. Wouldn’t it be easier to slice and swap foreground images the old way? But then again slicing is a pain, and people already start using background images. Do people actually turn off CSS and images? Or is that another remnant of WCAG 1.0 from 1999?
[…] Auch wenn die Internetanbindungen immer breitbandiger werden ist die Performance stets ein wichtiges Thema bei der Entwicklung von Webanwendungen. Eine relativ einfache Möglichkeit für mehr Geschwindigkeit zu sorgen ist die Reduzierung der Zugriffe auf den Web-Server. […]
[...] I decided to try out some other methods. I had come up with this idea independently, but to give credit, this guy had a similar idea first. He called it a foreground-sprite. Basically, it meant bypassing the background-image CSS and using margin-left or positioning to achieve the same results. The benefit is huge for manageability. However, there are lots of options. Do you still use CSS to define the positions? Maybe jQuery could help manage. [...]
Given a similar situation, I would use a background image. In your magnify example, I would have some relevant text…”Magnify” or “Change zoom level” or something, and then use an image replacement technique to show the icon instead. This at least eliminates the need for javascript, and users without styles on will still see the text. Only major issue is folks with CSS on but images off.
[...] CSS Keys, and the search for the right Sprite Glen Lipka wanted an easier way to manage the “one big matrix image that CSS uses to chop up to display pieces” technique, and wrote about the right sprite where he delves into various cleaner workarounds for the fact that Firefox doesn’t support background-position-x. He ends up with foreground-sprites. [...]
[…] CSS: The right sprite. There are many ways of using a image sprite. Most ways use a background-image-property that can be moved using CSS. The problem is that this method results in many CSS definitions that are hard to manage. Other approach: bypassing the background-image in CSS and using margin-left or positioning to achieve the same results. The benefit is huge for manageability. Foreground Sprites: the same idea, more detailed explanation. […]
[…] CSS: The right sprite. Eine neue Anwendung von CSS Sprites-Technik: um die Anzahl der Angaben in CSS zu minimieren, wird anstelle des background-image-Attributs das margin-Attribut ausgenutzt. Foreground Sprites: gleicher Ansatz, detaillierter erklärt. […]
[…] Till sist har Martin Kliehm har en intressant lösning pÃ¥ hur man använder sprites för vanliga bilder (inte bara bakgrundsbilder). Det kan användas om man t.ex. vill ha en knapp som inte har nÃ¥gon text (en play-knapp eller dylikt). […]
[…] Auch wenn die Internetanbindungen immer breitbandiger werden ist die Performance stets ein wichtiges Thema bei der Entwicklung von Webanwendungen. Eine relativ einfache Möglichkeit für mehr Geschwindigkeit zu sorgen ist die Reduzierung der Zugriffe auf den Web-Server. Das könnte bedeuten, dass man möglichst wenige CSS- oder JavaScript-Dateien in seine Seiten einbindet. Aber auch der Einsatz vieler Grafiken kann das Laden stark verlangsamen. Abhilfe schaffen hier Image Sprites. […]
Awesome trick. Thanks!
hello
I had a question on implementation of hidden text for a given official homepage containing a foreground image. Is it possible to implement it; keeping in mind that Google, Yahoo and other search engines ban the page if they find out that we are using alternative tags to optimize SEO ranking of our page?
Also is it possible to use just a single hidden meta tag for a foreground image without offending the rules put up by Google or Yahoo? Does it make sense to optimize just with a single alternative meta tag?
Hmmm… I’m not sure why you would want to do this. Surely a block level link with text shifted out (text-indent:-9999px) and a background sprite would be more accessible? Just noticed: this was already said by at least one other person (Jeff L) above.