Slider

W poniższym tekście postaramy się zrobić dynamiczny slider.

HTML i CSS

Zacznijmy od struktury HTML. Nasz slider będzie zwykłą listą elementów. Mogą nimi być poszczególne elementy listy, kolejne artykuły czy podobne rzeczy - po prostu będzie to zbiór jakiś elementów. W poniższym kodzie mamy zwykłą listę artykułów:


<div class="elements-list" id="slider1">

    <article class="element">
        <h2 class="element-title">
            Jestem tytułem slajdu 1
        </h2>
        <div class="element-text">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit...
        </div>
    </article>

    <article class="element">
        <h2 class="element-title">
            Jestem tytułem slajdu 2
        </h2>
        <div class="element-text">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit...
        </div>
    </article>

</div>

Powyższą strukturę za pomocą Javascript musimy zamienić na nasz slider. Będzie on miał strukturę taką jak na poniższej grafice:

Struktura html slidera

Czemu od razu nie stworzymy pełnej struktury slidera z przyciskami next/prev i paginacją? Oczywiście możemy. W ramach treningu jednak spróbujmy pójść o krok dalej. Nasz plugin będzie bardziej uniwersalny.

Masz więc listę artykułów na stronie. Chcesz z nich zrobić slider? Proszę bardzo - użyj naszego kodu. Chcesz ją zamienić na accordion? Użyj innego pluginu. A może ktoś na tą stronę wejdzie bez Javascriptu? Zobaczy początkową listę artykułów. Dodatkowo dzięki temu, że część elementów będzie generowana dynamicznie, będziemy mieli większe możliwości - bo np. nie każdy slider musi mieć paginację, albo przyciski poprzedni/następny.

Całość ostylujemy za pomocą grida oraz pozycjonowania absolutnego dla dolnego stronicowania.

Pokaż HTML

<div class="elements-list" id="slider1">
    <article class="element slider-slide-active">
        <h2 class="element-title">
            1 Lorem ipsum dolor sit.
        </h2>
        <div class="element-text">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Mollitia dolore inventore, ipsam optio. Magni repellat officia autem fuga, laboriosam facilis?
        </div>
    </article>
    <article class="element">
        <h2 class="element-title">
            2 Lorem ipsum dolor sit.
        </h2>
        <div class="element-text">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum exercitationem asperiores perspiciatis sapiente ducimus atque dicta labore dolores sequi ut.
        </div>
    </article>
    <article class="element">
        <h2 class="element-title">
            3 Lorem ipsum dolor sit.
        </h2>
        <div class="element-text">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus id quas, quia consectetur repellat quis! Minus voluptatibus, dolores iure deserunt.
        </div>
    </article>
</div>
Pokaż CSS

/* ----- slider ---- */
.slider {
    position: relative;
    background: #eee;
    background-size: cover;
    background-position: center center;
    display: grid;
    grid-template-columns: 4rem 1fr 4rem;
    grid-template-rows: 1fr;
    height: 700px;
}
.slider-slides-cnt {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
}
.slider-slide {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    opacity:0;
    z-index: 0;
}
.slider-slide-active {
    opacity: 1;
    z-index: 1;
}

.slider .element-title {
    color:#fff;
    font-family: sans-serif;
    position: relative;
    left:-3rem;
    opacity:0;
    font-size:6rem;
    text-transform: uppercase;
    font-weight: normal;
    margin-top:0;
    text-align: center;
    font-family: 'Roboto Condensed', sans-serif;
}
.slider .element-text {
    color:#fff;
    position: relative;
    left:-3rem;
    opacity:0;
    font-size:1.5rem;
    color:rgba(255,255,255,.6);
    padding-left:4rem;
    padding-right:4rem;
    text-align: center;
    font-family: serif;
    font-style: italic;
    max-width:60%;
}

/* animacja */
.slider-slide {
    opacity: 0;
    transition:0.4s all;
}
.slider-slide-active {
    opacity: 1;
}
.slider .element-title {
    left:-3rem;
    opacity:0;
    transition: 2s left, 2s opacity;
}
.slider .element-text {
    left:-3rem;
    opacity:0;
    transition: 2s 1s left, 2s 1s opacity;
}
.slider-slide-active .element-title {
    left:0;
    opacity:1;
}
.slider-slide-active .element-text {
    left:0;
    opacity:1;
}

