Jan Martinek

rangeFloater — vertikální plavec

September 16, 2010

Během tvorbu webu pro časopis studentů mé fakulty (brzy bude online na adrese casopishalas.cz) mě napadlo použít pohyblivý blok s obsahem, který vyplní prázdné místo v kontextovém sloupci vedle textu článku. Obsahem bloku mohou být související články, novinky z twitter feedu nebo inzerce (důležité je, že blok je na obrazovce tehdy, kdy čtenář po dočtení článku volí další postup, zároveň však nenarušuje hlavní obsah stránky). V tomto článku se soustředím výhradně na technickou stránku věci.

Vyplnění různě velkého prostoru obsahem je možné dosáhnout tak, že se daný obsah posunuje ve vymezeném prostoru současně s posunem stránky a zůstává tak viditelný. Podobné řešení je použité na novinky.cz, kde se takto na homepage posunuje reklamní banner (ale jeho implementaci jsem nezkoumal, vypadá zapeklitě).

Pokud stále nevíte, o čem mluvím: video vydá za délkaVidea*25*1000 slov, tak se mrkněte. Anebo mrkněte přímo na živé demo.

Následuje popis, jak dosáhnout kýženého efektu s minimálními úpravami kódu stránky za pomocí CSS a jQuery. Postup je velice jednoduchý. Kompletní zdrojáky jsou na konci článku. Pokud nechcete zkoumat, jak to funguje, ale vzít zdrojáky a fungovat, mrkněte na rýdmí.

Takže, cílem je rozpohybovat poslední blok obsahu v kontextovém sloupci. Jak na to? V HTML není třeba výrazných změn, stačí nám daný obsahový blok obalit divem a přidat elementům vhodné třídy.


<div class="rangeFloaterWrapper">
   <div class="rangeFloater">
      <p>Obsah</p>
   </div>
</div>

Nejprve je třeba se zamyslet. Dokud k tomuto obsahu nedorolujeme, může poklidně sedět na své vlastní pozici a čekat na objevení. Poté, kdy má následovat uživatelovo scrollování, bude nejspíše optimální ho zafixovat vůči obrazovce. A pokud dojdeme na stránce úplně dolů, určitě nebudeme chtít, aby jeho obsah překryl patičku či další obsah na stránce. Takže máme tři stavy: základní (tvářící se jako obyčejný prvek v toku stránky), vertikálně plovoucí (pozor, toto nemá nic společného s css vlastností float) a zaparkovaný nad dalším obsahem.

zmeny-stavu.png

V CSS můžeme dané stavy popsat následujícím způsobem.


.rangeFloaterWrapper { position: relative; }
.rangeFloater { position: absolute; top: 0; }
.floatStateBasic { }
.floatStateActive { position: fixed; }
.floatStateBottom { }

Ano, to je vše - zbytek definujeme v javascriptu. V kódu je využito toho, že absolutní pozicování se řídí relativně pozicovaným rodičovským elementem, zatímco fixní se logicky řídí pouze okraji zobrazovací plochy. Třídy floatStateBasic, floatStateActive a floatStateBottom přiřazujeme javascriptem.

Rozsah posunování je určen hlavním obalujícím elementem obsahu, nejčastěji to bývá např. #content či #page. Důležité je, že patička a další obsah musí být již mimo tento element, pokud nechceme, aby tam rangeFloater zajížděl. Tento element můžete určit v datovém atributu rangeFloateru:

<div class="rangeFloater" data-contentWrapper="#content">

Anebo ponechat výchozí hodnotu mainContentWrapper, která se nastavuje v rangeFloater.js.

Máme tři stavy, v javascriptu tedy musíme ošetřit čtyři změny stavu (aktivní→plovoucí, plovoucí→zaparkovaný a opačně).

stavy.png

Taková změna je vázaná na event $(window).scroll, prakticky způsobuje jen výměnu tříd a vypadá např. takto:


if (viewfinderTop > rangeTop && $(this).hasClass('floatStateDefault')) {
   $(this).removeClass('floatStateDefault');
   $(this).addClass('floatStateActive');
}

Trochu pestřejší kód má změna plovoucí→zaparkovaný, kde je třeba uvést vertikální pozici, na níž bude element zaparkovaný. Ta je spočítána vůči obalujícímu (relativně pozicovanému) elementu rangeFloaterWrapper.

V případě, že vyhrazené místo pro posunování je příliš malé, proměníme jej ve zcela obyčejný blok - odebereme mu definující třídu rangeFloater.

Toto je v podstatě vše. Zbývají už jen kosmetické úpravy. Určitě jste si v CSS všimli definice top: 0;. Zde je třeba doplnit horní okraj elementu (pseudo margin, který se pak užívá i v plovoucím režimu a je měněn v parkovacím režimu).

Uvnitř rangeFloateru se průběžně počítají proměnné, díky kterým dochází k přechodům mezi stavy. Tyto je možné pro potřeby debugování vypisovat přímo v daném rangeFloateru pomocí nastavení proměnné development = 1; v souboru rangeFloater.js.

Zdrojové kódy (Licence New BSD)

RangeFloater je zatím nápad bez uživatelského testování a otestovaný jen v několika prohlížečích. V Internet Exploreru 6 neběží (nepodporuje position: fixed), ale zatím jsem to díky cílové skupině, která tento prohlížeč používá minimálně (VŠ studenti, na které neustále křičí Facebook, že IE 6 je z módy), neměl potřebu řešit a kód se v něm vůbec neprovádí. Pokud máte nějaké návrhy na zlepšení nebo znepokojující zprávy ohledně použitelnosti tohoto řešení, dejte vědět v komentářích.

Určitě by bylo možné vyhnout se position: fixed a dopočítávat absolutní polohu průběžně, ale renderování je pak poněkud cukavé, zvláště ve Firefoxu.