?

Cześć!

(naciśnij spację, żeby kontynuować)

Dzisiaj nauczymy się, jak za pomocą języka JavaScript stworzyć interaktywną galerię obrazków.

(kliknij, żeby zobaczyć demo)

Struktura i wygląd galerii (HTML i CSS) są już gotowe.

(Kliknij tutaj, żeby zobaczyć)

Naszym zadaniem będzie ożywić ją dodając kod JavaScript.

Swoją drogą, przyjemne te slajdy, prawda? :-) Jeżeli zgubisz się gdziekolwiek w trakcie, naciśnij ESC żeby zobaczyć podgląd całości. Czy wiecie że wszystkie animacje i przejścia między nimi są zakodowane właśnie w JavaScript?

Na zielono pojawią się pytania do przedyskutowania w grupach.

Na niebiesko oznaczone są ważne informacje, które przydadzą nam się przy rozwiązywaniu zadań.

Na fioletowo oznaczone są zadania do wspólnego wykonania.

Nasz(a) instruktor(ka) będzie cały czas nad nami czuwać i chętnie odpowie na wszystkie pytania.

Klikając znak zapytania w prawym górnym rogu znajdziemy też ściągawkę ze wszystkimi pojęciami użytymi w tym kursie.

Gotowe? W takim razie zaczynamy!

Środowisko pracy

  • Codesandbox.io - do pisania naszego kodu. Ta platforma służy także do śledzenia na bieżąco postępów pracy. Do jej uruchomienia potrzebujesz tylko...
  • przeglądarki internetowej - na pewno masz swoją ulubioną. Jeżeli nie masz, polecamy Chrome. Ale w każdej, nowoczesnej przeglądarce są...
  • narzędzia programisty - bez nich ani rusz! Naciśnij F12 (Ctrl + Option + I na Macbookach), żeby je otworzyć.

Źródła

W trakcie warsztatów będziesz pracowała na kopii szablonu z Codesandbox.io

Po sklonowaniu szablonu wejdź do sandboxa. Znajdziesz w głównym folderze plik index.html. Jest to plik, na którym pracujemy.

Aby wiedzieć, do czego zmierzamy, przygotowaliśmy demo efektu końcowego.

Otwórzmy w przeglądarce plik demo.html.

Na efekt końcowy składają się trzy warstwy:

Struktura dokumentu

Jakie elementy są widoczne na stronie?

Obrazki, tekst, akapity, nagłówki, ikonki, itp.
- za to wszystko odpowiedzialny jest HTML.

Prezentacja

Jak wyglądają poszczególne elementy?

Rozmiar, kształt, kolor, rodzaj czcionki, pozycja względem innych elementów - na te pytania odpowiada CSS.

Interakcja z użytkownikiem

Co się dzieje na stronie?

Jakie możliwości ma użytkownik? Co się wtedy stanie? - Do tego potrzebny nam JavaScript - i tym też się za chwilę zajmiemy.

Teraz przełączmy się w przeglądarce na plik index.html.

Z jakich elementów składa się nasza galeria? Skąd możemy wiedzieć jak są zbudowane?

Narzędzia programisty!

Prawie każda przeglądarka internetowa ma w sobie bardzo zaawansowane narzędzia, które pomogą nam zrozumieć z czego składa się strona i w jaki sposób ona działa.

Opcja zbadaj pozwala nam podejrzeć szczegóły.

Podświetlenie wybranego elementu

Klasy, style, i możliwość ich zmiany

Konsola JavaScript


Czas na eksperymenty! Korzystając z tych narzędzi, spróbujmy zabrać klasę show z elementu który ma ją obecnie i dodać ją do innego obrazka.

Udało się? Klasa show dopisuje dodatkowe style CSS, dzięki czemu element, który ma tę klasę, pokazuje się na wierzchu pozostałych.

Jeżeli zastanawiasz się, jak to jest zrobione, nasz(a) instruktor(ka) chętnie wytłumaczy to dokładniej.

Naszym zadaniem będzie znalezienie sposobu, żeby przełączać tę klasę przy użyciu kodu i dzięki temu automatycznie zmieniać slajdy.

Skąd komputer wie, co ma robić?

Zapiszmy po polsku instrukcje potrzebne do pokazania slajdu nr 5.

Pewnie wygląda to mniej więcej tak:

1. Znajdź slajd nr 5 w dokumencie.
2. Dodaj do niego klasę `show`.

Do szukania elementów w dokumencie przyda nam się obiekt document.

Otwórz narzędzia programisty, wpisz w konsoli słowo document i sprawdź, co się stanie.

Obiekt document zawiera całą strukturę pliku HTML. Dzięki niemu możemy znaleźć dowolny element dokumentu i zmienić go przy pomocy kodu.

Dla ciekawych jak to działa - lektura na potem: What is the DOM? - on CSS Tricks

Obiekt document ma wiele metod do operowania na elementach strony. W tym momencie poznamy jedną z nich:

querySelector( warunek ) - wybiera pierwszy element, które spełnia dany warunek.

Jeżeli znasz CSS, to świetnie. Selektory w tym przypadku działają dokładnie tak samo, na przykład:

// wybiera pierwszy nagłówek 3 stopnia
document.querySelector( 'h3' )
// wybiera pierwszy artykuł
document.querySelector( 'article' )
// wybiera pierwszy element z klasą moja-klasa
document.querySelector( '.moja-klasa' )
// wybiera element z ID artykul-1
document.querySelector( '#artykul-1' ) 