/* przyciski poprzedni/następny */
.slider-nav {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    display: grid;
    grid-template-columns: 4rem 1fr 4rem;
    grid-template-rows: 1fr;
}
.slider-button-prev,
.slider-button-next {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    cursor: pointer;
    z-index: 2;
    background: rgba(0,0,0,0.1);
    border:0;
    text-indent:-999px;
    overflow:hidden;
    position: relative;
    z-index: 1;
}
.slider-button-next {
    grid-column: -1 / -2;
}
.slider-button-prev:focus,
.slider-button-next:focus {
    outline: none;
}
.slider-button-prev:before,
.slider-button-next:before {
    content:'<';
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    left:0;
    top:0;
    width:100%;
    height:100%;
    font-size:2rem;
    color:#fff;
    text-indent: 0;
}
.slider-button-next:before {
    content: '>';
}

/* stronicowanie */
.slider-pagination {
    z-index: 2;
    position: absolute;
    left:0;
    bottom:1rem;
    margin:0;
    padding:0;
    width:100%;
    display: flex;
    justify-content: center;
    list-style:none;
}
.slider-pagination-element {
}
.slider-pagination-button {
    background: transparent;
    width:2rem;
    height:2rem;
    border:0;
    text-indent:-999px;
    overflow: hidden;
    cursor: pointer;
    position: relative;
}
.slider-pagination-button:before {
    content:'';
    position: absolute;
    left:50%;
    top:50%;
    transform:translate(-50%, -50%);
    background: transparent;
    width:0.9rem;
    height:0.9rem;
    border-radius: 50%;
    transition:0.5s all;
    box-shadow:inset 0 0 0 2px rgba(255,255,255,0.5);
}
.slider-pagination-element-active .slider-pagination-button:before {
    background: #fff;
    transform:translate(-50%, -50%) scale(1.2);
}
.slider-pagination-button:focus {
    outline: none;
}

klasa i konstruktor Slider

Założeniem jest, że slajdów możemy mieć kilka na stronie, więc powinniśmy zrobić je pod wspólny wzór - czyli skorzystamy z konstruktora lub klas.

W poniższym tekście użyję klas. Pierwszą czynnością jest stworzenie odpowiednich zmiennych:


class Slider {
    constructor(elemSelector) {
        this.currentSlide = 0; //aktualny slide
        this.sliderSelector = elemSelector; //selektor elementu który zamienimy na slider
        this.elem = null; //tutaj pobierzemy element który zamienimy na slider
        this.slider = null; //tutaj wygenerujemy slider
        this.slides = null; //tutaj pobierzemy slajdy
        this.prev = null; //przycisk prev
        this.next = null; //przycisk next
        this.dots = []; //przyciski kropek
    }
}

Metoda generateSlider()

Pierwszą metodą jaką napiszemy to metoda zamieniająca powyższą strukturę html na odpowiednią.


class Slider {
    ...

    generateSlider() {
        //pobieramy element który zamienimy na slider
        this.slider = document.querySelector(this.sliderSelector);
        this.slider.classList.add("slider");

        //tworzymy kontener dla slajdów
        const slidesCnt = document.createElement("div");
        slidesCnt.classList.add("slider-slides-cnt");

        //pobieramy element slajdów
        this.slides = this.slider.children;

        //to jest żywa kolekcja, więc przy przeniesieniu każdego slajda
        //jej długość maleje
        while (this.slides.length) {
            this.slides[0].classList.add("slider-slide");
            //jeżeli element appendujemy do innego elementu
            //to tak jakbyśmy go usuwali z tej kolekcji
            //bo jeden element nie może być równocześnie w 2 miejscach
            slidesCnt.append(this.slides[0]);
        }

        //musimy na nowo pobrać slajdy, bo powyższa kolekcja jest już pusta
        this.slides = slidesCnt.querySelectorAll(".slider-slide");

        //wygenerowaliśmy kontener ze slajdami, wstawiamy go więc do slidera
        this.slider.append(slidesCnt);
    }
}

