Posun obsahu se zachytáváním

Když uživatel posouvá stránku, málokdy se mu povede, že posun skončí přesně na začátku odstavce nebo obrázku, což pak zhoršuje UX (dojem ze stránky). Do teď se podobné situace řešili pomocí javaskriptu, ale kombinace JS a skrolování většinou UX ještě zhorší.

Díky nové CSS specifikaci Scroll snap můžete tyto situace vyřešit automaticky jen pomocí CSS definice.

Nová specifikace je již podporována v prohlížečích založených na Webkit (tedy Chrome, Safari a nový Edge; Opera zatím ne). Firefox momentálně podporuje starší zamítnutou specifikaci. Firefox od verze 68 podporuje již novou specifikaci. Podpora ve starém Internet Explorer a Edge s MS jádrem viz níže.

Typ přichycení

Základní vlastností, pomocí které určíte, že se daný prvek má skrolovat, je scroll-snap-type společně s overflow: scroll (nebo auto). Vlastnost očekává kombinaci dvou hodnot. První je jedna z NONEXY a BOTH(resp. místo X a Y lze použít logické hodnoty block a inline) a určuje, ve kterém směru se má skrolovat (nebo zda v obou směrech či žádném), a druhá pak určuje, jestli se má posun na daném bodě zastavit pokaždé (mandatory) nebo jen při přiblížení se k němu (proximity):

body { //stránka se skroluje bez zachytávání
    scroll-snap-type: none; //výchozí hodnota
}
article {
    overflow: hidden;
    overflow-y: auto;
    scroll-snap-type: Y proximity;     //fyzické
    scroll-snap-type: block proximity; //logické
}
.gallery {
    overflow: hidden;
    overflow-x: scroll;
    scroll-snap-type: X mandatory;      //fyzické
    scroll-snap-type: inline mandatory; //logické
}
[/css]

Příklad říká, že svislý posun článku se má přichytit, jen když posun skončil poblíž začátku nadefinovaného prvku (např. odstavce). V opačném případě (např. dlouhý odstavec, který se nevejde na obrazovku) se přichycení neprovede. Naopak horizontální posun obrázků (vedle sebe) se musí vždy přesně zastavit na požadovaném bodě.

V praxi rozdíl mezi mandatory a proximity vypadá tak, že u mandatory se prostor mezi prvky rozdělí na polovinu a kdykoliv posun skončí, rozhodne se, zda je blíže předchozí nebo následující položce a podle toho se provede přichycení. Naopak u proximity se vyhradí určitá oblast (třeba +/- 50px) kolem začátku prvku, který se má přichytit a pokud posun skončil v této oblasti, přichycení se provede; pokud ale posun skončil mimo jakoukoliv oblast přichycení, posun skončí bez něj.

Všimněte si, že typ uchycení vždy definujeme na kontejneru, který se posouvá!

Body uchycení

Když máte určený typ přichycení, musíme určit body, ke kterým se bude posun přichycovat.

Body přichycení nastavíme prvkům uvnitř kontejneru, který se posouvá pomocí vlastnosti scroll-snap-align:

article > p {
    scroll-snap-align: start;
}
.gallery > img {
    scroll-snap-align: center;
}
.gallery > img:last-child {
    scroll-snap-align: end;
}

Vlastnost scroll-snap-align má tři možné hodnoty, které určují, zda se u daného prvku posun zastaví u jeho začátku (start), prostředku (center) nebo konce (end). V příkladu si tedy všimněte, že u článku se posun zastaví vždy na začátku odstavce, zatímco u galerie se zastaví tak, aby byl obrázek vždy vycentrovaný. Výjimku máme u posledního obrázku galerie, kde se má posun zastavit tak, aby byl poslední obrázek zarovnaný ke konci galerie.

Pozor na nepřístupný obsah

Jakmile nadefinujete povinné (mandatory) body zachycení, bude se jimi prohlížeč řídit a bude obsah posouvat mezi nimi. Pokud tedy např. určíte uchycení na začátky odstavců, ale budete mít v textu odstavec delší než je jedna obrazovka (např. u mobilního zařízení), nebude si moci uživatel posunou text tak, aby si přečetl konec odstavce (protože obsah přeskočí automaticky na další odstavec). Povinné přichycení by se tedy mělo používat pouze na prvky jako jsou obrázky (či obdobné prvky), které se mohou zmenšit a přizpůsobit na velikost obrazovky.

