Strona początkowa

Ujażmiamy Nivo-Slider

Dzisiaj zakończymy zmagania ze sliderami na bardziej praktycznym przykładzie.

Strona takie jak http://www.webappers.com/ i http://www.webresourcesdepot.com/ udostępniają nam setki różnych dodatków, wśród których znajdują się także slidery. Jednym z najbardziej popularnych sliderów jest Nivo Slider. Kto o nim nie słyszał powinien nadrobić swoją niewiedzę, gdyż jest to bluźnierstwo podobne w skali do takich czynów jak nie obejrzenie żadnej części Gwiezdnych Wojen (a znam takich bluźnierców).

Nivo Slider jaki jest każdy widzi. Napisanie od podstaw takiego bajeru nie jest wcale taką banalną strawą - szczególnie testowanie tego na różnych przeglądarkach. Dzisiaj zajmiemy się ujarzmianiem tego potwora, która to czynność często gęsto może się wam przydać.

Zaczynamy

Na samym początku tworzymy listę obrazków, które Nivo Slider będzie animował. Powinny być one tej samej wielkości:

		<div id="slider">
			<a href=""><img src="obrazek1.jpg" width="400" height="200" alt="" /></a>
			<a href=""><img src="obrazek2.jpg" width="400" height="200" alt=""  /></a>
			<a href=""><img src="obrazek3.jpg" width="400" height="200" alt=""  /></a>
			<a href=""><img src="obrazek4.jpg" width="400" height="200" alt=""  /></a>
		</div>
	

W naszym przykładzie każdy obrazek będzie linkiem do pewnej strony, ale nie jest to wymagane. Dodatkowo dla każdego obrazka stworzymy opis. Zwykły tekstowy opis powinien znaleźć się w atrybucie title każdego obrazka:

		<div id="slider">
			<a href=""><img src="obrazek1.jpg" width="400" height="200" alt="" title="To jest tekstowy opis tego slajda" /></a>
			...
		</div>
	