Po pierwsze pobieramy nasz przyszły slider.

Wszystkie slajdy muszą znaleźć się w jednym wspólnym elemencie. W naszym przypadku będzie to stworzony dynamicznie .slider-slides-cnt.

Aby przerzucić do niego elementy slidera, pobieramy je za pomocą właściwości children, a następnie robiąc po nich pętlę wrzucamy za pomocą append do powyższego elementu.

Powyższą metodę generateSlider() od razu odpalamy w konstruktorze:


class Slider {
    constructor(elemSelector) {
        this.currentSlide = 0; //aktualny slide
        this.sliderSelector = elemSelector; //selektor elementu który zamienimy na slider
        this.elem = null; //tutaj pobierzemy element który zamienimy na slider
        this.slider = null; //tutaj wygenerujemy slider
        this.slides = null; //tutaj pobierzemy slajdy
        this.prev = null; //przycisk prev
        this.next = null; //przycisk next
        this.dots = []; //przyciski kropek

        this.generateSlider();
    }

    ...
}

Metoda createPrevNext()

Kontynuujemy generowanie struktury html. Kolejną metodą będzie createPrevNext(), która stworzy dla naszego slidera przyciski "Poprzedni i następny slide":


class Slider {
    ...

    createPrevNext() {
        //generujemy przycisk "Poprzedni slajd"
        this.prev = document.createElement("button");
        this.prev.type = "button";
        this.prev.innerText = "Poprzedni slide";
        this.prev.classList.add("slider-button");
        this.prev.classList.add("slider-button-prev");
        this.prev.addEventListener("click", this.slidePrev.bind(this));

        //generujemy przycisk "Następny slajd"
        this.next = document.createElement("button");
        this.next.type = "button";
        this.next.innerText = "Następny slide";
        this.next.classList.add("slider-button");
        this.next.classList.add("slider-button-next");
        this.next.addEventListener("click", this.slideNext.bind(this));

        //wrzucamy je do wspólnego diva
        //który dam nam ciut większe możliwości stylowania
        const nav = document.createElement("div");
        nav.classList.add("slider-nav");
        nav.appendChild(this.prev);
        nav.appendChild(this.next);

        //diva z przyciskami wrzucamy do slajdu
        this.slider.appendChild(nav);
    }
}

Żeby mieć w klikniętym przycisku dostęp do właściwości metod naszego slidera, musimy je przypiąć za pomocą metod bind() lub korzystając z funkcji strzałkowej:


this.prev.addEventListener("click", this.slidePrev.bind(this));
//lub
this.prev.addEventListener("click", () => this.slidePrev());

Cała reszta kodu to stworzenie 2 przycisków i ustawienie im podstawowych właściwości.

Po kliknięciu na te przyciski odpalimy właściwości slidePrev() i slideNext(). Napiszemy je za chwilę.

Powyższą metodę odpalmy na końcu metody generateSlider:


class Slider {
    ...

    generateSlider() {
        ...

        this.createPrevNext();
    }
}

Metoda createPagination()

Kolejną czynnością jest stworzenie metody generującej kropki zmieniające slajdy:


class Slider {
    ...

    createPagination() {
        const ulDots = document.createElement("ul");
        ulDots.classList.add("slider-pagination");

        //tworzymy pętlę w ilości liczby slajdów
        for (let i=0; i<this.slides.length; i++) {
            //każdorazowo tworzymy LI wraz z buttonem
            //każdy button po kliknięciu zmieni slajd
            //za pomocą metody changeSlide()

            const li = document.createElement("li");
            li.classList.add("slider-pagination-element");

            const btn = document.createElement("button");
            btn.classList.add("slider-pagination-button");
            btn.type = "button";
            btn.innerText = i+1;
            btn.setAttribute("aria-label", `Ustaw slajd ${i+1}`);

            btn.addEventListener("click", () => this.changeSlide(i));

            li.appendChild(btn);

            ulDots.appendChild(li);

            //wygenerowany przycisk wrzucamy do wspólnej tablicy
            //dzięki temu potem łatwiej będzie nam się do tych kropek odwoływać
            this.dots.push(li);
        }

        this.slider.appendChild(ulDots);
    }
}