Jeżeli nie znasz CSS, nic straconego. W narzędziach programisty pod prawym przyciskiem myszy znajdziesz opcję Kopiuj > Kopiuj selektor.

Otwórzmy konsolę i na podstawie przykładów z poprzedniego slajdu napiszmy kod, który znajdzie nam slajd nr 5.

document.querySelector( '#slide5' )

To, co dostałyśmy z powrotem to element drzewa dokumentu. Chcemy teraz znaleźć metody, które pozwolą na dodanie lub zabranie mu klasy CSS.

W tym celu pomogą nam:

  • classList.add( nazwa-klasy ) - dodaje klasę o podanej nazwie,
  • classList.remove( nazwa-klasy ) - zabiera daną klasę z elementu.

Złóżmy w konsoli kod potrzebny do pokazania slajdu 5 - szukamy go przy użyciu querySelector, a potem wołamy na nim metodę classList.add z nazwą klasy (w tym przypadku show). Pilnujemy, żeby zgadzała nam się wielkość liter i kończymy polecenie średnikiem.

Gotowe?

document.querySelector( '#slide5' ).classList.add( 'show' );

Działa? Teraz zróbmy to samo dla slajdu 4.

document.querySelector( '#slide4' ).classList.add( 'show' );

Kod dla slajdu 4 nie zadziałał, chociaż jest zbudowany praktycznie tak samo jak poprzedni. Jakieś pomysły dlaczego tak się stało?

Musimy schować slajd 5, który przykrywa nam pozostałe.

Napiszmy w konsoli kod, który to zrobi.

document.querySelector( '#slide5' ).classList.remove( 'show' );

Super! Skopiujmy wszystkie trzy fragmenty do naszego pliku gallery.js. Przydadzą nam się niedługo przy programowaniu nawigacji.

 

Na dole mamy 5 białych guziczków. Oprogramujemy każdy z nich tak, żeby pokazywał odpowiedni slajd.

Możemy zerknąć na demo.html, żeby zobaczyć jak to powinno wyglądać.

Zastanówmy się i zapiszmy po polsku, jak zapisać następujący algorytm: gdy ktoś kliknie myszą guzik nr 5, pokaż slajd o tym numerze.

Gotowe?

Wyszło nam pewnie coś takiego:

1. Znajdź piąty guziczek.
2. Sprawdzaj, kiedy zostanie kliknięty.
3. Kiedy jest kliknięty wykonaj akcję:
	3.1. Schowaj aktualnie widoczny slajd.
	3.2. Pokaż slajd piąty.

Tym razem zaczniemy od końca. Ostatnie dwie instrukcje powinny wykonywać się razem, więc potrzebujemy znaleźć jakiś sposób, żeby je ze sobą powiązać.

Do tego potrzebne nam będą funkcje.

Funkcja to ciąg kilku poleceń, które tworzą logiczną całość i powinny wykonywać się razem jedno po drugim.

W JavaScript możemy budować funkcje na kilka różnych sposobów, z czego najprostszy to:

function nazwafunkcji( ) {
   /* treść funkcji */
}

W pliku gallery.js stwórz funkcję o nazwie showSlide5. W jej treści dodaj kod, który pokazuje piąty slajd (tak, jak zrobiłaś to na poprzednim etapie).

function showSlide5( ) {
  document.querySelector( '#slide5' ).classList.add( 'show' );
}

Mamy naszą pierwszą funkcję! Teraz chcemy powiązać ją z guziczkiem nr 5, tak żeby wykonywała się za każdym razem, kiedy ktoś go kliknie.

Pamiętacie, jak udało nam się znaleźć konkretny slajd na poprzednim etapie? Jak możemy zrobić teraz to samo z guziczkiem?

document.querySelector( '#pin5' )

Teraz potrzebujemy jakiejś metody, która pozwoli nam powiązać naszą funkcję z kliknięciem w ten element.

Wszystkie elementy dokumentu mają taką metodę i jest to:

  • addEventListener( wydarzenie, akcja-do-wykonania ) - wykonuje podaną akcję, gdy z elementem stanie się coś konkretnego.

Wydarzeniem może być wszystko, co dzieje się na stronie: to, co robi z nią użytkownik (kliknięcie, przesunięcie myszy nad elementem, wciśnięcie klawisza, zmaksymalizowanie okna, scroll, itp), a także rzeczy, które dzieją się automatycznie, np. załadowanie strony.

W tym przypadku potrzebny nam będzie typ wydarzenia 'click', który odpowiada za kliknięcie w dany element.

W pliku gallery.js masz już kod wyszukujący guziczek nr 5. Przy użyciu metody addEventListener powiąż kliknięcie na tym guziczku (wydarzenie 'click') z funkcją showSlide5 napisaną wcześniej.

document.querySelector( '#pin5' ).addEventListener( 'click', showSlide5 );

Co teraz? Odświeżamy stronę i patrzymy, czy działa ;-)

Mamy już kod, który pokazuje slajd 5. Teraz zanim go pokażemy, przydałoby się znaleźć aktualny slajd i go schować.

Jak możemy znaleźć aktualny slajd? Czy jest coś, co wyróżnia go od pozostałych?

