Skutečně responzivní obrázky s responsive-images.js

Responzivní web design je něco, o čem v dnešní době hodně slýcháte. Okamžik, kdy jsem se skutečně začal věnovat responzivnímu designu, byl před několika měsíci, kdy jsem si začal uvědomovat, že „responzivní“ není jen o přizpůsobení vašich webových stránek velikosti obrazovky návštěvníků, ale mnohem víc než to.

Skutečně responzivní webdesign je o úplném přizpůsobení vašeho webu zařízení návštěvníka. Nezahrnuje pouze škálování, ale stejně důležité je snížení objemu dat, která přenášíte návštěvníkům, kteří mají pomalejší připojení, jako je 3G nebo dokonce EDGE.

Náhrada obrázku

Jedním z datově nejnáročnějších prvků na webu jsou obrázky. Abychom snížili množství dat, která poskytují našemu webu, můžeme je nahradit vhodnějšími obrázky pro zařízení, které náš návštěvník používá.

To je něco, co se nativně provádí pomocí HTML5 <picture> prvek W3C se vyvíjí. Prvek zatím není podporován v žádném prohlížeči, a dokud nebude, budeme k tomu potřebovat buď back-end nebo javascriptové řešení.

Plugin?

Na to již existuje řada pluginů. Když jsem však hledal jeden, nenašel jsem ten, který by vyhovoval mým potřebám. Většina z nich vyžadovala import nějakého dalšího javascriptu vedle pluginu a HTML značky, které používají, mi připadaly trochu špinavé a neuspořádané. Dostal jsem nápad, že by mohlo existovat čistší řešení.

responsive-images.js

Tehdy jsem přišel s nápadem na responsive-images.js. Což je jednoduchý a lehký javascriptový plugin (1kb) pro responzivní nahrazování obrázků. Používá čisté značení HTML a ke svému fungování nevyžaduje žádný další javascript.

Použití

<img alt='kitten!' data-src-base='demo/images/' data-src='<480:smallest.jpg                                                                            ,<768:small.jpg,
                                                          <960:medium.jpg, 
                                                          >960:big.jpg' />

<noscript><img alt='kitten!' src='demo/images/medium.jpg' /></noscript>

To je příklad toho, jak vypadá HTML. Docela čisté, že?

Použití označení nad prohlížečem načte demo/images/smallest.jpg, pokud je velikost zobrazované oblasti nižší (nebo rovna) 480 pixelům, demo/images/small.jpg, pokud je velikost zobrazované oblasti vyšší než 480 pixelů a nižší než 768 pixels, demo/images/medium.jpg, pokud je velikost zobrazované oblasti vyšší než 768 pixelů a méně než 960 pixelů, a demo/images/big.jpg, pokud je velikost zobrazované oblasti vyšší než 960 pixelů.

Retina?

Ale co když můj návštěvník používá zařízení sítnice?

<img alt='kitten!' data-src-base='demo/images/' data-src='<480:retina/smallest.jpg                                                                      ,<768:small.jpg,
                                                          <960:medium.jpg, 
                                                          >960:big.jpg' 

                                                data-src2x='<480:retina/smallest.jpg                                                                     ,<768:retina/small.jpg,
                                                            <960:retina/medium.jpg, 
                                                            >960:retina/big.jpg'/>

Tadaa! Pomocí data-src2x atribut je možné specifikovat obrázky, které má skript použít v případě, že má zařízení retina displej.

Něco, co mi na tomto značení vadilo, je, že všechny cesty k obrazu jsou definovány dvakrát. Obrázky sítnice bych obvykle uložil do samostatné podsložky jako demo/images/retina . Aby bylo označení trochu čistší, existuje také možnost změnit pouze data-src-base cesta pro zařízení sítnice pomocí data-src-base2x atribut.

<img alt='kitten!' data-src-base='demo/images/' data-src-base2x='demo/images/retina/' data-src='<480:smallest.jpg,
                       <768:small.jpg,
                       <960:medium.jpg, 
                       >960:big.jpg' />

Žádný atribut src?

Jo, z toho bych byl taky trochu nervózní.

Problém je ale, jak popisuje Smashing Magazine, že když nastavíte src Prohlížeč předběžně načte obrázek před použitím jakéhokoli javascriptu nebo dokonce CSS. Dvojité načítání obrázků by tak bylo nevyhnutelné.

Zatím zde nevidím žádné řešení (myšlenky, někdo?). Dobré na tom je, že plugin funguje i v prohlížečích jako IE5 a Opera Mobile a že zatím nejsou známy žádné nepodporované prohlížeče, takže je docela bezpečné vynechat src atribut.

V zákulisí

Jak samotný plugin funguje, je docela snadné. Prochází všechny obrázky na stránce a nejprve zkontroluje, zda obrázek obsahuje data-src atribut k určení, zda má být obrázek responzivní

if( !image.hasAttribute('data-src') ){
    continue;
} 

Poté rozdělí data-src atribut u každé čárky, který nám dává něco jako:

[<480:smallest.jpg, <768:small.jpg, <960:medium.jpg, >960:big.jpg]

Začne procházet výsledky a každý výsledek znovu rozdělí na dvojtečku

[<768, smallest.jpg]

Nyní určíme, zda mluvíme o nad 768 nebo pod 768, jednoduše zavoláním indexOf