Tak samo jak przy metodzie createPrevNext() powyższą metodę też odpalamy w metodzie generateSlider:


class Slider {
    generateSlider() {
        ...

        this.createPrevNext();
        this.createPagination();
    }
}

Odpalmy nasz slider i zobaczmy co w tej chwili mamy:


const slide = new Slider("#slider1");
Demo 1 - wygenerowana struktura html

Metoda changeSlide()

Po kliknięciu na kropkę odpalamy metodę changeSlide(), która ma przełączyć slajd. Metoda usuwa więc wszystkim slajdom klasę .slider-slide-active i dodaje ją tylko aktualnemu. Żeby zrobić pętlę forEach po kolekcji divów, skorzystamy ze sztuczki, którą poznaliśmy tutaj:


class Slider {
    ...

    changeSlide(index) {
        //robimy pętlę po slajdach usuwając klasę active
        this.slides.forEach(slide => {
            slide.classList.remove("slider-slide-active");
            slide.setAttribute("aria-hidden", true);
        });

        //dodajemy ją tylko wybranemu
        this.slides[index].classList.add("slider-slide-active");
        this.slides[index].setAttribute("aria-hidden", false);

        //podobny manewr robimy dla kropek
        this.dots.forEach(dot => {
            dot.classList.remove("slider-pagination-element-active");
        });
        this.dots[index].classList.add("slider-pagination-element-active");

        //aktualny slide przestawiamy na wybrany
        this.currentSlide = index;
    }
}

Dodatkowo wykorzystajmy tą metodę do ustawienia początkowego slajdu odpalając ją na końcu konstruktora:


class Slider {
    constructor(elemSelector) {
        this.sliderSelector = elemSelector;
        this.currentSlide = 0;
        this.slider = null;
        this.elem = null;
        this.slides = null;

        this.prev = null;
        this.next = null;
        this.dots = [];

        this.generateSlider();
        this.changeSlide(this.currentSlide);
    }

    ...
}

Metody slidePrev() i slideNext()

Powyższa metoda zmienia slajd. Klikając na przyciski "poprzedni-następny" też będziemy zmieniać slajd, tylko nie na konkretny numer, a o jeden więcej lub mniej.

Jak spojrzysz na początkowe zmienne, mamy tam zmienną currentSlide. Określa ona aktualny slajd.

Chcemy by przyciski "poprzedni-następny" działały w pętli. Jeżeli dojdziemy do ostatniego slajdu, to kolejne przełączenie powinno przestawić slider na pierwszy slajd. I w drugą stronę - jeżeli klikniemy poprzedni będąc na pierwszym slajdzie, powinien się wyświetlić ostatni. Wykorzystajmy do tego wspomnianą zmienną:


class Slider {
    ...

    slidePrev() {
        this.currentSlide--;
        if (this.currentSlide < 0) {
            this.currentSlide = this.slides.length - 1;
        }
        this.changeSlide(this.currentSlide);
    }

    slideNext() {
        this.currentSlide++;
        if (this.currentSlide > this.slides.length - 1) {
            this.currentSlide = 0;
        }
        this.changeSlide(this.currentSlide);
    }
}

W poniższym przykładzie lekko zmieniłem za pomocą styli czcionkę i tło:

Demo 2 - działające przyciski

Automatyzacja

Do automatycznego przełączania slajdów możemy wykorzystać setTimeout(). Wystarczy za pomocą tej funkcji wywołać powyższą metodę slideNext().

W metodzie init odpalamy metodę changeSlide() - tak by po wejściu na stronę był widoczny pierwszy slajd. Po kliknięciu na przyciski poprzedni-następny też wywołujemy metodę changeSlide(). Jeżeli chcemy by po zmianie slajdu kolejna zmiana nastąpiła automatycznie, wystarczy na końcu tej metody dodać nasz setTimeout, który wywoła nam jeszcze raz metodę changeSlide():