Jeżeli jednak interesuje nas nieco większa kontrola nad opisem (wstawianie do niego HTML), wtedy taki opis powinniśmy umieścić w divie, dla którego nadamy klasę nivo-slider-caption. Atrybut ID tego diva powinien znaleźć się w atrybucie title obrazka (poprzedzony znakiem #), dla którego jest opisem:

		<div id="slider">
			<a href=""><img src="obrazek1.jpg" width="400" height="200" alt="" /></a>
			<a href=""><img src="obrazek2.jpg" width="400" height="200" alt="" title="To jest tekstowy opis tego slajda"  /></a>
			<a href=""><img src="obrazek3.jpg" width="400" height="200" alt="" title="#opisSlide2"  /></a>
			<a href=""><img src="obrazek4.jpg" width="400" height="200" alt="" title="#opisSlide3"  /></a>
		</div>
		
		<div id="opisSlide2" class="nivo-html-caption">
    		To jest przykładowy <strong>html opis dla obrazka 2</strong>
		</div>
		<div id="opisSlide3" class="nivo-html-caption">
    		To jest inny <strong>opis dla obrazka 3</strong>
		</div>
	

Po przygotowaniu html, czas odpalić naszego slidera:

		
		$(window).load(function() {
			$('#slider').nivoSlider({
				effect:'random', //styl animacji
				slices:15, //ile kawałków przypada na animację
				animSpeed:500, //szybkość animacji
				pauseTime:3000, //szybkość zatrzymania na slajdzie
				startSlide:0, //zaczynamy od slajda
				directionNav:true, //czy pokazać strzałki next - prev
				directionNavHide:true, //pokaż strzałki tylko po najechaniu
				controlNav:true, //stwórz klikalne 1,2,3...
				controlNavThumbs:false, //użyj miniaturek dla powyższych
				controlNavThumbsFromRel:false, //użyj relatywnych ścieżek z miniaturek
				controlNavThumbsSearch: '.jpg', //rozszerzenie mianiturki...
				controlNavThumbsReplace: '_thumb.jpg', //nazwa miniaturki 
				keyboardNav:true, //użyj klawiatury (lewo / prawo)
				pauseOnHover:true, //zatrzymaj po najechaniu myszką
				manualAdvance:false, //własne przejście
				captionOpacity:0.8, //przezroczystość opisów
				beforeChange: function(){}, //zdarzenie wywoływane tuż przed animacją
				afterChange: function(){}, //zdarzenie wywoływane tuż po animacji
				slideshowEnd: function(){}, //wywoływane po animacji wszystkich slajdów
				lastSlide: function(){}, //po pokazaniu ostatniego slajdu
				afterLoad: function(){} //wywołane po załadowaniu slajdów (grafik)
			});
		});
	

Konstrukcja monstrualna, ale jak wiemy z rozdziału o pluginach, możemy podać tylko parametry, które chcemy zmienić. Reszta przyjmie swoje domyślne wartości (czylite te podane powyżej). Pierwszy parametr określający rodzaj animacji - tak jak to mówi nam specyfikacja na stronie projektu - może przyjąć właściwości:
sliceDown, sliceDownLeft, sliceUp, sliceUpLeft, sliceUpDown, sliceUpDownLeft, fold, fade, random, slideInRight, slideInLeft.
Opisywać ich działania nie będę, bo wystarczy sobie ustawić dany typ by zobaczyć jak to wygląda (a i jestem leniem). My zostaniemy przy losowym (domyślnym) typie.

Po wywołaniu powyższego kodu, wygeneruje on fajne wyglądający slider ze strzałkami do przewijania, linkami do przełączania między slidami (1, 2, 3) itp.
Wszystko fajnie wygląda, jednak nie do końca jest tak wspaniałe, ponieważ istnieje mała niedogodność.
Wygenerowanym linkom możemy nadać wspólny wygląd (na stronie Nivo Slider przełączniki takie mają wygląd kropek), jednak czasami jest to nie wystarczające.
Wyobraźmy sobie, że przełącznikami kolejnych slidów mają nie być standardowo generowane liniki 1,2,3, a elementy zupełnie inne, które już istnieją na stronie. Mogą to być optiony selekta czy dowolne inne elementy.
Dla przykładu nasza strona może zawierać listę tekstów, które po kliknięciu powinny zmieniać nasz slider.

		<ul class="lista">
			<li>To jest pierwszy tekst przełączający</li>
			<li>To jest drugi tekst przełączający</li>
			<li>To jest trzeci tekst przełączający</li>
			<li>To jest trzeci tekst przełączający</li>
		</ul>
	

Aby przygotować nasz slider, musimy zmienić kilka jego domyślnych właściwości (parametrów):

		$(window).load(function() {
			$('#slider').nivoSlider({
				directionNav:false, //nie chcemy strzałek
				directionNavHide:false, //nie chcemy strzałek po najechaniu
				controlNav:true //stwórz 1,2,3
			});
		});
	

Resztę zostawimy domyślnie, bo nie będzie nam to zawadzać. Stan bieżący naszej pracy dostępny jest tutaj:

Nivo slider demo 1

Nasz slider umieściłem na zewnętrznej stronie, bo slidery tego typu w liczbie większej niż 2 mogą ujarzmić nawet dobrą konfigurację sprzętową (szczególnie w niegrzeszącym wydajnością FF).

Jak więc widać z powyższego kodu, nasz slider będzie prostackim "osłem", który nie da się kontrolować prawie w żaden sposób. Jedyne kontrolki to linki do kolejnych slajdów czyli nasze 1, 2, 3. Gdy użyjemy Firebuga, okaże się, że wspomniane linki zgrupowane są w divie, który ma klasę nivo-controlNav. Widać więc wyraźnie, że popełniły podstawowy błąd, jaki popełnia każdy amator na polu walki. Nigdy nie chodzimy w zwarciu!
Dzięki ich amatorskiemu podejściu możemy je wykorzystać do naszych celów czyli podpięcia przełączania pod inne elementy.
Ukryjemy je więc za pomocą CSS, a następnie symulujemy ich klikanie, poprzez wywołanie dla nich zdarzenia click:

		.nivo-controlNav a {display:none;}
	
		var $lista = $('.lista'); //pobieramy naszą listę
		var $li = $lista.find('li') //pobieramy li naszej listy
		$li.bind(
			'click' : function() {
				var index = $li.index($(this)); //pobieramy numer klikniętego li
				$('.nivo-controlNav a').eq(index).click() //klikamy na 1,2,3, którego numer jest równy pobranemu powyżej numerowi
			}
		)
	

Nivo slider demo 2

Włala. Po kliknięciu na któryś z tekstów naszej listy, "klikamy" na ukryty link nivo-slidera, dzięki czemu przełączamy go na stosowny slajd.
Prosta sprawa. Po raz kolejny udało nam się w bardzo prosty sposób uratować świat.
Niestety jak to w życiu bywa, sprawa nie jest do końca taka prosta.

Po kliknięciu na link, musimy zaznaczyć (nadać mu klasę), że został on kliknięty. Dzięki temu użytkownik będzie wiedział, który slide jest wyświetlany w danym momencie.

		var $lista = $('.lista'); //pobieramy naszą listę
		var $li = $lista.find('li') //pobieramy li naszej listy
		$li.bind(
			'click' : function() {
				var index = $li.index($(this));
				$('.nivo-controlNav a').eq(index).click()
				$li.removeClass('active'); //usuwamy klasę z wszystkich tekstów
				$(this).addClass('active'); //nadajemy klasę tylko klikniętemu
			}
		).eq(0).addClass('active'); //pierwszemu LI dajemy klasę active
	

Nivo slider demo 3

Poprawki, poprawki i ...poprawki

Znowu banał - prawda? Banalna, aczkolwiek błędna!
Przy każdym kliknięciu ustawiamy klasę, a przecież animacja może podczas takiego klikania dalej trwać, więc bardzo szybko użytkownik zauważy asynchronizację.
Aby to naprawić skorzystamy ze zdarzenia nivo slidera, a mianowicie afterChange: function(){}.

Po kliknięciu na dany tekst, włączymy przełączenie na dany slajd (jak w skrypcie powyżej). Ustawienia klasy natomiast wykonamy dopiero po zakończeniu animacji:

		var $lista = $('.lista'); //pobieramy naszą listę
		var $li = $lista.find('li') //pobieramy li naszej listy
		$li.bind({
			'click' : function() {
				var index = $li.index($(this));
				$('.nivo-controlNav a').eq(index).click();
				//$li.removeClass('active');
				//$(this).addClass('active');
			}
		}).eq(0).addClass('active'); //pierwszemu LI dajemy klasę active
		
		$('#slider').nivoSlider({
			directionNav:false, //nie chcemy strzałek
			directionNavHide:false, //nie chcemy strzałek po najechaniu
			controlNav:true, //doszedł przecinek!
			afterChange: function() {
				var $lista = $('.lista');
				var $li = $lista.find('li');
				var index = $('.nivo-controlNav .active').index(); //pobieramy numer aktywnego slajdu
				$li.removeClass('active');
				$li.eq(index).addClass('active');
			}
		});		
	

Jeżeli komuś nie odpowiada "spowolnione" ustawianie aktywnej klasy dla klikniętego tekstu, może wtedy wykorzystać zdarzenie beforeChange lub wymieszać powyższe metody kliknięcia i użycia zdarzenia afterChange (np do włączania/wyłączania możliwości kliknięcia - za pomocą dodatkowej zmiennej). Jeżeli dotrwałeś do tego momentu, myślę, że nie będzie to żadnym problemem dla ciebie.

Nivo slider demo 4

Obsługa dodatkowych klas

Przypuśćmy, że nasza lista posiadała dodatkowe klasy first i last, które określają odmienny wygląd pierwszego i ostatniego elementu.

		<ul class="lista">
			<li class="first">To jest pierwszy tekst przełączający</li>
			<li>To jest drugi tekst przełączający</li>
			<li>To jest trzeci tekst przełączający</li>
			<li class="last">To jest trzeci tekst przełączający</li>
		</ul>
	

Z powodu braku obsługi background-position-y przez przeglądarkę FF w wersji < 4, nie będziemy w stanie użyć wspólnej klasy .active dla wszystkich elementów.
Z tego też powodu zamiast używania klasy active dla klikniętych LI, jesteśmy zmuszeni do przerabiania klasy first na first_active, a last na last_active. Dla reszty elementów działamy jak poprzednio, aplukując klasę active.

Niestety autor nie ma siły (zmęczenie bohatera) do kolejnej dyskusji na temat nie prawidłowości tego podejścia.
Jeżeli i ty należysz do grupy webmasterów, którzy uważają, że ten problem nie istnieje i CSS sprite spokojnie można obsłużyć za pomocą podwójnej deklaracji x, y, zachęcam wtedy do przeprowadzenia eksperymentów z CSS sprite. Może zmienisz zdanie? A może zmienisz moje zdanie?

Wykorzystamy do tego nasze wyrenderowano-schowane odnośniki :). Jeżeli index klikniętego LI równa się 0, wtedy nadajemy klasę first_active, jeżeli ostatni (length-1) klasę last_active, jeżeli natomiast różnych od tych wartości, działamy jak poprzednio nadając klasę active. Pamiętać należy też, że teraz nasz pierwszy element nie ma klasy active gdy jest aktywny, stąd musimy przerobić nadawanie klasy dla 1 LI:

		.eq(0).addClass('first_active'); //pierwszemu LI dajemy klasę aktywna
	
		afterChange: function(){
			var $lista = $('.lista');
			var $li = $lista.find('li');
			var $odnosniki = $('.nivo-controlNav a'); //nasze schowane odnosniki
			var index = $odnosniki.filter('.active').index(); //pobieramy numer aktywnego slajdu

			//korzystamy ze skruconej wersji zapisu if
			//jeżeli index = 0 to ustawiamy klasę na first_active
			//jeżeli index = nivoControls.length-1 to ustawiamy klasę na last_active
			//jeżeli index jest inny niż powyższe przypadki to klasa ustawiana jest na active

			var klasa = (index==0)?'first_active' : (index==$odnosniki.length-1)? 'last_active' : 'active';

			//usuwamy wszystkie klasy, po czym dodajemy wyliczoną powyżej klasę

			$li.removeClass('active').removeClass('first_active').removeClass('last_active').eq(index).addClass(klasa);
		}
	

Nivo slider demo 5

Właśnie ujarzmiliśmy byka. Może nie jest to najpiękniejsza metoda walki z rogatym stworzeniem, ale to już wina popularnej przeglądarki że walnęła takiego babola w braku obsługi jednej z najbardziej pożądanych właściwości CSS.

Jak widać w powyższym przykładzie, stosując Firebuga + kilka prostych technik, bardzo łatwo jest zmieniać działanie gotowego pluginu tak by pasował do naszych potrzeb. Im będziemy lepiej tańczyć po elementach DOM, tym będziemy mieli większe możliwości do działania na naszą korzyść.