Aktualny slajd, w odróżnieniu od pozostałych, ma dodaną klasę show. Dodając kropkę przed nazwą klasy dostaniemy selektor, który pozwala po niej wyszukiwać. Korzystając z tej wiedzy i poprzednich przykładów, dodaj na początku funkcji showSlide5 kod, który znajdzie aktualny slajd i go schowa.

Po schowaniu aktualnego slajdu całość przedstawi nam się następująco:

function showSlide5( ) {
   document.querySelector( '.show' ).classList.remove( 'show' );
   document.querySelector( '#slide5' ).classList.add( 'show' );
}
document.querySelector( '#pin5' ).addEventListener( 'click', showSlide5 );

Działa? Teraz możemy na tej podstawie stworzyć kod dla pozostałych slajdów i przycisków. Najprostszym sposobem będzie metoda kopiuj-wklej-zmień co trzeba :)

Czas na testy!

Co potrzebujemy sprawdzić, żeby mieć pewność, że wszystko jest OK?

Nawigacja poprzedni / następny

Następnym krokiem będzie zaprogramowanie strzałek po prawej i lewej, które przeniosą nas do następnego / poprzedniego obrazka.

Tradycyjnie, zacznijmy od zapisania po polsku, co potrzebujemy zrobić, żeby po kliknięciu przycisku >> pokazał się następny slajd w kolejności.

Jest kilka sposobów, na jaki można to zapisać. Tutaj mamy przykładowy:

1. Znajdź prawą strzałkę,
2. Sprawdzaj, kiedy zostanie kliknięta.
3. Kiedy zostanie kliknięta wykonaj zdarzenie:
	3.1. Znajdź slajd, który aktualnie ma przypisaną klasę `show`,
	3.2. Zabierz mu tę klasę,
	3.3. Znajdź element następny w kolejności,
	3.4. Dodaj mu klasę `show`.

Jeżeli wasz sposób różni się bardzo od przykładowego, instruktor(ka) chętnie podpowie co dalej.

Sporo z tych lub podobnych instrukcji mamy już omówione:

  • 1. Znajdź prawą strzałkę,
  • 2. Sprawdzaj, kiedy zostanie kliknięta.
  • 3. Kiedy zostanie kliknięta wykonaj zdarzenie:
    • 3.1. Znajdź slajd, który aktualnie ma przypisaną klasę show,
    • 3.2. Zabierz mu tę klasę,
    • 3.3. Znajdź element następny w kolejności,
    • 3.4. Dodaj mu klasę show.

Masz wątpliwości, jak zrobić którąś z podświetlonych rzeczy? Teraz jest dobry moment, żeby to wyjaśnić.

Korzystając z tego, co już wiemy, w pliku gallery.js stwórz funkcję showNextSlide i dodaj w niej ukrywanie aktualnego elementu. Powiąż tę funkcję z kliknięciem na prawej strzałce.

Przykład rozwiązania:

function showNextSlide( ) {
   document.querySelector( '.show' ).classList.remove( 'show' );
   // tutaj będzie reszta naszego kodu
}
document.querySelector( '#next' ).addEventListener( 'click', showNextSlide );

Sprawdźmy, jak to działa!

Najtrudniejsze na tym etapie jest znalezienie następnego elementu w kolejności. Jakie mamy pomysły, na to jak to zrobić?

Potrzebujemy zapamiętać gdzieś, jaki slajd był ostatnio wyświetlany i na tej podstawie obliczyć, który będzie następny.

W tym celu potrzebne nam będą zmienne.

Zmienna jest to schowek, w którym możemy zapisywać informacje i odczytywać je później w innym miejscu. W JavaScript w każdej zmiennej można przechowywać praktycznie wszystko - tekst, liczbę, element dokumentu, złożony obiekt, a nawet funkcję.

Żeby stworzyć zmienną w JavaScript używamy słowa kluczowego let:

//zmienna tekstowa
let courseTitle = 'Galeria JavaScript';

Tak stworzonej zmiennej możemy potem używać dalej w kodzie po prostu podając jej nazwę, na przykład:

//zmienna liczbowa - długość tekstu
let titleLength = courseTitle.length;

Jak już wspomnieliśmy, w zmiennej można przechowywać wszystko. Zastanówmy się, w jakiej formie będzie najłatwiej zapamiętać aktualny slajd, żeby potem na jego podstawie obliczyć kolejny.

Najłatwiej będzie nam zapamiętać numer aktualnego elementu. To pozwoli nam wyznaczyć następny / poprzedni element poprzez dodawanie i odejmowanie.

Spróbujmy doprecyzować nasz algorytm dla prawej strzałki, wiedząc, że będziemy odczytywać i zapisywać numer aktualnego elementu.

Wygląda to pewnie tak:

1. Znajdź prawą strzałkę,
2. Sprawdzaj, kiedy zostanie kliknięta.
3. Kiedy zostanie kliknięta wykonaj zdarzenie:
	3.1. Znajdź slajd, który aktualnie ma przypisaną klasę `show`,
	3.2. Zabierz mu tę klasę, // dotąd bez zmian
	3.3. Odczytaj numer aktualnego elementu zapisany w zmiennej,
	3.4. Dodaj do niego `1`,
	3.5. Znajdź element o nowym numerze,
	3.6. Dodaj mu klasę `show`.
	3.7. Zapisz nowy numer jako aktualny element.