class Slider {
    constructor() {
        ...
        this.time = null;
        ...
    }
    ...

    changeSlide(index) {
        ...

        clearInterval(this.time);
        this.time = setTimeout(() => this.slideNext(), 6000); //co 6 sekund automatycznie się przełączy
    }
}

Jeżeli ktoś będzie szybko zmieniał slajdy (klikał po przyciskach lub kropkach), każdorazowo zostanie odpalone czyszczenie aktualnie odpalonego timeOuta i każdorazowo będzie on ustawiany na nowo. Dlatego nasze automatyczne przełączenie slajdu zadziała dopiero gdy użytkownik się uspokoi.

Opcje, więcej opcji

W powyższej metodzie ustawiliśmy przełączanie slajdów co 6 sekund. Aż prosi się, by przy wywołaniu naszego slidera programista mógł ustawić takie wartości.

Zastosujemy tutaj podejście, które dokładniej opisałem tutaj. Do naszego konstruktora trafi obiekt z opcjami, które ustawi programista. Opcje te nadpiszą domyślne ustawienia, w wyniku czego dostaniemy nowy scalony obiekt z ustawieniami slidera.


const opts = {
    width : 100
}

const defaultOpts = {
    width: 10,
    height: 200
}

const options = { ...defaultOptions, ...opts }

console.log(options); //{width: 100, height:200}

lub


const opts = {
    width : 100
}

const defaultOptions = {
    width: 10,
    height: 200
}

const options = Object.assign({}, defaultOptions, opts);

console.log(options); //{width: 100, height:200}

Dodajmy domyślne ustawienia w konstruktorze:


class Slider {
    constructor(elemSelector, opts) {
        this.slider = document.querySelector(elemSelector);
        this.slides = this.slider.querySelectorAll(".slider-slide");
        this.currentSlide = 0;
        this.prev = null;
        this.next = null;
        this.dots = [];
        this.time = null;

        const defaultOpts = {
            pauseTime : 0
        };
        this.options = Object.assign({}, defaultOpts, opts);

        this.generateSlider();
        this.changeSlide(this.currentSlide);
    }

    ...
}

Domyślnie czas przerwy między kolejnym przełączaniem będzie wynosił 0. Jeżeli programista tego nie zmieni, nie będziemy automatycznie przełączać slajdów:


class Slider {
    ...

    changeSlide(index) {
        this.slides.forEach(slide => {
            slide.classList.remove("slider-slide-active");
            slide.setAttribute("aria-hidden", true);
        });

        this.slides[index].classList.add("slider-slide-active");
        this.slides[index].setAttribute("aria-hidden", false);

        if (this.options.generateDots) {
            this.dots.forEach(dot => {
                dot.classList.remove("slider-pagination-element-active");
            });
            this.dots[index].classList.add("slider-pagination-element-active");
        }

        this.currentSlide = index;

        if (typeof this.options.pauseTime === "number" && this.options.pauseTime !== 0) {
            clearInterval(this.time);
            this.time = setTimeout(() => this.slideNext(), this.options.pauseTime);
        }
    }
}

Podobne opcje możemy ustawić dla innych elementów naszego slidera. Dla przykładu możemy dodać opcję do zmiany tekstów na przyciskach:


class Slider {
    constructor(elemSelector, opts) {
        this.currentSlide = 0;
        this.slider = document.querySelector(elemSelector);
        this.slides = this.slider.querySelectorAll(".slider-slide");
        this.prev = null; //przycisk prev
        this.next = null; //przycisk next
        this.dots = [];
        this.time = null;

        const defaultOpts = {
            pauseTime : 0,
            prevText : "Poprzedni slide",
            nextText : "Następny slide"
        };
        this.options = Object.assign({}, defaultOpts, opts);

        this.generateSlider();
        this.changeSlide(this.currentSlide);
    }

    ...
}

class Slider {
    ...