Povinné body uchycení je potřeba používat s rozvahou a pouze tam, kde se prvky mohou zmenšit tak, aby se vešli na obrazovku!

Pokud si nejste jisti, kombinujte povinné přichycení s CSS podmínkou @media (min-*) tak, aby se aplikovaly jen na dostatečně velkých zařízeních a monitorech.

@media (min-height: 400px) {
   /* na příliš nízkých displejích
      se nebudou odstavce přichytávat
      k okrajům obrazovky */
    article {
        overflow: hidden;
        overflow-y: auto;
        scroll-snap-type: Y mandatory;
    }
}

Na druhou stranu Scroll snap bylo navrženo právě pro použití na malých mobilních zařízeních (malý berte s rezervou, myslí se tím současné 6″ phablety), na kterých může být problém posouvat stránky s galeriemi a dalšími grafickými prvky, takže výše uvedené omezení na rozlišení nemusí dávat smysl a spíše byste se měli zamyslet nad tím, jak obsah pro mobilní zařízení přizpůsobit.

Okraje

Tak, jako můžete u DIVů, odstavců apod. určit, jaké mají mít okraje (padding a margin), aby jejich obsah pěkně pasoval do stránky, můžete stejně určit okraje zachycení (tedy vzdálenost od okraje):

article {
    scroll-padding: 2em;
}

Tento příklad dává smysl pouze v kombinaci s předchozími příklady. U článku jsme nastavili, že posun se má přichytit k začátku odstavce. V kombinaci s vlastností scroll-padding pak říkáme, že za začátek odstavce považujeme místo o (zhruba) 2 řádky výše než je skutečný začátek odstavce. V praxi to tedy znamená, že posun se zastaví tak, že z předchozího odstavce bude zobrazen zhruba 1 až 2 řádky a další odstavec bude tedy vidět vždy od začátku. Tím vytvoříme pěkně čitelný text, protože uživatel se bude moci přesvědčit, že čte pokračování textu a že stránka třeba nepřeskočila o dva nebo více odstavců.

Padding posunu určujeme na kontejneru, který se posouvá!

Naopak pokud chcete nastavit pro každý prvek jiný posun, můžeme prvkům v kontejneru určit okraj posunu vlastností scroll-margin:

.gallery > img {
    scroll-margin: 10px;
}
.gallery > img:last-child {
    scroll-margin: 0;
}

Pro galerii, kde se posun zarovnává na prostředek obrázků říkáme, že obrázek se má brát jako že je o 10 pixelů větší. Pokud by se tedy nevešel celý do stránky (viewport kontejneru), bude zarovnaný doleva a odsazený o 10px doprava. Druhé pravidlo pak říká, že poslední obrázek se má zarovnat přesně s koncem kontejneru.

Margin posunu vždy určujeme na prvcích uvnitř kontejneru!

Obě vlastnosti scroll-padding a scroll-margin se chovají stejně jako původní margin a padding a je tedy možno nadefinovat různé rozměry pro různé strany:

article {  /* okraj jen nahoře */
    scroll-padding: 0;
    scroll-padding-top: 2em;
}
.gallery {  /* okraj jen vlevo */
   scroll-margin: 0 0 0 10px;
}

Jak už bylo zmíněno výše, Scroll Snap byl již navržen s ohledem na logické vlastnosti, takže místo např. scroll-padding-top můžete použít vlastnost scroll-padding-block-start.

Podpora v prohlížečích

Vlastnosti pro padding a margin nemusejí být podporovány ve všech prohlížečích, které Scroll snap podporují (např. v Chrome 72 zatím nejsou podporovány).

Funkčnost byla prvně implementována ve Webkit, takže podporu má (nyní v 2018/2019) Chrome od verze 69+ (září 2018; na Android o něco dříve) a Safari 11+ (září 2017). Opera a ostatní prohlížeče využívající webkit (např. Samsung Internet) ale tato vlastnost zatím nezapnuly (experimental flag). Starý Firefox (39+) a Edge (12-18) podporují starší verzi specifikace, takže něco nemusí fungovat přesně tak, jak byste očekávali. Nové verze Firefox 68 (červenec 2019) a Edge 76 (konec 2019) již podporují novou specifikaci.