Wszystko jasne? Teraz jest dobry czas na pytania przed ruszeniem dalej.

Skąd możemy odczytać aktualny numer na etapie 3.3?

Potrzebujemy zapisać go wcześniej poza funkcją, najlepiej na początku pliku.

Jaką wartość chcemy mu przypisać na początku?

Po załadowaniu strony pojawia się pierwszy slajd, więc na początku aktualny numer powinien mieć wartość 1.

Skoro już wiemy, co mamy robić, przełóżmy to teraz na praktykę. Na początku pliku gallery.js stwórzmy zmienną o nazwie currentNumber i przypiszmy jej wartość 1.

let currentNumber = 1;

Świetnie, mamy naszą pierwszą zmienną!

Teraz chcemy dodać do niej 1 i zapamiętać tę wartość. W tym celu możemy stworzyć sobie dodatkową zmienną do pomocy.

Na końcu funkcji showNextSlide stwórzmy nową zmienną o nazwie newNumber, i przypiszmy jej wartość o 1 większą niż ma currentNumber.

let newNumber = currentNumber + 1;

Bardzo dobrze. Teraz skoro mamy już obliczony nowy numer, potrzebujemy znaleźć element o tym numerze.

Wszystkie nasze slajdy mają ID w postaci #slide + kolejny numer (1, 2, 3, ...).

Dzięki temu możemy wykorzystać możliwości JavaScript i skleić tekst z liczbą używając tego samego znaku +, na przykład:

'#slide' + 1

Korzystając z tej właściwości, dopiszmy na końcu funkcji showNextSlide kod który: znajduje element o numerze newNumber i dodaje do niego klasę show.

    document.querySelector( '#slide' + newNumber ).classList.add( 'show' );

Została nam jeszcze jedna rzecz - zapisanie nowego numeru jako aktualny.

Na końcu funkcji showNextSlide zapiszmy w zmiennej currentNumber nowy numer aktualnego slajdu. (Obecnie jest on przechowywany w newNumber).

Jeżeli wszystko poszło zgodnie z planem, nasz kod powinien wyglądać mniej więcej tak:

let currentNumber = 1;

function showNextSlide( ) {
    document.querySelector( '.show' ).classList.remove( 'show' );
    let newNumber = currentNumber + 1;
    document.querySelector( '#slide' + newNumber ).classList.add( 'show' );
    currentNumber = newNumber;
}
document.querySelector( '#next' ).addEventListener( 'click', showNextSlide );

Wszystko gotowe? Czas na testy! :-)

Zauważyłyście to? Po dojechaniu do końca pojawia nam się czarny ekran.

Dlaczego tak się dzieje i jak możemy to naprawić?

Mamy tylko 5 slajdów, więc po dojechaniu do końca skrypt próbuje załadować nam slajd 6 który nie istnieje.

Żeby to naprawić, możemy sprawdzać po dodawaniu jaki mamy obecny numer. Jeżeli jest większy niż 5, to trzeba zmienić go z powrotem na 1.

Jak możemy poprawić nasz algorytm pisany po polsku, żeby to uwzględnił?

3.1. Znajdź slajd, który aktualnie ma przypisaną klasę `show`,
3.2. Zabierz mu tę klasę,
3.3. Odczytaj numer aktualnego elementu zapisany w zmiennej,
3.4. Dodaj do niego 1,
3.5. Jeżeli nowy numer jest większy od 5, to zmień go na 1.
3.6. Znajdź element o nowym numerze,
3.7. Dodaj mu klasę `show`.
3.8. Zapisz nowy numer jako aktualny element.

Żeby osiągnąć taki efekt, przydadzą nam się instrukcje warunkowe.

Instrukcje warunkowe to fragmenty kodu, które wykonują się tylko wtedy, kiedy jest spełniony dany warunek.

Najprostsza z nich wygląda tak:

if ( warunek ) {
    //wykonaj kod, który jest tutaj.
}

Do porównania, która liczba jest większa lub mniejsza używamy znaków ><.

Jeżeli coś jest niejasne, teraz jest dobry moment na pytania.

We właściwym miejscu funkcji showNextSlide dodajmy instrukcję warunkową, która: sprawdza, czy nowy numer (newNumber) jest większy od 5, i jeżeli jest, to przypisuje mu wartość 1.

Na tym etapie nasz kod wygląda tak:

let currentNumber = 1;

function showNextSlide( ) {
    document.querySelector( '.show' ).classList.remove( 'show' );
    let newNumber = currentNumber + 1;
    if( newNumber > 5 ) {
        newNumber = 1;
    }
    document.querySelector( '#slide' + newNumber ).classList.add( 'show' );
    currentNumber = newNumber;
}
document.querySelector( '#next' ).addEventListener( 'click', showNextSlide );

Czas na testy!

Jeżeli wszystko działa jak trzeba, możemy stworzyć i powiązać analogiczną funkcję dla lewej strzałki. Nazwijmy ją showPreviousSlide. Większość kodu możemy skopiować z funkcji dla prawej strzałki, musimy tylko zmienić dodawanie na odejmowanie, a także warunek dotarcia do końca. Po skończonej pracy przetestujmy, jak to działa.

Przykładowy kod funkcji showPreviousSlide:

function showPreviousSlide( ) {
    document.querySelector( '.show' ).classList.remove( 'show' );
    let newNumber = currentNumber - 1;
    if( newNumber < 1 ) {
        newNumber = 5;
    }
    document.querySelector( '#slide' + newNumber ).classList.add( 'show' );
    currentNumber = newNumber;
}
document.querySelector( '#prev' ).addEventListener( 'click', showPreviousSlide );

Udało się! Mamy już zaprogramowane podstawowe funkcje galerii.

Chcecie jeszcze? Jest jeszcze parę bonusów dla chętnych.

Bonus 1 - łączenie wszystkiego razem

Mamy już dwa sposoby nawigacji po galerii - guziczki na dole i strzałki. Najwyższy czas sprawdzić, jak wygląda współpraca pomiędzy nimi.

Jeżeli używamy na zmianę guziczków i strzałek, kolejność obrazków jest nie do końca w porządku. Co właściwie jest nie tak? Jakieś pomysły dlaczego tak się dzieje?

Funkcje showNextSlideshowPreviousSlide odczytują z currentNumber numer aktualnego slajdu i na tej podstawie wybierają następny.

Niestety funkcje przypisane do guziczków (showSlide1 ... showSlide5) nie aktualizują tej wartości, więc nasz kod ma złe dane na temat tego, który slajd jest aktualnie na wierzchu.

Hurra! Mamy nasz pierwszy efekt uboczny ;-) Takie rzeczy często dzieją się przy dodawaniu nowych funkcjonalności do starego kodu. Żeby temu zapobiec, przydają się testy i upraszczanie kodu tak bardzo, jak tylko to możliwe.

Jak możemy naprawić nasz problem i zapobiec podobnym sytuacjom w przyszłości?

Najprostszym rozwiązaniem byłoby zapisanie wartości currentNumber we wszystkich funkcjach przypisanych do guziczków.

W ten sposób każda z funkcji showSlide1 ... showSlide5 zapisywałaby odpowiedni numer od 1 do 5.

To rozwiązanie ma niestety poważną wadę. Za każdym razem, kiedy będziemy coś zmieniać w naszym kodzie, potrzebujemy sprawdzić wszystkie istniejące funkcje i upewnić się czy są dalej aktualne. Jeżeli nie, trzeba będzie poprawiać każdą z nich z osobna.

Dużo lepszym rozwiązaniem będzie stworzenie uniwersalnej funkcji, która pokaże dowolny slajd o podanym numerze. Dzięki temu logika pokazywania slajdu będzie zawsze taka sama, a ewentualne zmiany trzeba będzie wprowadzić tylko raz.

W tym celu przydadzą nam się parametry funkcji.

Jak pewnie pamiętacie, funkcja to spójny ciąg poleceń, który można wywołać z dowolnego miejsca w kodzie żeby wykonywały się jedno po drugim. Jeżeli przekażemy do funkcji parametry, te same polecenia mogą za każdym razem działać na innych danych.

Prosty przykład - funkcja, która bierze imię jako parametr i wypisuje w konsoli pozdrowienia dla osoby o danym imieniu:

function hej( imie ) {
    console.log( 'Hej ' + imie + '! Miło Cię widzieć!' );
}

Teraz możemy wywołać tę funkcję, na przykład:

hej( 'Magda' );

Co się stanie jeżeli wywołamy funkcję hej w ten sposób?

Koniec dygresji, wracamy do naszego pokazu slajdów :-)

Na podstawie funkcji które mamy już napisane, zastanówmy się, jakie są części wspólne? Jak możemy zapisać po polsku, co uniwersalna funkcja powinna robić?

1. Dla podanego numeru slajdu
1.1. Znajdź aktualnie pokazywany element,
1.2. Zabierz mu klasę `show`,
1.3. Znajdź element o podanym numerze,
1.4. Dodaj mu klasę `show`,
1.5. Zapisz podany numer jako aktualny numer slajdu

Jeżeli masz jakieś wątpliwości, teraz jest dobry moment na pytania.

W pliku gallery.js dodaj nową funkcję showSlide, która przyjmuje numer slajdu do pokazania (newNumber) jako parametr. W treści funkcji umieść wszystkie potrzebne instrukcje: chowanie aktualnego slajdu, znalezienie slajdu o nowym numerze i pokazanie go, zapisanie aktualnego numeru.

Przykładowy kod, jak można to zrobić:

function showSlide( newNumber ) {
    document.querySelector( '.show' ).classList.remove( 'show' );
    document.querySelector( '#slide' + newNumber ).classList.add( 'show' );
    currentNumber = newNumber;
}

Co dalej? Potrzebujemy zmodyfikować funkcje które już mamy tak, żeby korzystały z tej uniwersalnej.

W pliku gallery.js znajdź funkcje showSlide1 do showSlide5. Zastąp ich obecną treść wywołaniem uniwersalnej funkcji showSlide z odpowiednim parametrem.

Kod dla guziczka nr 1 - pozostałe wyglądają analogicznie:

function showSlide1( ) {
  showSlide( 1 );
}
document.querySelector( '#pin1' ).addEventListener( 'click', showSlide1 );

Teraz zróbmy to samo dla funkcji operujących na strzałkach.

W pliku gallery.js znajdź funkcje showPreviousSlideshowNextSlide. Usuń z nich wszystko, co znajduje się w uniwersalnej funkcji showSlide i zamiast tego wywołaj tę funkcję z odpowiednim parametrem.