if( query[0].indexOf('<') )

Na úhlové závorce opět rozdělíme strunu.

query[0].split('<') 

Nyní, než porovnáme 768 s naším zobrazením návštěvníků, musíme nejprve zjistit, zda je k dispozici nějaký nižší bod přerušení.

if( queriesList[(j -1)] ) 

V tomto případě je nižší sada bodů přerušení 480. Stejně jako výše rozdělíme řetězec v hranaté závorce a dvojtečce. Nyní zkontrolujeme, zda je výřez mezi našimi dvěma hodnotami.

viewport <= breakpoint && viewport > previous_breakpoint

Pokud je tomu tak, jednoduše změníme zdroj obrázku na zdroj, který patří k bodu přerušení 768

image.setAttribute('src', newSource); 

V tomto okamžiku je zdroj obrázku nastaven na demo/images/small.jpg a náš návštěvník si prohlíží správný obrázek pro jeho/její zařízení.

Podpora prohlížeče

Jakmile byl plugin funkční, začal jsem dělat nějaké testy prohlížeče. Testoval jsem na iOS, Androidu a různých desktopových prohlížečích. Protože jsem opravdu chtěl vidět, jak daleko mohu zvednout laťku, byly testovány i starší prohlížeče včetně IE5, Opera 10.6, Safari 4, Firefox 3 a Chrome 14. Na telefonech jsem testoval zařízení včetně Android 1.4, iOS 3 a dokonce i Opera Mobile .

Při provádění těchto testů jsem narazil na několik problémů. Dva byly pro IE, ale jeden byl pro náš milovaný iOS...

1. Viewports na mobilu

Snadné, že? Stačí zadat document.documentElement.clientWidth tam a máš se dobře. To jsem si myslel. Ale zdá se, že je to trochu složitější. Bez správného width=device-width nastavenou ve vaší metaznačce některá mobilní zařízení vrátí zobrazovanou oblast jako standardní velikost (980 pixelů), což způsobí, že skript vrátí obrázek vhodný pro zobrazovanou oblast o šířce 980 pixelů.

Zatím jsem pro to nenašel jediné řešení pro javascript a nejsem si jistý, jestli nějaké existuje. Vzhledem k tomu, že většina responzivních webů má width=device-width v jejich metaznačce to každopádně není velký problém. Rád bych to však ještě prozkoumal. Pokud si o tom myslíte, dejte mi vědět!

2. hasAttribute

Chcete-li zjistit, zda má obrázek data-src Atribut skripty používá hasAttribute metoda. Problém s tím však je, že IE7 a nižší to nepodporují. Pro ně jsem musel vytvořit řešení.

Nejprve zkontrolujeme, zda hasAttribute metoda je k dispozici

if( image.hasAttribute )

Pokud ano, použijeme hasAttribute metoda

image.hasAttribute('data-src')

Pokud není k dispozici, použijeme náhradní řešení

typeof image['data-src'] !== undefined

To jsou základy toho všeho. Poté jsem však narazil na další problém. Myslel jsem:IE7 a nižší nepodporují hasAttribute tak proč prostě nedefinovat hasAttribute metodu sám v případě, že neexistuje? Prototypová funkce nepřipadá v úvahu, protože IE7 a nižší je také nepodporují, takže jsem vytvořil normální.

if( !images[0].hasAttribute ){

    function hasAttr(el, attrName){
        return typeof el[attrName] !== undefined ? 1 : 0;
    }

} else {

    function hasAttr(el, attrName){
        return el.hasAttribute(attrName) ? 1 : 0;
    }

}

Už tady vidíte moji chybu? Deklarované funkce jsou načteny před provedením jakéhokoli jiného kódu, což znamená, že náš if příkaz je neplatný a výsledkem je Object does not support property or method hasAttribute chyba. Zkusme to znovu

if( !images[0].hasAttribute ){

    hasAttr = function(el, attrName){
        return typeof el[attrName] !== undefined ? 1 : 0;
    };

} else {

    hasAttr = function(el, attrName){
        return el.hasAttribute(attrName) ? 1 : 0;
    };

}

Pokud nyní použijeme funkční výrazy, kód se načte pouze tehdy, když interpret dosáhne tohoto řádku. Což je naše if výpisová práce.

3. addEventListener

Další na řadě je metoda addEventListener, která není dostupná v IE8 a nižších verzích. Místo toho používají metodu připojitEvent. Stejně jako u hasAttribute Použil jsem zde také jednoduché řešení tím, že jsem nejprve zkontroloval, zda addEventListener metoda existuje.

if( window.addEventListener ){

Pokud ano, použijte to!

window.addEventListener('load', makeImagesResponsive, false);
window.addEventListener('resize', makeImagesResponsive, false);

pokud ne, použijte attachEvent metoda

window.attachEvent('onload', makeImagesResponsive);
window.attachEvent('onresize', makeImagesResponsive);

A co vy?

Nezapomeňte se podívat na stránku Github. Bylo by mi ctí, kdybyste se chtěli rozdělit a přispět a dali byste mi vědět, co si myslíte. Rád bych slyšel váš názor!;)

Těšíte se?

Popadněte všechna zařízení, která najdete, a podívejte se na ukázku níže, kde najdete některá citlivá koťátka. :)

Zobrazit ukázku