Při použití scroll snap vlastností je potřeba použít prefix -ms-pro podporu v Edge (IE10 a IE11 také podporují prefix -ms-, ale jen když je detekována dotyková obrazovka).

V Chrome 71, které Scroll snap podporuje, jsem se setkal s tím, že po načtení stránky sice přichycení fungovalo, ale po delší době nebo po změně obsahu stránky přes JS/AJAX se přichycení rozbilo a kontejnery se posouvali bez něj.

Javascript

Společně s CSS vlastnostmi přichází i JS API pro posun pomocí programu (resp. obě funkce již nějakou dobu existují, ale nyní podporují zachytávání).

Pokud chcete posunout kontejner na určitý prvek, použijte jeho funkci scrollIntoView(), která bere v úvahu nastavené body zachytávání a okraje (scroll-padding a scroll-margin):

<a href="#example"
   onclick=document.getElementById('example')
           .scrollIntoView(); return false;">
   Zobrazit příklad
</a>

Pozor na to, že funkce scrollIntoView() nemusí existovat. V uvedeném příkladu bude vše fungovat, protože pokud funkce neexistuje, JS se neprovede a odkaz bude fungovat postaru pomocí href na ID prvku. Ve složitějších skriptech ale budete muset kontrolovat, zda funkce existuje, a pokud ne, provést posun postaru pomocí scrollTop nebo scrollLeft.

Stejně funguje i funkce scrollTo(X, Y), která taktéž bere v úvahu nadefinované body přichycení:

//posune stránku na konec
// (s ohledem na přichycení tam,
//   kde to prohlížeč podporuje)
window.scrollTo(
    0, document.body.getBoundingClientRect().y
);

Funkce scrollIntoView() má několik parametrů (předávaných v objektu): parametr behavior (pozor, není tam „u“!) může mít hodnoty instant (provede skok bez animace) nebo smooth (plynule posune stránku pomocí animace). Výchozí hodnota je auto (podle vzdálenosti); parametry block a inline určují, kam se prvek zarovná. Hodnota block určuje zarovnání bloků (většinou nahoru nebo dolu) a inline zarovnání v řádce (tedy doleva a doprava). Hodnoty jsou start, center, end a nearest, kde start a end určuje zarovnání nahoru nebo dolu resp. doleva nebo doprava. Hodnota center určuje zarovnání prvku na střed prostoru a nearest zarovná prvek k bližšímu z okrajů. Výchozí hodnoty jsou block: center a inline: nearest.

Parametr behavior podporují pouze prohlížeče s podporou scroll-snap-align. Některé prohlížeče nepodporují hodnoty center a nearest (výchozí hodnoty jsou pak start). Hodně staré prohlížeče (IE8+) podporují místo objektu pouze boolean, kde TRUE je zarovnání nahoru a FALSE zarovnání dolů (výchozí je TRUE).

el.scrollIntoView({
    behavior: smooth,
    block: start,   //nahoru (tedy k první řádce)
    inline: center  //na střed v řádce
}
//Jednoduché volání (IE8+)
e.scrollIntoView(true); //zarovnání nahoru

Feature detect

Pro ověření, zda prohlížeč podporuje přichytávání, můžete použít CSS definici @supports() nebo JS funkci CSS.supports(). Pozor ale na to, že vlastnost scroll-snap-type byla nadefinována již v první verzi, kterou podporují starší prohlížeče. Pro ověření nové verze je potřeba použít vlastnost scroll-snap-align:

@supports (scroll-snap-align: start) {
    /* ... definice přichytávání */
}
function scrollIntoView(el) {
    if ('CSS' in window &&
        CSS.supports('scroll-snap-align',
                            'start')) {
        //scroll pomocí bodů zachycení
        el.scrollIntoView();
    }
    else { //normální scroll na pozici prvku
        window.scrollTo(0, el.offsetTop);
    }
}

//volání vytvořené funkce
window.scrollIntoView(
        document.getElementById('example')
);

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *