O webu
Lazy loading obrázků

V případě, že je na stránce hodně obrázků, které nejsou ihned po příchodu na stránku vidět, jsou tzv. pod ohybem, může být rozumné je načítat až v momentě, kdy na ně návštěvník odroluje.

Sníží se tak objem přenesených dat i počet HTTP spojení.

Postup

Jak zmíněné načítání při odrolování vytvořit v JavaScriptu:

  1. Obrázky mimo viditelnou oblast stránky se po načtení skryjí.
  2. Při rolování (window.onscroll) se zkontroluje, které obrázky mají být vidět, a zobrazí se (donačtou se).

Skrytí obrázku

První zádrhel je v tom, že skrytí přes display: none nestačí, tj. něco jako:

<img src='obrazek.png' style='display: none'>

Sice obrázek skryje, ale fysickému stažení obrázku nezabrání. Naštěstí ale existují další postupy, jak načtení opravdu zabránit:

  1. Obrázku dát prázdný/nesmyslný atribut src a ten skutečný až v momentě, kdy se na obrázek odscrolluje. Původní src může být v nějakém vlastním atributu, ze kterého se hodnota skriptem přesune.

  2. Vložit obrázek jako CSS pozadí.

    <div class='obal-obrazku' style="display: none">
      <div style="width: 100px; height: 100px; background: url(obrazek.png)">
      </div
    <div

    Tady už display: none zafunguje a obrázek se automaticky nenačte.

    Důležité ale je display: none přidat rodiči <div>u s background obrázkem. Jinak se i tak v některých prohlížečích obrázek stáhne (test s rodičem / elementem s pozadím).

  3. Použít značku <noscript>. Její obsah se při zapnutém JS nevyhodnocuje. Nevýhoda je, že v IE 7 se z ní nedá přečíst innerHTML. I tak ale jde přečíst hodnotu <noscript> atributu, což pro uložení adresy obrázku k pozdějšímu načtení může stačit (ukázka).

    Hotové řešení založené na jQuery, které využívá tuto techniku.

  4. Vypsat kolem obrázku JavaScriptem značku <script> s neznámým MIME typem. Prohlížeč takový kód nevyhodnotí, ale jeho obsah půjde spolehlivě vydolovat z innerHTML (ukázka).
  5. Použít značku <template>. Ta zatím ale nefuguje v žádném IE, jen v Chrome a Firefoxu.

Konkrétní volba řešení by měla záviset na požadavcích, zejména s ohledem na vyhledávače:

  • První a druhá možnost obrázky pro roboty v podstatě zneviditelní. Proto je vhodná jen v případě, že to nevadí / vyhledávač se může k obrázku dostat někde jinde.
  • U značky <template> není jasné, jak se k ní budou v budoucnu roboti chovat. Zatím je ale Googlem indexována.
  • Vypsání <script>u z neznámým MIME typem je značně nestandardní.

Hotové řešení

HTML

<div class="img">
  <script>document.write("<script type='text/lazy-loading'>")</script>
    <img src="obrazek.png" width="100" height="100">
  <script>document.write("<\/script>")</script>
</div>

JS

var lazyImages = [];
function inViewPort(img) {
	var coords = img.getBoundingClientRect();
	return (coords.top >= 0 && coords.left >= 0 && coords.top) <= (window.innerHeight || document.documentElement.clientHeight);
}
window.onload = function () {
	var imgArea = document; // kde se obrázky hledají
	var images = imgArea.getElementsByTagName("div");
	for (var i = images.length - 1; i > 10; i--) {
		if (images[i].className != "img") continue;        
		lazyImages.push(images[i]);		
	}
}
window.onscroll = function() {
	for (var i = lazyImages.length - 1; i >= 0; i--) {
		if (inViewPort(lazyImages[i])) {
			var hiddenCode = lazyImages[i].getElementsByTagName("script")[1];
			if (hiddenCode) {
				lazyImages[i].innerHTML = hiddenCode.innerHTML;
			}
			lazyImages.splice(i, 1);
		}
	}
}

Alternativní řešení

Problém výše uvedeného postupu může být v tom, že při jakémkoliv rolování se budou vždy procházet všechny obrázky a bude se kontrolovat jejich posice a zda jsou vidět (funkce inViewPort).

Pokud by to stránku zpomalovalo, nabízí se si při načtení a změně rozměrů stránky ukládat posici obrázků.

Ještě mě napadl jeden postup. K obrázkům vytvořeným jako CSS pozadí uložit jejich přibližnou posici do CSS třídy (zaokrouhlenou třeba na stovky — například top-2000 pro obrázky, co jsou přibližně 2000 pixelů od začátku stránky). A při rolování vytvořit skriptem CSS, které obrázek/obrázky s danou třídou zviditelní.

Něco ve smyslu:

pridatCss(".top-" + zaokrouhlit(odrolovanoShora + vyskaOkna) + " {display: block}");

Odkazy jinam

  • be]Lazy.js – miniaturní knihovna pro lazy loading obrázků; skutečný src obrázku maskuje do data-atributu