Kod dla strzałki w prawo po zmianach:

function showNextSlide( ) {
  let newNumber = currentNumber + 1;
  if( newNumber > 5 ) {
    newNumber = 1;
  }
  showSlide( newNumber );
}
document.querySelector( '#next' ).addEventListener( 'click', showNextSlide );

Wszystko jasne? Czas na testy!

Dobra robota!

I dobra wiadomość - bonusów jest jeszcze więcej!

Bonus 2 - podświetlanie guziczków

Następną fajną rzeczą, którą warto by było zrobić jest podświetlanie guziczka, który odpowiada aktualnemu obrazkowi.

Możemy zobaczyć jak to wygląda na naszym demo.

W jaki sposób aktualny guziczek podświetla się na stronie demo? Co podpowiadają nam narzędzia programisty?

Widzimy, że aktualnie podświetlany guziczek ma dodatkowo dodaną klasę selected. Wzorując się na funkcji showSlide, dodajmy w pliku gallery.js nową funkcję selectPin, która przyjmuje numer guziczka do podświetlenia jako parametr, resetuje aktualnie podświetlony guziczek i podświetla ten o podanym numerze.

Zrobione? Tutaj mamy przykład jak taka funkcja może wyglądać:

function selectPin( newNumber ) {
  document.querySelector( '.selected' ).classList.remove( 'selected' );
  document.querySelector( '#pin' + newNumber ).classList.add( 'selected' );
}

Gdzie najlepiej będzie tę funkcję wywołać? Dlaczego tym razem nie zapisujemy wartości currentNumber?

Chcemy podświetlać guziczek za każdym razem, kiedy przełączamy slajdy, więc najlepiej będzie wywoływać tę funkcję wewnątrz funkcji showSlide.

Numer podświetlonego guziczka jest zawsze taki sam jak numer pokazywanego slajdu, dlatego nie musimy zapamiętywać tej samej wartości drugi raz.

Wewnątrz funkcji showSlide dodaj podświetlanie guziczka - wywołaj funkcję selectPin z odpowiednim parametrem.

Nasza funkcja showSlide ma teraz postać:

function showSlide( newNumber ) {
  document.querySelector( '.show' ).classList.remove( 'show' );
  document.querySelector( '#slide' + newNumber ).classList.add( 'show' );
  currentNumber = newNumber;
  selectPin( newNumber );
}

Tradycyjnie - testujemy! :-)

Ooops, coś poszło nie tak! Jakieś pomysły, co się stało i jak to możemy naprawić?

Pomoże nam w tym konsola przeglądarki. Klikając nazwę pliku w komunikacie o błędzie możemy zobaczyć, który dokładnie fragment naszego kodu powoduje problemy:

Widzimy, że nasz skrypt ma problemy z właściwością classList, bo obiekt z którego próbuje to odczytać po prostu nie istnieje.

Dlaczego tak się stało? Przy uruchomieniu strony żaden z guziczków nie ma klasy selected, więc wyszukiwanie po tej klasie zwróciło pusty zbiór.

Jak możemy rozwiązać ten problem?

Upewnijmy się, że przy załadowaniu strony mamy prawidłowo pokazany pierwszy slajd i podświetlony pierwszy guziczek. W tym celu dodajmy kod, który to zrobi w okolicach początku pliku gallery.js.

Tutaj mamy przykład jak to rozwiązać:

let currentNumber = 1;
document.querySelector( '#slide' + currentNumber ).classList.add( 'show' );
document.querySelector( '#pin' + currentNumber ).classList.add( 'selected' );

Jak tym razem wyszły testy?

Jeżeli nie macie jeszcze dosyć, to zapraszamy po przerwie :-)

Bonus 3 - Pętle

Bonus 3 - Pętle

Bonus 3 - Pętle

Bonus 3 - Pętle

Co zrobić, żeby się nie powtarzać bez sensu?

W naszym kodzie znajduje się teraz pięć, bardzo podobnych do siebie kawałków kodu

function showSlide1 ( ) {
     showSlide( 1 );
}
document.querySelector('#pin1').addEventListener( 'click', showSlide1 );
function showSlide2 ( ) {
     showSlide( 2 );
}
document.querySelector('#pin2').addEventListener( 'click', showSlide2 );

...

Wyobraźmy sobie, jak by nasz kod wyglądał, gdyby nasza galeria miała więcej, niż pięć zdjęć

function showSlide312 ( ) {
   showSlide( 312 );
}
document.querySelector('#pin312').addEventListener( 'click', showSlide312 );

Aż prosi się o to, żeby jakoś zautomatyzować ten proces.

W końcu do tego są komputery, żeby nam ułatwiać życie! ;-)

Wyobraźmy sobie coś takiego:

podstawiając pod zmienną numerPinu liczby od 1 do 5 chcę wykonać poniższe {
  document
    .querySelector('#pin' + numerPinu)
    .addEventListener( 'click', showSlide(numerPinu) );
}

W JavaSkrypcie możemy połamać długie instrukcje na kilka linijek dla lepszej czytelności.

Jak się okazuje, jest to bardzo proste!

Posłużymy się do tego pętlą for:

for (let numerPinu = 1; numerPinu <= 5; numerPinu++) {
  document
    .querySelector('#pin' + numerPinu)
    .addEventListener( 'click', showSlide(numerPinu) );
}

