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.