Lightbox
W poniższym tekście zajmiemy się stworzeniem Lightboxa, czyli mechanizmu dość często stosowanego do pokazywania zdjęć na stronach.
Takich rozwiązań jest na necie dziesiątki, a najpopularniejszymi są Lightbox 2 czy Fancybox. W realnych projektach sięgał bym raczej po nie (czy po inne pasujące). Wynika to z faktu, że są one przetestowane przez setki użytkowników, a i dodanie ich do projektu to zaledwie kilka sekund.
Nie znaczy to jednak, że nie warto nauczyć się tworzyć coś "customowego", a przy okazji nauczyć czegoś nowego.
HTML i CSS
Robiąc na stronie galerie zdjęć, które po kliknięciu powinny pokazywać duże zdjęcia powinniśmy ją zrobić w formie serii linków zawierających miniaturki. Dzięki temu nawet jeżeli użytkownikowi nie działał Javascript, spokojnie będzie mógł przejść do podglądu dużego zdjęcia.
<a class="gallery-el" href="big1.png" data-text="lorem ipsum sit dolor">
<img src="mini1.png" alt="">
</a>
<a class="gallery-el" href="big2.png" data-text="lorem ipsum sit dolor">
<img src="mini2.png" alt="">
</a>
<a class="gallery-el" href="big3.png" data-text="lorem ipsum sit dolor">
<img src="mini3.png" alt="">
</a>Stwórz sobie html z powyższą galerią, dodaj plik z poniższymi stylami i wrzuć na chwilę poniższy kod lightboxa byś miał pewność, że z wyglądem nie będzie problemu.
Zaczynamy
Tworząc Slider wykorzystaliśmy składnię Class. Język Javascript jest na tyle plastyczny, że dane problemy możemy rozwiązywać na różne sposoby. Zamiast klasy i zabawy obiektami, nasz lightbox napiszemy za pomocą samych funkcji. Dzięki temu nie użyjemy ani razu this, co dla niektórych będzie zbawieniem.
Wszystkie elementy z jakich będzie składać się lightbox będziemy trzymać w obiekcie DOM. Dzięki temu o wiele łatwiej będzie nam się do nich odwoływać (szczególnie jak masz porządny edytor, który będzie ci podpowiadał), ale i nie pomylimy zmiennych przetrzymujących elementy na stronie z innymi zmiennymi, które np. trzymają aktualny licznik. Moglibyśmy też po prostu użyć odpowiednich przedrostków np. elLightbox, elButton, ale jakoś tak to brzmi po Meksykańsku...
Lightbox będzie składał się z kilku elementów, które za chwilę obsłużymy. Musimy więc mieć do nich referencję. Możemy je tworzyć za pomocą odpowiednich metod (np. createElement()), ale możemy też wrzucić cały kod jako innerHTML, a następnie wybrać odpowiednie elementy za pomocą querySelector(). Dzięki temu potencjalne poprawki będą o wiele łatwiejsze.
Stwórzmy więc dwie metody - jedna zwróci kod całego lightboxa, a druga wybierze odpowiednie elementy:
Do pobranych elementów podepnijmy odpowiednie zdarzenia:
Doszły nam dodatkowe funkcje: prevImage(), nextImage() i hideLightbox(). Dodajmy je do naszego kodu:
Metody prevImage() i nextImage() działają bardzo podobnie do tych, które pisaliśmy w Sliderze. Różnią się w zasadzie tym, że dodatkowo pobieramy z danego elementu (linka który kieruje do dużej grafiki) tekst, który trzymany jest w data-text (patrz pierwszy listing):
Funkcja showCount() będzie służyć do pokazywania tekstu w elemencie count:
Dodawanie i usuwanie zdjęć
Zmieniając aktualnie wyświetlane zdjęcie, zmieniamy currentIndex w tablicy links. Napiszmy dwie funkcje, które będą służyły do dodawania i usuwania do niej linków prowadzących do dużych zdjęć:
Funkcja przyjmie jakiś selektor, który będzie służył do pobrania elementów (linków prowadzących do dużych zdjęć) ze strony. Takich elementów może być naście (w końcu to galeria). Robimy więc po nich pętlę, sprawdzając czy dane elementy są linkami i mają atrybut href (inaczej nie ma sensu ich dodawać). Ewentualnie moglibyśmy tutaj jeszcze sprawdzić czy rzeczywiście prowadzą do grafik:
Jeżeli to jest link i gdzieś kieruje, pobieram jego href i text. Link taki zostanie dodany to tablicy linków, po której będziemy się przemieszczać w funkcjach prevImage() i nextImage(). Gdy użytkownik kliknie na taką miniaturkę na stronie, my pokażemy mu lightboxa, ale też musimy wiedzieć, które zdjęcie właśnie wyświetlamy z tablicy linków (chociażby do tego by pokazać tą informację na liczniku i wiedzieć kiedy przeskoczyć do początkowego zdjęcia). Posłuży do tego funkcja getCurrentIndex(), którą zapiszemy za chwilę.
Po wykryciu indeksu pokazujemy lightboxa i wyświetlamy zdjęcie.
Kolejna w kolejce będzie funkcja usuwająca zdjęcia z tablicy links. Robimy więc pętlę po przekazanych do usunięcia elementach, sprawdzamy czy dany element jest linkiem, a następnie odfiltrowujemy tablicę links z pominięciem danego elementu:
Programista może wywołać funkcję addLinks() przekazując do niej tylko jeden element. Podobnie po usunięciu elementów za pomocą removeLinks() może zostać w tablicy tylko jeden (albo nic) elementów. W takim przypadku pokazywanie licznika i przycisków poprzedni/następny mija się z celem. Poprawmy więc powyższe funkcje:
I dodajmy brakujące funkcje:
Obsługa wczytywania grafiki
Gdy wywołujemy funkcję showImage() ustawiamy dla zdjęcia po prostu nowe src. Zdjęcie może się wczytywać jakiś czas, dlatego fajnie by było, gdyby użytkownik zobaczył jakąś mini ikonkę wczytywania (takie mikro interakcje są bardzo ważne).
Dodajmy dwie funkcje, które będą tworzyć i usuwać ikonkę wczytywania:
A następnie podepnijmy je dla zdjęcia. Zrobimy to wewnątrz funkcji, w której podpinaliśmy i inne elementy:
Obsługa błędów
Nie zawsze zdjęcie musi się wczytać. Tak samo jak pokazywaliśmy ikonkę wczytywania, w razie konieczności pokażmy element z komunikatem błędu:
Ładniejsze pokazywanie lightboxa
W tej chwili nasz lightbox pojawia się i znika "na chama", ponieważ jest wstawiany i usuwany z dokumentu. Ale my chcemy mieć doskonały lightbox, który będzie pojawiał się płynnie. Poprawmy to lekko przerabiając funkcje showLightbox() i hideLightbox() dodając do nich animację korzystając z api animate:
Opcje
Dodajmy też dodatkowe opcje, które pozwolą nam w przyszłości sterować działaniem naszego lightboxa.
Wywołanie lightboxa
I na tym zakończymy. Aby teraz użyć naszego lightboxa, skorzystamy z kodu:
Miniaturki
Skoro nam tak dobrze idzie, pokuśmy się o dodanie miniaturek pod lightboxem. Jak to zrobić?
Po pierwsze podczas generowania naszego lightboxa musimy dodać odpowiednie elementy do niego:
Dodaliśmy dwa elementy .lightbox-thumbnails i .lightbox-thumbnails-list, ponieważ potrzebujemy ich do stylowania by wycentrować miniaturki w poziomie.
Element .lightbox-thumbnails będziemy pokazywać lub ukrywać, natomiast do .lightbox-thumbnails-list wstawimy miniaturki.
Dodajmy więc funkcję generującą odpowiedni kod i użyjmy jej przy dodawaniu lub odejmowaniu linków:
Dodajmy też funkcje do pokazywania i krywania miniaturek - tak samo jak to zrobiliśmy dla licznika i przycisków poprzedni/następny:
Zauważyłeś, że poza pokazywaniem/ukrywaniem miniaturek dodajemy do lightboxa dodatkową klasę? Wynika ona z jednego z dziwnych zachowań CSS. Jeżeli wstawisz img (nasz duży obrazek) do jakiegoś elementu, to ciężko jest wymusić na tym obrazku by miał max-height: 100%. Żadne flexy, gridy tutaj nie działają (przynajmniej ja nie znalazłem na to sposobu). Pozostaje więc dać mu maksymalną wysokość korzystając z innej jednostki.
Dodajmy do stylowania dodatkowe style:
Aktywna miniaturka
Z pewnością będziesz chciał wskazać użytkownikowi, które zdjęcie aktualnie pokazujesz.
Zmodyfikujmy nieco nasze stylowanie dodając klasę .is-active dla aktywnej miniaturki:
Napiszmy też funkcję, która doda tą klasę do odpowiedniego elementu:
A następnie podepnijmy klikanie na miniaturki:
Bonus: fix dla mobilek
Przetestowałem nasze dzieło na różnych przeglądarkach na swoim leciwym telefonie. Nie zaskoczyło mnie to, że klasycznie pojawił się problem z "pełną wysokością ekranu", która na urządzeniach mobilnych bardzo często sprawia problemy (nie bierze pod uwagę pasków np. z narzędziami). I tak na mobilnym Chromie czy Firefoxie nasze dzieło działało całkiem dobrze, natomiast mobilne Vivaldi (super przeglądarka!) pozwala dodać na dole ekranu dodatkowy pasek narzędzi, który przykrywa nasze miniaturki. Jest to spowodowane tym, że 100vh na urządzeniach mobilnych nie uwzględnia pasków, więc obszar jest nieco większy. Teoretycznie jest na to wartość -webkit-fill-available, ale nie znalazłem przykładu jak jej użyć z calc(). Pozostaje więc wykorzystać Javascript, którym pobierzemy aktualną wysokość ekranu a następnie dokonamy odpowiednich wyliczeń.
Stwórzmy do tego odpowiednią funkcję i jej użyjmy:
Last updated