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ż HTMLPokaż CSS
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({
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 plugin
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");
let time = null; //posłuży do automatycznego przewijania slidera
});
...
Podepnijmy teraz odpowiednie zdarzenia pod przyciski przesuwające:
(function($) {
$.fn.carousel = function(config) {
const options = $.extend({
animationTime : 700,
pauseTime : 3000,
onScroll : function() {},
}, config);
return this.each(function() {
const $this = $(this);
...
const scrollPrev = () => { ... }
const scrollNext = () => { ... }
$prevBtn.bind("click", e => {
e.preventDefault();
scrollPrev();
});
$nextBtn.bind("click", e => {
e.preventDefault();
scrollNext();
});
});
}
});
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.
(function($) {
$.fn.carousel = function(config) {
const options = $.extend({
animationTime : 700,
pauseTime : 3000,
onScroll : function() {},
auto : false
}, config);
return this.each(function() {
...
const scrollNext = () => {
e.preventDefault();
if (!$ul.is(":animated")) {
const $li = $ul.find(".carousel-list-el");
$ul.animate({
"margin-left" : -$li.outerWidth(true) //właściwość outerWidth(true) zwraca szerokość pierwszego elementu w kolekcji. Parametr true oznacza czy doliczyć margines
}, options.animationTime, () => {
$li.last().after($li.first());
$ul.css({"margin-left" : 0});
options.onScroll();
});
}
}
...
});
}
});
Pozostał nam przeciwny kierunek:
(function($) {
$.fn.carousel = function(config) {
const options = $.extend({
animationTime : 700,
pauseTime : 3000,
onScroll : function() {},
auto : false
}, config);
return this.each(function() {
...
const scrollPrev = () => {
e.preventDefault();
if (!$ul.is(":animated")) {
const $li = $ul.find(".carousel-list-el");
$ul.css("margin-left", -$li.last().outerWidth(true));
$li.first().before($li.last());
$ul.animate({
"margin-left" : 0
}, options.animationTime, () => {
options.onScroll();
});
}
}
$prevBtn.bind("click", e => {
e.preventDefault();
scrollPrev();
});
$nextBtn.bind("click", e => {
e.preventDefault();
scrollNext();
});
});
}
});
Nasze przesuwanie realizujemy tylko w momencie gdy nasza lista się nie przesuwa, stąd na początku sprawdzenie $ul.not(":animated").
Jeżeli elementów w karuzeli będzie mniej niż zajmowana przez nią przestrzeń, zauważalne będzie przekładanie elementu z lewej na prawą stronę. Bardzo prosto możemy to naprawić poprzez zduplikowanie elementów na liście.
(function($) {
$.fn.carousel = function(config) {
const options = $.extend({
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 $prevBtn = $this.find(".carousel-prev");
const $nextBtn = $this.find(".carousel-next");
let time = null;
$ul.append($li.clone(true)).append($li.clone(true)); //klonujemy elementy 2x (może być i więcej)
...
});
}
});
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({
animationTime : 700,
pauseTime : 3000,
onScroll : function() {},
auto : false
}, config);
...
}
Automatyczne przesuwanie powinniśmy odpalić w trzech momentach: przy starcie naszej karuzeli, i gdy użytkownik będzie klikał 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({
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 $prevBtn = $this.find(".carousel-prev");
const $nextBtn = $this.find(".carousel-next");
let time = null;
$ul.append($li.clone(true)).append($li.clone(true));
const autoNext = function() {
if (options.auto) {
clearTimeout(time);
time = setTimeout(function() {
scrollNext()
}, options.pauseTime);
}
};
const scrollPrev = () => {
if (!$ul.is(":animated")) {
const $li = $ul.find(".carousel-list-el");
$ul.css("margin-left", -$li.last().outerWidth(true));
$li.first().before($li.last());
$ul.animate({
"margin-left": 0
}, options.animationTime, () => {
options.onScroll();
});
autoNext();
}
};
const scrollNext = () => {
if (!$ul.is(":animated")) {
const $li = $ul.find(".carousel-list-el");
$ul.animate({
"margin-left": -$li.outerWidth(true)
}, options.animationTime, () => {
$li.last().after($li.first());
$ul.css({"margin-left": 0});
options.onScroll();
autoNext();
});
}
};
$prevBtn.bind("click", e => {
e.preventDefault();
scrollPrev();
});
$nextBtn.bind("click", e => {
e.preventDefault();
scrollNext();
});
autoNext();
});
}
})(jQuery);
Przykład użycia
I w zasadzie zakończyliśmy naszą pracę. Pozostaje odpalenie jej dla danych elementów.
<div class="carousel demo" id="demo">
<button arial-label="poprzedni" class="carousel-prev"> < </button>
<div class="carousel-wrapper">
<ul class="carousel-list">
<li class="carousel-list-el">
<img src="https://pokaimg.me/150x200/people?text=1" width="200" height="200" alt="">
</li>
<li class="carousel-list-el">
<img src="https://pokaimg.me/300x200/people?text=2" width="300" height="200" alt="">
</li>
<li class="carousel-list-el">
<img src="https://pokaimg.me/100x200/people?text=3" width="100" height="200" alt="">
</li>
<li class="carousel-list-el">
<img src="https://pokaimg.me/200x200/people?text=4" width="200" height="200" alt="">
</li>
<li class="carousel-list-el">
<img src="https://pokaimg.me/250x200/people?text=5" width="250" height="200" alt="">
</li>
</ul>
</div>
<button arial-label="następny" class="carousel-next"> > </button>
</div>
$(function() {
$("#demo").carousel({
animationTime: 300,
pauseTime: 5000,
auto: true
});
});
Tutaj mała uwaga. W powyższym przykładzie do slajdów wstawiłem umyślnie różnej wielkości zdjęcia. Może tam się znaleźć tekst czy dowolny inny element. Gdy przewijamy karuzelę, pobieramy szerokość danego elementu. W przypadku zdjęć mogą one być w tym momencie jeszcze w pełni nie wczytane, dlatego przy ich używaniu warto dodać im atrybuty width/height
.