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.
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ě).
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.