    createPrevNext() {
        this.prev = document.createElement("button");
        this.prev.type = "button";
        this.prev.innerText = this.options.prevText;
        this.prev.classList.add("slider-button");
        this.prev.classList.add("slider-button-prev");
        this.prev.addEventListener("click", this.slidePrev.bind(this));

        this.next = document.createElement("button");
        this.next.type = "button";
        this.next.innerText = this.options.nextText;
        this.next.classList.add("slider-button");
        this.next.classList.add("slider-button-next");
        this.next.addEventListener("click", this.slideNext.bind(this));

        const nav = document.createElement("div");
        nav.classList.add("slider-nav");
        nav.appendChild(this.prev);
        nav.appendChild(this.next);
        this.slider.appendChild(nav);
    }
}

Ostatnie dwie opcje, które dodamy to możliwość wyłączenia generowania przycisków "poprzedni-następny" jak i generowania kropek:


class Slider {
    ...

    generateSlider() {
        //pobieramy element który zamienimy na slider
        this.slider = document.querySelector(this.sliderSelector);
        this.slider.classList.add("slider");

        //tworzymy kontener dla slajdów
        const slidesCnt = document.createElement("div");
        slidesCnt.classList.add("slider-slides-cnt");

        //pobieramy element slajdów
        this.slides = this.slider.children;

        //to jest żywa kolekcja, więc przy przeniesieniu każdego slajdu
        //jej długość maleje
        while (this.slides.length) {
            this.slides[0].classList.add("slider-slide");
            slidesCnt.appendChild(this.slides[0]);
        }

        //musimy na nowo pobrać slajdy, bo powyższa kolekcja jest już pusta
        this.slides = slidesCnt.querySelectorAll(".slider-slide");

        //wygenerowaliśmy kontener ze slajdami, wstawiamy go więc do slidera
        this.slider.appendChild(slidesCnt);

        if (this.options.generatePrevNext) this.createPrevNext();
        if (this.options.generateDots) this.createPagination();
    }
}

Oraz w metodzie changeSlide(), która działa na kropkach.


class Slider {
    changeSlide(index) {
        this.slides.forEach(slide => {
            slide.classList.remove("slider-slide-active");
            slide.setAttribute("aria-hidden", true);
        });

        this.slides[index].classList.add("slider-slide-active");
        this.slides[index].setAttribute("aria-hidden", false);

        if (this.options.generateDots) {
            this.dots.forEach(dot => {
                dot.classList.remove("slider-pagination-element-active");
            });
            this.dots[index].classList.add("slider-pagination-element-active");
        }

        this.currentSlide = index;

        if (typeof this.options.pauseTime === "number" && this.options.pauseTime !== 0) {
            clearInterval(this.time);
            this.time = setTimeout(() => this.slideNext(), this.options.pauseTime)
        }
    }

    ...
}

Od tej chwili programista używając naszego slidera będzie mógł podawać także jego opcje:


//slider1
const opts1 = {
    pauseTime : 0,
    prevText : "Poprzedni",
    nextText : "Następny"
}
const slide1 = new Slider("#slider1", opts);


//slider2
const opts1 = {
    pauseTime : 5000,
    generatePrevNext : false
}
const slide2 = new Slider("#slider2", opts);

Cały kod

Dotarliśmy do końca. Poniżej cały kod JavaScript (css się nie zmienił) i link do dema:


class Slider {
    constructor(elemSelector, opts) {
        this.sliderSelector = elemSelector;
        this.currentSlide = 0;
        this.time = null;
        this.slider = null;
        this.elem = null;
        this.slides = null;
        this.prev = null;
        this.next = null;
        this.dots = [];

        const defaultOpts = {
            pauseTime : 0,
            prevText : "Poprzedni slide",
            nextText : "Następny slide",
            generateDots : true,
            generatePrevNext : true
        };
        this.options = Object.assign({}, defaultOpts, opts);

        this.generateSlider();
        this.changeSlide(this.currentSlide);
    }