Sprawdźmy to!

Chyba coś nie tak :(

Od razu wyświetla nam się ostatnia fotografia, a piny nie działają...

Spójrzmy jeszcze raz na naszą pętlę:

for (let numerPinu = 1; numerPinu <= 5; numerPinu++) {
  document
    .querySelector('#pin' + numerPinu)
    .addEventListener( 'click', showSlide(numerPinu) );
}

Problemem jest funkcja showSlide(), która jest wywoływana w momencie gdy wykonywana jest pętla, zamiast po kliknięciu na pin

Przypomnijmy sobie jak wyglądał nasz pierwotny kod

function showSlide1 ( ) {
  showSlide( 1 );
}
document.querySelector('#pin1').addEventListener( 'click', showSlide1 );

Wywołanie funkcji showSlide było zamknięte w funkcji showSlide1, co pozwoliło nam na wywołanie jej w odpowiednim momencie (czyli po kliknięciu)

Ten kod:

function showSlide1 ( ) {
  showSlide( 1 );
}
document.querySelector('#pin1').addEventListener( 'click', showSlide1 );

Można zapisać odrobinę inaczej

document.querySelector('#pin1').addEventListener( 'click', function () {
  showSlide( 1 );
} );

Przenieśliśmy definicję funkcji bezpośrednio do miejsca, w którym chcemy ją przypisać do event listenera

Dodatkowo zabraliśmy jej nazwę. Teraz to funkcja anonimowa!

Wróćmy teraz do naszej pętli. Z nowo zdobytą wiedzą powinniśmy sobie poradzić z prawidłowym jej napisaniem

for (let numerPinu = 1; numerPinu <= 5; numerPinu++) {
  document
    .querySelector('#pin' + numerPinu)
    .addEventListener( 'click', function () {
      showSlide(numerPinu);
    });
}

No to zobaczmy, jak nam to wyszło

Piny działają i mamy (+-) pięć razy mniej kodu!

Bonus 4 - Autoplay!

Tym razem spróbujmy dodać do naszego pokazu slajdów tryb autoplay.

Tradycyjnie, możemy podejrzeć jak on działa na naszym demo, klikając przycisk w prawym dolnym rogu.

Tym razem wyjątkowo nie bierzmy pod uwagę wszystkiego, co się dzieje na stronie i zbudujmy na razie możliwie ogólny obraz sytuacji. Widzimy, że autoodtwarzanie na zmianę włącza się i wyłącza w miarę jak klikamy przycisk play kilka razy. Jak możemy zapisać na najbardziej ogólnym poziomie to co się tu dzieje?

Pewnie wyszło nam mniej więcej coś takiego:

1. Znajdź przycisk play,
2. Sprawdzaj, kiedy zostanie kliknięty.
3. Kiedy zostanie kliknięty wykonaj zdarzenie:
    3.1. Sprawdź, czy pokaz slajdów się obecnie odtwarza:
        3.1.1. Jeżeli tak, to go wyłącz,
        3.1.2. Jeżeli nie, to go włącz.

Czas zacząć budować funkcję która będzie zarządzać naszym pokazem slajdów. W jaki sposób możemy sprawdzić, czy pokaz slajdów się obecnie odtwarza?

W ten sam sposób, w jaki pamiętamy numer aktualnego slajdu - używając zmiennej.

W tym przypadku nasza zmienna będzie przyjmować tylko dwie wartości (odtwarza się / nie odtwarza się), więc najlepiej będzie przypisać jej wartość logiczną

Pamiętacie, że w JavaScript zmienna może być praktycznie wszystkim? Między innymi możemy jej przypisać wartość logiczną (prawda lub fałsz).

Prawdę zapisujemy w JavaScript jako true, dla fałszu używamy słowa false:

let isJavaScriptAwesome = true;
let isJavaScriptComplicated = false;

Wartości logiczne porównujemy używając potrójnego znaku równości, np:

if ( isJavaScriptAwesome === true ) {
    let doWeHaveEnoughYet = false;
}

Na początku pliku gallery.js stwórz zmienną playing, która będzie przechowywała aktualny stan pokazu slajdów. Na początku pokaz powinien być wyłączony, więc przypisz jej wartość fałsz.

let playing = false;

Świetnie! Teraz możemy przejść do szkieletu naszej funkcji. Przejrzyjmy szybko kod napisany na poprzednich etapach, żeby przypomnieć sobie jak powiązać funkcję z kliknięciem myszy na elemencie.

W pliku gallery.js dodaj nową funkcję playButtonClicked i powiąż ją z kliknięciem na przycisku play. W treści funkcji sprawdź obecny stan pokazu slajdów zapisany w zmiennej playing. Na razie wystarczy, że zmienisz go na przeciwny (z prawdy na fałsz, z fałszu na prawdę).

Najprawdopodobniej otrzymamy coś takiego:

function playButtonClicked( ) {
    if ( playing === true ) {
        playing = false;
    } else {
        playing = true;
    }
}
document.querySelector( '#play' ).addEventListener( 'click', playButtonClicked );

Jak możemy na tym etapie przetestować czy nasz kod działa poprawnie?

Najprościej będzie wpisać w konsoli nazwę naszej zmiennej - dostaniemy z powrotem jej aktualną wartość.

Zajmijmy się teraz samym przyciskiem play. Widzimy, że zmienia on ikonkę w zależności od tego ile razy został kliknięty. Co sprawia, że ta ikonka się zmienia?

Oczywiście jest to klasa CSS. Tym razem właściwa klasa nazywa się on.

Widzimy, że liczba rzeczy do zrobienia przy włączeniu / wyłączeniu pokazu slajdów pomału nam się zwiększa. Może by tak wydzielić z nich osobne funkcje?

W pliku gallery.js dodajmy dwie nowe funkcje: startSlideshowstopSlideshow. Funkcja start dodaje klasę on do przycisku play i zapisuje w zmiennej playing że pokaz slajdów jest włączony. Funkcja stop robi na odwrót. W funkcji playButtonClicked wywołajmy każdą z tych dwóch funkcji w odpowiednich miejscach.

Dużo nowego kodu! Sprawdźmy, jak nam poszło tym razem:

function playButtonClicked( ) {
    if ( playing === true ) {
        stopSlideshow();
    } else {
        startSlideshow();
    }
}
document.querySelector( '#play' ).addEventListener( 'click', playButtonClicked );

function startSlideshow( ) {
    document.querySelector( '#play' ).classList.add( 'on' );
    playing = true;
}

function stopSlideshow( ) {
    document.querySelector( '#play' ).classList.remove( 'on' );
    playing = false;
}

Wszystko jasne na razie? Jak tak, to testujemy.

Jeżeli wszystko działa dobrze, to przechodzimy do najtrudniejszej części - włączania i wyłączania pokazu slajdów.

Na czym właściwie polega pokaz slajdów? Jak możemy zapisać jego algorytm po polsku? Część mamy już opisaną w funkcji startSlideshow, więc pozostaje nam zastanowić się nad resztą.

Jest kilka sposobów, żeby to zapisać, ale idea jest mniej więcej ta sama:

Funkcja Włącz Pokaz Slajdów:
1. Dodaj klasę playing do przycisku play
2. Zapisz stan pokazu slajdów w zmiennej playing
3. Poczekaj 3 sekundy,
4. Przełącz slajd na następny,
5. Wróć do punktu 3.

W następnym kroku potrzebujemy jakiegoś sposobu do odmierzania czasu.

Do tego przydadzą nam się interwały.

Interwał powtarza podane mu instrukcje cyklicznie w danym mu odstępie czasu.

Można go stworzyć poprzez funkcję:

  • setInterval( akcja, odstep ) - wykonuje akcję za każdym kolejnym razem, kiedy upłynie określony odstęp czasu (podany w milisekundach).

W funkcji startSlideshow stwórzmy interwał, który co 3000 milisekund przełącza slajd na następny. Pamiętajmy, że do przełączania slajdu na następny mamy już gotową funkcję :-)

