jQuery - własny plugin

Korzystając z jQuery, nie raz używamy różnego rodzajów pluginów, czyli rozszerzeń do tej wspaniałej biblioteki. W tym artykule stworzymy nasze własne rozszerzenie.

Rozszerzenia do jQuery można podzielić na dwie części. Jedne z nich rozszerzają jQuery nie operując na elementach HTML:


    $.fn.random = function(min, max) {
        return Math.floor(Math.random()*(max-min+1)+min);;
    }

    console.log($.random(5, 50));

Drugie - bardziej nas interesujące operują na zbiorach elementów. Przykładem takiej funkcji może myć np. fadeOut(), który powoduje zanikanie obiektów, które zostały do niego przekazane.


$(".someElements").fadeOut("slow");

HTML i CSS

Nasz plugin będzie bardzo prostą karuzelą. Zanim zaczniemy pisać kod, stwórzmy HTML i CSS, który nam się przyda.

Pokaż HTML

<div class="carousel">
    <span class="carousel-prev">
        &#x25C2;
    </span>
    <div class="carousel-wrapper">
        <ul class="carousel-list">
            <li class="carousel-list-el">..1..</li>
            <li class="carousel-list-el">..2..</li>
            <li class="carousel-list-el">..3..</li>
            <li class="carousel-list-el">..4..</li>
        </ul>
    </div>
    <span class="carousel-next">
        &#x25B8;
    </span>
</div>

Pokaż CSS

/* infinite carousel */
.carousel {
    margin:1rem 0;
    clear: both;
    background: #eee;
    position: relative;
    width: 730px;
    height: 230px;
    padding:0 20px;
}

.carousel-wrapper {
    overflow: hidden;
    position: relative;
    width: 100%;
    height: 100%;
}

.carousel-list {
    list-style-type: none;
    height: 100%;
    margin: 0;
    padding: 0;
    width: 9999px;
}

.carousel-list-el {
    float: left;
    width: 220px;
    margin: 5px;
    text-align: center;
    line-height:220px;
    font-size:30px;
    background: #fff;
    height: 220px;
    overflow: hidden;
}

.carousel-prev,
.carousel-next {
    cursor: pointer;
    position: absolute;
    overflow: hidden;
    width: 50px;
    height:50px;
    border-radius:50%;
    z-index: 2;
    top:50%;
    transform:translate(0, -50%);
    font-size:2rem;
    font-family:sans-serif;
    text-align: center;
    line-height: 50px;
    color:#fff;
    background: tomato;
}

.carousel-prev {
    left: -15px;
}

.carousel-next {
    right: -15px;
}

Ogólny schemat pluginów

Ogólny schemat plugiu może mieć postać:


(function($){
    $.fn.functionName = function(config) {
        const options = $.extend({
            //...parametry
        }, config);

        return this.each(function() {
            //...metody, właściwości itp
        });
    }
})(jQuery);

Nasz plugin powinien mieć określone parametry swojego działania, które użytkownik powinien móc nadpisać własnymi wartościami. W czystym Javascript użylibyśmy tutaj Object.assign() lub stread syntax. Podobne czynności robiliśmy już w rozdziale gdzie tworzyliśmy slider. W jQuery możemy skorzystać z funkcji extend():


(function($){
    $.fn.carousel = function(config) {

        const options = $.extend({
            displayElements : 3, //ile elementów będziemy pokazywać
            animationTime   : 700, //czas animacji
            pauseTime       : 3000, //przerwa między automatycznym przewijaniem
            onScroll        : function() {} //opcjonalna funkcja zwrotna po przewinięciu jednego slajdu
        }, config);

    }
})(jQuery);

Dzięki temu użytkownik używający naszego dodatku będzie mógł go odpalać przekazujac do niego stosowne parametry:


$(".some-div").carousel({
    animationTime : 2000
});

Tworzymy kod pluginu

Przechodzimy do meritum. Plugin może być odpalony na pojedynczego ale i dla wielu elementów na stronie. Powinniśmy więc zrobić po nich pętlę i obsłużyć każdy z osobna:


