English-only | Jenom česky | Bilingual/Dvojjazyčně

CSS Technique: Fast Rollovers Without Preload

Řešení CSS: Rychlé rollovery bez načítání

When using CSS image rollovers, two, three, or more images must be loaded (and often be preloaded for best results). We've got one image for each state (normal, hover, active, visited etc). Putting all states into one image makes dynamic changes faster and requires no preload.

Let's have a simple example. The menu items are the a-elements with display:block. Proper padding and background image for a, a:hover and a:active make the rollover. To simplify the rollover, I used only one picture containing three states of a button — normal, :hover, and :active.

rollover background image
Fig. 1: Three states together in one image

Usually, in CSS rollovers, we use background images in this way:

Když vytváříme dynamická obrázková tlačítka (rollovery), musí se obvykle do prohlížeče načíst dva, tři i více obrázků (pro základní stav, pro :hover, obrázek pro :active atd.). Často se načítají i předem na pozadí, aby uživatel při změně stavu nemusel čekat na načtení dalšího obrázku (preload). Když ale všechny tyto obrázky spojíme do jediného obrázku, dynamické změny urychlíme a navíc není třeba žádného preloadu.

Vezměme si jednoduchý příklad. Položky menu jsou tvořeny prvky "a" s nastavením display:block. Vhodné nastavení výplně (padding) a obrázek na pozadí pro a, a:hover a a:active vytvářejí dynamické tlačítko (rollover). Pro jeho zjednodušení jsem použil jen jeden obrázek, obsahující tři stavy tlačítka — normální, :hover a :active.

Obrázek na pozadí pro rollover
Obr. 1: Tři stavy současně v jednom obrázku

V CSS rolloverech používáme obrázky na pozadí obvykle nějak takto:

#menu a {
   ...
   background: url("button.gif") top left no-repeat;
   }
#menu a:hover {
   ...
   background-image: url("button-over.gif");
   }
#menu a:active {
   ...
   background-image: url("button-active.gif");
   }

/* etc... */

Using one common picture, we don't need to change the background image. We just change its background-position. The :hover state will use the background image moved proper count of pixels (in the example the shift is 157px to the left), the :active state will use bigger shift (by 314px in the example).

Když ale použijeme jeden společný obrázek, měnit obrázek na pozadí nemusíme. Stačí změnit jeho umístění (hodnota background-position). Pro stav :hover se použije pozadí posunuté o vhodný počet pixelů (v našem příkladu to je o 157px doleva), stav :active použije posun ještě větší (v příkladu o 314px).

#menu a {
   background: url("button.gif") 0 0 no-repeat;
   ...
   }
#menu a:hover {
   background-position: -157px 0;
   ...
   }
#menu a:active {
   background-position: -314px 0;
   ...
   }

That's all. Just one image is used. No preload is needed. State switching is as fast as possible (moving background position is much faster than replacing background image). AFAIK, it works in every CSS2 capable browser (IE5+, Mozilla, Opera, Safari etc.)

A to je celé. Je použit jen jediný obrázek, není třeba žádný preload, přepínání mezi stavy je tak rychlé, jak jen to jde (posunout obrázek na pozadí je mnohem rychlejší než jej vyměnit). Pokud vím, toto řešení funguje v každém prohlížeči podporujícím CSS2 (IE5+, Mozilla, Opera, Safari atd.)


Update

Simple yet strong update comes from Marek Blaha (Czech author). This workaround solves the problem when Windows IE sometimes slowly reloads the background image. It causes the element flickers while mouse hovering.

The solution is just simple: the background image (moved to the "hover position") is attached to the outer element, and the same background is attached to the inner A element as well (in the "base position", though). The :hover state just changes the A's background to transparent making the 'outer' background bellow visible. Thus, no background is beeing switched and IE has no more reason to flicker.

Check the source code of the Example 2 to see the solution at work. It works fine in every browser I've met (excluding MacIE, which fails here)...

Jednoduchý, ale účinný doplněk poslal Marek Blaha. Tento postup řeší problém s pomalým načítáním pozadí ve Windows IE, což způsobuje blikání prvku, když nad ním přejíždí kurzor.

Řešení je v podstatě prosté: obrázek na pozadí (posunutý do pozice pro :hover) je přiřazen vnějšímu prvku a stejný obrázek má i vnitřní prvek A (ale v "základní" pozici). Ve stavu A:hover se jeho pozadí změní na průhledné, čímž se zviditelní pozadí "vnějšího" prvku pod ním. Tím pádem se s obrázkem na pozadí vůbec nemanipuluje a IE už nemá důvod blikat.

Prohlédněte si zdrojový kód příkladu 2, kde uvidíte toto řešení v praxi. Funguje prakticky v každém prohlížeči, který jsem potkal (kromě MacIE, kde toto řešení selhává)...