Po dopisaniu nowego kodu funkcja prezentuje nam się następująco:

function startSlideshow( ) {
    document.querySelector( '#play' ).classList.add( 'on' );
    playing = true;
    setInterval( showNextSlide, 3000 );
}

Co tym razem pokazują nam testy?

Widzimy, że pokaz slajdów uruchamia się prawidłowo. Teraz potrzebujemy jakiegoś sposobu, żeby go wyłączyć.

Jak możemy wyłączyć raz uruchomiony interwał?

To było dosyć podchwytliwe pytanie. Jak już pewnie pamiętamy, zmienne w JavaScript mogą zapisać wszystko.

Co się stanie, jeżeli zapiszemy w niej wynik działania funkcji setInterval? Możemy jej użyć potem, żeby ten interwał wyłączyć.

W okolicach początku pliku gallery.js stwórz nową zmienną o nazwie slideshowInterval. We właściwym miejscu w kodzie zapisz w tej zmiennej wynik działania funkcji setInterval.

Tym razem powinno nam wyjść mniej więcej coś takiego:

let slideshowInterval;
// (...)

function startSlideshow( ) {
    document.querySelector( '#play' ).classList.add( 'on' );
    playing = true;
    slideshowInterval = setInterval( showNextSlide, 3000 );
}

Teraz już zostaje nam tylko wyłączyć interwał.

W tym celu pomoże nam funkcja:

  • clearInterval( interwal ) - wyłącza interwał zapisany wcześniej w podanej zmiennej.

Spróbujmy to wykorzystać od razu w praktyce.

W funkcji stopSlideshow wyczyśćmy zapisany wcześniej interwał. W tym celu wywołajmy funkcję clearInterval z odpowiednim parametrem.

Całość funkcji stopSlideshow wygląda teraz tak:

function stopSlideshow( ) {
    document.querySelector( '#play' ).classList.remove( 'on' );
    playing = false;
    clearInterval( slideshowInterval );
}

Co mówią nam testy?

Gratulacje!

To już koniec naszego kursu. Mamy za sobą kawał dobrej roboty, ale to dopiero początek możliwości języka JavaScript.

Za chwilę przedstawimy wam kilka ciekawych projektów zbudowanych w tym języku.

Ale pewnie już teraz zastanawiacie się "Co dalej?".

Dobra wiadomość: Internet jest pełen darmowych materiałów do samodzielnej nauki JavaScriptu!

Kilka pomysłów gdzie zacząć:

Polecamy też zaglądać na naszą stronę na Facebooku gdzie damy znać o kolejnych kursach.

Dzięki za wspólnie spędzony czas i do zobaczenia!