(function($){
    $.fn.functionName = function(config) {
        const options = $.extend({
            //...parametry
        }, config);

        return this.each(function() {
            //...metody, właściwości itp
            const $this = $(this);
        });
    }
})(jQuery);

Po pierwsze podstawmy odpowiednie elementy pod zmienne:


return this.each(function() {
    const $this = $(this);

    const $ul = $this.find(".carousel-list");
    const $li = $ul.find(".carousel-list-el");
    const $prevBtn = $this.find(".prev");
    const $nextBtn = $this.find(".next");
    const itemWidth = $li.first().outerWidth(true); //szerokość elementu - o tyle będzie przesuwał się slider
    let time = null; //posłuży do automatycznego przewijania slidera

});

Podepnijmy teraz odpowiednie zdarzenia pod przyciski przesuwające (prev i next):


    $prevBtn.on("click", scrollPrev);
    $nextBtn.on("click", scrollNext);

Pozostało nam już tylko napisać te zdarzenia.
Pierwsze to scrollPrev(), które będzie pokazywało wcześniejsze pozycje elementy LI.

W tym momencie przyda się kawałek kartki.
Nasza karuzela będzie miała bardzo prostą zasadę. Jeżeli przewijamy listę w lewo, wtedy na pierwszą pozycję wrzucamy element z jej końca. Jeżeli listę przesuwamy w prawo, wtedy wrzucimy na jej koniec element z jej początku.

Przy przesuwaniu listy w lewo wykonamy następujące czynności:

  • Przesuniemy listę o 1 pole w lewo (0 - item_width)
  • Wstawimy ostatni element listy na jej początek
  • Zaanimujemy przesunięcie listy z pozycji 0 - item_width do pozycji 0

Aby zrealizować powyższe kroki musimy utworzyć lokalną metodę, która będzie przesuwać nasz scroller.


const scrollPrev = function() {
    if (!$ul.is(":animated")) {
        const $li = $ul.find(".carousel-list-el");
        $ul.css("margin-left", -item_width);
        $li.first().before($li.last());
        $ul.animate({"margin-left" : 0}, options.animationTime, function() {
            options.onScroll();
        });
    }
}

Nasze przesuwanie realizujemy tylko w momencie gdy nasza lista się nie przesuwa, stąd na początku sprawdzenie $ul.not(":animated").

Na początku tej metody po raz kolejny pobieram wszystkie LI naszej listy. Czemu?
Pobrane na początku pluginu LI podstawione są już pod zmienną, dlatego nie odzwierciedlają przerzucania z końca na początek i początku na koniec (bo w jQuery gdy pobieramy elementy, podobnie do querySelectorAll nie pobieramy ich jako żywe kolekcje).

Pozostał nam przeciwny kierunek:

  • Przesuwamy naszą listę w lewo (0-item_width)
  • Po animacji wstawiamy po końcowym LI ten z początku
  • ustawiamy pozycję UL na 0

const scrollNext = function() {
    if (!$ul.is(":animated")) {
        const $li = $ul.find(".carousel-list-el");
        $ul.animate({"margin-left" : -itemWidth}, options.animationTime, function() {
            $li.last().after($li.first());
            $ul.css({"margin-left" : 0});
            options.onScroll();
        });
    }
}

Nasz plugin ma teraz postać:


(function($){
    $.fn.carousel = function(config) {
        const options = $.extend({
            displayLi      : 3,
            animationTime   : 700,
            pauseTime     : 3000,
            onScroll        : function() {}
        }, config);

        return this.each(function() {
            const $this = $(this);

            const $ul = $this.find(".carousel-list");
            const $li = $ul.find(".carousel-list-el");
            const item_width = $li.first().outerWidth(true);
            const $prevBtn = $this.find(".carousel-prev");
            const $nextBtn = $this.find(".carousel-next");
            let time = null;

            const scrollPrev = function() {
                if (!$ul.is(":animated")) {
                    const $li = $ul.find(".carousel-list-el");
                    $ul.css("margin-left",-item_width);
                    $li.first().before($li.last());
                    $ul.animate({"margin-left" : 0}, options.animationTime, function(){
                        options.onScroll();
                    });
                }
            };

            const scrollNext = function() {
                if (!$ul.is(":animated")) {
                    const $li = $ul.find(".carousel-list-el");

                    $ul.animate({"margin-left" : -item_width}, options.animationTime, function(){
                        $li.last().after($li.first());
                        $ul.css({"margin-left" : 0});
                        options.onScroll();
                    });
                }
            };

            $prevBtn.bind("click", scrollPrev);
            $nextBtn.bind("click", scrollNext);
        });
    }
})(jQuery);