    generateSlider() {
        //pobieramy element który zamienimy na slider
        this.slider = document.querySelector(this.sliderSelector);
        this.slider.classList.add("slider");

        //tworzymy kontener dla slajdów
        const slidesCnt = document.createElement("div");
        slidesCnt.classList.add("slider-slides-cnt");

        //pobieramy element slajdów
        this.slides = this.slider.children;

        //to jest żywa kolekcja, więc przy przeniesieniu każdego slajdu
        //jej długość maleje
        while (this.slides.length) {
            this.slides[0].classList.add("slider-slide");
            slidesCnt.appendChild(this.slides[0]);
        }
        this.slides = slidesCnt.querySelectorAll(".slider-slide");
        this.slider.appendChild(slidesCnt);

        if (this.options.generatePrevNext) this.createPrevNext();
        if (this.options.generateDots) this.createPagination();
    }

    slidePrev() {
        this.currentSlide--;
        if (this.currentSlide < 0) {
            this.currentSlide = this.slides.length - 1;
        }
        this.changeSlide(this.currentSlide);
    }

    slideNext() {
        this.currentSlide++;
        if (this.currentSlide > this.slides.length - 1) {
            this.currentSlide = 0;
        }
        this.changeSlide(this.currentSlide);
    }

    changeSlide(index) {
        this.slides.forEach(slide => {
            slide.classList.remove("slider-slide-active");
            slide.setAttribute("aria-hidden", true);
        });

        //dodajemy ją tylko wybranemu
        this.slides[index].classList.add("slider-slide-active");
        this.slides[index].setAttribute("aria-hidden", false);

        //podobny manewr robimy dla kropek
        if (this.options.generateDots) {
            this.dots.forEach(dot => {
                dot.classList.remove("slider-pagination-element-active");
            });
            this.dots[index].classList.add("slider-pagination-element-active");
        }

        //aktualny slide przestawiamy na wybrany
        this.currentSlide = index;

        if (this.options.pauseTime !== 0) {
            clearInterval(this.time);
            this.time = setTimeout(() => this.slideNext(), this.options.pauseTime)
        }
    }

    createPrevNext() {
        this.prev = document.createElement("button");
        this.prev.type = "button";
        this.prev.innerText = this.options.prevText;
        this.prev.classList.add("slider-button");
        this.prev.classList.add("slider-button-prev");
        this.prev.addEventListener("click", this.slidePrev.bind(this));

        this.next = document.createElement("button");
        this.next.type = "button";
        this.next.innerText = this.options.nextText;
        this.next.classList.add("slider-button");
        this.next.classList.add("slider-button-next");
        this.next.addEventListener("click", this.slideNext.bind(this));

        const nav = document.createElement("div");
        nav.classList.add("slider-nav");
        nav.appendChild(this.prev);
        nav.appendChild(this.next);
        this.slider.appendChild(nav);
    }

    createPagination() {
        const ulDots = document.createElement("ul");
        ulDots.classList.add("slider-pagination");

        //tworzymy pętlę w ilości liczby slajdów
        for (let i=0; i<this.slides.length; i++) {
            //każdorazowo tworzymy LI wraz z buttonem
            //każdy button po kliknięciu zmieni slajd
            //za pomocą metody changeSlide()

            const li = document.createElement("li");
            li.classList.add("slider-pagination-element");

            const btn = document.createElement("button");
            btn.classList.add("slider-pagination-button");
            btn.type = "button";
            btn.innerText = i+1;
            btn.setAttribute("aria-label", `Ustaw slajd ${i+1}`);

            btn.addEventListener("click", () => this.changeSlide(i));

            li.appendChild(btn);

            ulDots.appendChild(li);
            this.dots.push(li);
        }

        this.slider.appendChild(ulDots);
    }

//wywołanie bez opcji
const slide = new Slider("#slider1");

//wywołanie z opcjami
const slide = new Slider("#slider1", {
    pauseTime : 10000,
    generateDots : true,
    generatePrevNext : true,
    prevText : "Poprzedni",
    nextText : "Następny"
});

Demo

Poniższy przykład - już końcowy - zawiera różne slidery wraz z dodatkowym stylowaniem, którym zmieniłem ich wygląd. Dla strzałek zastosowałem https://www.bootstrapcdn.com/fontawesome/ wraz ikonami (lewo, prawo) dla strzałek

, oraz naprawdę lekkim efektem przy najechaniu na nie kursorem. Demo

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.

Menu