Automatyczne przewijanie

Aby nasz slider dodatkowo automatycznie się przesuwał, wykorzystamy dobrze nam znaną metodę setTimeout(), którą podstawimy pod zmienną time.

Dodajemy więc dodatkową opcję auto, która automatycznie będzie odpalać funkcję scrollNext():


(function($){
    $.fn.carousel = function(config) {
        const options = $.extend({
            displayLi     : 3,
            animationTime   : 700,
            pauseTime     : 3000,
            onScroll      : function() {},
            auto          : false
        }, config);

        ...

}

Automatyczne przesuwanie powinniśmy odpalić w trzech momentach. Po pierwsze przy starcie naszego plugina (jeżeli użytkownik ustawił opcję auto na true). Po drugie i trzecie - jeżeli użytkownik przestanie klikać na przyciski Poprzedni/Następny.

Żeby nie duplikować kodu, napiszmy dodatkową funkcję i odpalmy ją w stosownych momentach:


(function($){
    $.fn.carousel = function(config) {
        const options = $.extend({
            displayLi     : 3,
            animationTime   : 700,
            pauseTime     : 3000,
            onScroll      : function() {},
            auto          : false
        }, config);

        return this.each(function() {
            const $this = $(this);

            const $ul = $this.find(".carousel-list");
            const $li = $ul.find(".carousel-list-el");
            const item_width = $li.first().outerWidth(true);
            const $prevBtn = $this.find(".carousel-prev");
            const $nextBtn = $this.find(".carousel-next");
            let time = null;

            const autoNext = function() {
                if (options.auto) {
                    clearTimeout(time);
                    time = setTimeout(function() {
                        scrollNext()
                    }, options.pauseTime);
                }
            };

            const scrollPrev = function() {
                if (!$ul.is(":animated")) {
                    const $li = $ul.find(".carousel-list-el");
                    $ul.css("margin-left",-item_width);
                    $li.first().before($li.last());
                    $ul.animate({"margin-left" : 0}, options.animationTime, function(){
                        options.onScroll();
                    });

                    autoNext();
                }
            };

            const scrollNext = function() {
                if (!$ul.is(":animated")) {
                    const $li = $ul.find(".carousel-list-el");

                    $ul.animate({"margin-left" : -item_width}, options.animationTime, function(){
                        $li.last().after($li.first());
                        $ul.css({"margin-left" : 0});
                        options.onScroll();

                        autoNext();
                    });
                }
            };

            $prevBtn.bind("click", scrollPrev);
            $nextBtn.bind("click", scrollNext);

            autoNext();
        });
    }
})(jQuery);

Przykład użycia

I w zasadzie zakończyliśmy naszą pracę. Pozostaje odpalenie jej dla danych elementów:


$(function() {
    $("#demo2").carousel({
        displayLi: 3,
        animationTime: 300,
        pauseTime: 3000,
        auto: true
    });
});

$(function() {
    $("#demo2").carousel({
        displayLi: 3,
        animationTime: 700,
        pauseTime: 500,
        auto: false
    });
});

Podsumowanie

Powyższy artykuł pokazał ci jak możesz zbudować własny plugin.

Czy budowanie karuzel jest najlepszym wyborem? W dzisiejszych czasach rzucanie się na takie zabawy wydaje się sztuką dla sztuki, ponieważ mamy na rynku świetne rozwiązania takie jak:

A pamiętajmy, że w naszym - nie ma co ukrywać - prymitywnym rozwiązaniu nie obsłużyliśmy ani responsywności, ani obsługi chociażby palca.

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.