Lekce 10 - Jak na vlastní jQuery plug-in
V minulé lekci, Stylování v jQuery, jsme se zabývali stylováním.
Jak již napovídá titulek článku, napíšeme si vlastní plug-in do
jQuery. Určitě jste se již s nějakým takovým pluginem setkali. Volají se
stejně jako jakákoli funkce v této knihovně a to například takto
$(element).funkce()
.
Pluginů je na internetu spoustu, možná také proto je framework tak úspěšný. Může se ale stát, že vám žádný tento plug-in nebude vyhovovat a to třeba licencí. Ta totiž ani zdaleka nemusí být pouze open-source, a tak se můžete setkat i s placenými plug-iny. Avšak stejně tak můžete placené pluginy vydávat sami. Tento článek je úvodem do tvorby vlastních doplňků do jQuery. Článek je založen na vývoji reálného plug-inu a to na jednoduchém slideru obsahu.
Struktura HTML & CSS
Když píšeme vlastní doplněk, musíme si uvědomit, jaká bude struktura
HTML a podle ní se zařídit a nastylovat ji. Chceme slider obsahu, a proto pro
nás bude nejlepší použít seznam, tedy element <ul>
.
Nastylujeme ho podle tříd, které přiřadí sám plug-in. Tady je malá
ukázka HTML struktury se základními CSS styly:
HTML struktura
<ul> <li>Obsah prvního slidu</li> <li>Obsah druhého slidu</li> </ul>
CSS styly
/** * Vsechny slidy */ ul.easySlider-content { padding: 20px; margin-bottom: 25px; position: relative; height: 250px; z-index: 100; -webkit-box-shadow: 0 0 10px #aaa; -moz-box-shadow: 0 0 10px #aaa; box-shadow: 0 0 10px #aaa; border: 1px solid #dcdcdc; background: #ffffff; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIxJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlZWVlZWUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); background: -moz-linear-gradient(top, #ffffff 1%, #eeeeee 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#ffffff), color-stop(100%,#eeeeee)); background: -webkit-linear-gradient(top, #ffffff 1%,#eeeeee 100%); background: -o-linear-gradient(top, #ffffff 1%,#eeeeee 100%); background: -ms-linear-gradient(top, #ffffff 1%,#eeeeee 100%); background: linear-gradient(top, #ffffff 1%,#eeeeee 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); } ul.easySlider-content li { margin-right: 20px; } /** * Aktivní slide */ ul li.active { display: block !important; } /** * Posuvne sipky */ a.arrowLeft { display: block; width: 70px; height: 70px; background: #fff url('images/arrow_left.png') 3px center no-repeat; position: absolute; margin: 140px -48px; border-radius: 50px; border: 5px solid #dcdcdc; } a.arrowRight { display: block; width: 70px; height: 70px; background: #fff url('images/arrow_right.png') 18px center no-repeat; position: absolute; margin: 140px 770px; border: 5px solid #dcdcdc; border-radius: 50px; } a.arrowLeft:hover, a.arrowRight:hover { background-color: #dcdcdc; }
jQuery.fn
jQuery.fn
je, dalo by se říct, jmenný prostor pro vaše
doplňky. Všechny funkce umístěny právě do tohoto objektu můžeme zavolat
přímo z jQuery. Kontextem těchto funkcí je právě vybraný element. Co je
to ale ten kontext funkcí? Každá funkce v JavaScriptu je zpracovávána v
nějakém kontextu. Laicky řečeno je to to klíčové slovo this
.
Pokud tedy vložíme funkci do jQuery.fn
a zavoláme náš plug-in
např.: $('#obsah').nazevMeFunkce()
, bude v dané funkci klíčové
slovo this
obsahovat element s id="obsah"
. Bude však
přístupný jako objekt HTMLElement
, ne jako jQuery objekt. Pokud
bychom ho chtěli použít jako objekt jQuery, jednoduše uvedeme
this
jako parametr jQuery objektu: jQuery(this)
nebo
$(this)
(toto jsou totožné zápisy).
Když již víme, kam vložit náš plug-in, můžeme začít psát. Obecně
je nejlepší náš doplněk zabalit do anonymní funkce, aby se nepřepisovaly
proměnné a tak dále. Je to lepší také proto, že znak $
používají i jiné knihovny (krom jQuery) jako zástupný znak a to v
anonymních funkcích řešit nemusíme. Měli bychom si také navrhnout, jak
budeme volat další funkce v plug-inu a jak vyřešíme uživatelské
nastavení. Uvedu teď jednoduchý příklad základního plug-inu s logikou
volání funkcí, který pak rozeberu.
// Naše anonymní funkce - jediným parametrem je $, který zastupuje objekt jQuery (function( $ ) { // Zde budou metody, které může z plug-inu zavolat i sám uživatel var methods = { // Inicializační metoda - je volána z hlavní funkce init: function( o ) {} }; // Vstupní funkce plug-inu $.fn.easySlider = function( method ) { // Pokud máme jméno funkce, kterou chceme zavolat if ( methods[method] ) { // Zavoláme danou funkci se všemi ostatními předanými argumenty plug-inu return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if ( typeof method == 'object' || !method ) { // Pokud ne, zavoláme inicializační metodu return methods.init.apply(this, arguments); } else { // Pokud metoda neexistuje, vypíšeme chybu $.error('Metoda ' + method + ' neexistuje v plug-inu jQuery.easySlider'); } }; // Základní nastavení $.fn.easySlider.defaults = { }; })( jQuery ); // zavoláme funkce s parametrem jQuery
Vytvořili jsme si funkci easySlider()
v objektu
jQuery.fn
. Můžeme ji tedy zavolat např.:
$('ul#slider').easySlider()
.
Ale co když bude potřeba něco nastavit nebo zavolat nějakou vnitřní
funkci pluginu? Je velmi nepraktické měnit samotný kód pluginu. Tyto kódy
jsou také často minimalizovány a tak to ani nejde. Výše uvedený příklad
však počítá i s tímto. Jako argument můžeme uvést buďto objekt, ve
kterém bude všechno nastavení plug-inu, nebo jméno funkce v objektu
methods
a nebo také nic a plugin si poté sám zavolá
methods.init()
. Toto je jen jeden způsob, jak udělat logiku
volání funkcí. Dalo by se to udělat i jinak. Pro nás je však tohle velmi
praktické, a tak nemusíme nic řešit.
Naposled tu máme objekt defaults
. V něm je uloženo základní
nastavení. Je to proto, aby uživatel nemusel nastavovat vše, ale jen to, co
zrovna chce jinak.
Ještě než se vrhneme na samotný plug-in, chtěl bych ujasnit, co vlastně
dělá funkce apply()
. Každá funkce v JavaScriptu má nějaký
svůj kontext, ve kterém se zpracovává, jak již jsem zmínil výše. Funkce
apply()
zavolá danou funkci s kontextem, jaký si zvolíme. Má
celkem dva argumenty, prvním je právě ten kontext. Ten jsme si nastavili na
this
, tedy na právě vybraný element. Druhým argumentem je
pole, ve kterém uvedeme všechny argumenty funkce. Zde
uvádíme proměnnou arguments
, což je pole všech argumentů
funkce.
Inicializační metoda
Máme základ a tak si každý prvek inicializujeme. Jediný argument bude
objekt s uživatelským nastavením. Abychom však měli jistotu, že jsou v
tomto objektu všechna nastavení, rozšíříme defaults
právě o
tento objekt. Opět uvedu celý kód funkce, který záhy rozeberu:
// Inicializační metoda - je volána z hlavní funkce init: function( o ) { // Získáme nastavené volby plug-inu o = $.extend({}, $.fn.easySlider.defaults, o); // Uložíme si aktuální kontext funkce (element plug-inu) var $that = $(this); // Funkce po kliknutí na šipky var left_click = function( e ) { e = $.event.fix(e); e.preventDefault(); $that.easySlider("prev"); }, right_click = function( e ) { e = $.event.fix(e); e.preventDefault(); $that.easySlider("next"); }; // Projdeme všechny předané elementy a inicializujeme pro ně plug-in return this.each(function() { // Najdeme všechny obrázky ve slideru var $items = $(this).find('li'), count = $items.length, $self = $(this); // Všechny je skryjeme a pozicujeme absolutně $items.css({ display: 'none', position: 'absolute' }); // Vložíme seznamu třídu pro přístupnější manipulování v CSS $self.addClass('easySlider-content').data('speed', o.speed); // Aktivujeme první element $($items.get(o.active)).addClass("active"); // Vytvoříme si postranní šipky pro posun slideru var $arrowLeft = $('<a />').attr('href', '#left').addClass('arrowLeft'); var $arrowRight = $('<a />').attr('href', '#right').addClass('arrowRight'); // Nastavíme callback na kliknutí $arrowLeft.bind('click', left_click); $arrowRight.bind('click', right_click); // Vložíme šipky před seznam slidů $self.before( $arrowRight ); $self.before( $arrowLeft ); }); }
Hned na prvním řádku je použita funkce jQuery.extend()
. Tato
funkce rozšiřuje libovolný počet objektů, kde objekty uvedeny naposled
mají větší prioritu. První argument je cílený objekt, kam chceme
výsledek uložit (více info v oficiální dokumentaci
jQuery.extend). A teď česky. Vezmeme proměnné a funkce z objektu
defaults
a pokud existuje stejnojmenná funkce či proměnná v
objektu o
, přepíšeme její hodnotu na hodnotu, která je v
objektu o
. To zajistí, že nemusíme složitě zjišťovat, zda-li
nám uživatel zadal všechny volby. Další je pomocná proměnná
$that
. Krom jednoduššího zápisu, který nám možná ušetří
desetinu sekundy, jsou ve funkci methods.init()
definovány ještě
jiné funkce s jiným kontextem a v těchto funkcích by výraz
$(this)
znamenal úplně něco jiného. Proto si ho musíme uložit
do proměnné, která bude přístupná i z těchto funkcí.
Celá funkce vrací this
, tedy opět vybrané elementy, a pro
každý tento element provede nastavení plug-inu. Pojďme si tedy kód
podrobně rozebrat. Jelikož předpokládáme, že struktura slideru bude seznam
(v HTML <ul>
a <li>
), najdeme si všechny
elementy <li>
a uložíme si je do proměnné
$items
. Dále tu máme pomocnou proměnnou, ve které je počet
elementů <li>
(tedy počet slidů) a opět pomocnou
proměnnou $self
, ve které je kontext funkce, ve které pracujeme.
Ten se totiž změnil s použitím jQuery.each
, jak jste si mohli
všimnout.
Vše jsme si již načetli do proměnných, a tak si je nastavíme. Ze všeho
nejdříve skryjeme všechen obsah a nastavíme mu absolutní pozici. To z toho
důvodu, aby se nám obsah nerozházel při přechodu. Dále pak elementu
<ul>
přidáme třídu .easySlider-content
a
nastavíme ji data-speed
na hodnotu, kterou zadá uživatel. To
uděláme proto, abychom si ji mohli v jiné funkci přečíst, ale nebudu
předbíhat.
Nyní již vytvoříme tlačítka pro přechod na další nebo předchozí
slide. Nastavíme jim danou třídu a vložíme je před slider. Přeskočil
jsem záměrně bind('click')
. Musíme totiž říci skriptu, co se
má stát po kliknutí na tyto šipky. Použijeme předem nadefinované funkce
left_click()
a right_click()
. Tyto funkce máme
nadefinovány ještě před returnem, abychom je nemuseli definovat třeba i sto
krát, což by mohlo zpomalit celý skript. V této funkci byste totiž měli
definovat jen to nejnutnější, co se musí takto provést opravdu pro každý
element. Co bych chtěl zdůraznit v těchto funkcích je použití
$.event.fix(e)
. Jak již možná víte, v některých
prohlížečích musíte přistupovat k proměnné události
(event
) jinak nebo jsou tam jinak nazvané parametry, takže si
musíme dávat pozor. Co už však málokdo ví je, že tato jQuery funkce to
zařídí za nás a my už se s tím nemusíme znepokojovat. Pak jen použijeme
e.preventDefault()
, která zamezí provedení výchozí události
(u odkazů je to přesměrování na danou adresu href
). Na konci
funkce je zavolána metoda methods.next()
přes naši volací
logiku. Synonymem pro toto volání je methods.next.apply(that);
,
kde that
je kontext funkce init()
.
Veřejné a privátní funkce
Jak už jsem říkal, všechny funkce v objektu methods jsou volatelné z
našeho pluginu a to tímto způsobem:
$(element).easySlider("nazev_funkce", ["první parametr", "druhý parametr", ...]);
(hranaté závorky naznačují jen to, že jsou tyto argumenty nepovinné - v
normálním skriptu tam nejsou!). Funkce jsou tedy veřejné. Můžeme také
vytvořit privátní funkce a to tak, že je uložíme mimo objekt
methods
, do té samé anonymní funkce. Takové funkce by byly
přístupné jen v naší anonymní funkci a zvenčí by přístupné nebyly. V
tomto případě privátní metody nepoužíváme, avšak někdy jsou velmi
užitečné.
Nyní již zbývají jen tři funkce. Jedna nastaví aktivní vrstvu a
zároveň provede animaci (tzv. slide) na základě předaného indexu. Nazveme
jí třeba active()
. Další dvě budou funkce prev()
a next()
, které vypočítají index předchozího, resp. dalšího
slidu a předají ho funkci active()
.
// Funkce nastaví aktivní slide (Index od 0 do počtu obrázků v galerii) active: function( index, direction ) { // Nastavíme rychlost speed = $(this).data('speed') || 800; // Nastavíme směry efektů directionHide = direction || 'left'; directionShow = directionHide == 'left' ? 'right' : 'left'; // Skryjeme aktivní položku $(this).find('li.active').hide('slide', { direction: directionHide }, speed); // Všem položkám odstraníme třídu .active $(this).find('li').removeClass('active'); // Načteme aktivní slide var slide = $(this).find('li').get(index) || false; // Zobrazíme ho $(slide).addClass('active').show('slide', { direction: directionShow }, speed); // Vrátíme aktivní element return $(this).find('li').get(index) || false; }, // Přesune se na další slide next: function() { // Najdeme další element a zjistíme jeho index, ke kterému přičteme +1 var index = ($(this).find('li.active').index()+1); // Aktivujeme tento slide, pokud existuje. Pokud ne, automaticky se přesuneme na první (nultý) return $(this).easySlider("active", ($(this).find('li').get(index) ? index : 0)); }, // Přesune se na předchozí slide prev: function() { var index = $(this).find('li.active').index()-1 < 0 ?$(this).find('li').length-1 : $(this).find('li.active').index()-1; // Aktivujeme slide s títo indexem return $(this).easySlider("active", index, 'right'); }, /** Metoda init ... */
$(element).easySlider("active", index, direction)
Stačí rozebrat tyto tři metody a náš plugin bude hotov. První funkce
active()
aktivuje element, který zjistíme z předaného indexu
(viz. první parametr funkce). Druhý parametr direction
je
nepovinný (buďto 'left'
nebo 'right'
) a ten určuje
směr, kam se slider posune. Funkce na zobrazení a skrytí elementu používá
jquery.effects.core.js
z knihovny jQuery UI. Proto je tento plug-in
na ní závislý. Pokud budete chtít použít jen čistě jQuery, můžete je
nahradit funkcemi fadeIn()
a fadeOut()
nebo
slideDown()
a slideUp()
a nebo prostě
animate()
, to už je na vás.
$(element).easySlider("next")
Posune slider na další element. Pokud další není, přesune se na
začátek. Volá funkci active()
. Funkce prev()
je
stejná s tím rozdílem, že se přesune na předchozí nebo poslední
element.
Pár tipů nakonec
- Minimalizujte svůj kód, ale poskytujte verze i normální (pokud tedy nechcete dělat komerční pluginy). Tak budou moci vaše pluginy dále vyvíjet a upravovat i jiní, pokud to nebude v rozporu s vámi danou licencí. Pro minimalizování kódu doporučuji skript Deana Edwardse - Packer
- Do uživatelského nastavení vložte co nejvíce věcí. Sepište pak podrobnou dokumentaci tohoto nastavení.
- Snažte se moc nenastavovat CSS styly. To nechte na externích souborech. Ve skriptu nechte jen nezbytné styly.
- Piště nevtíravý kód. Nechtějte po uživatelích, aby museli přidávat
HTML, to přece dokáže JavaScript taky
- Před vydáním otestujte svůj výtvor v co možná nejvíc prohlížečích. Pokud ho nějaký prohlížeč nepodporuje, napište to do dokumentace.
V následujícím kvízu, Kvíz - Knihovna jQuery, si vyzkoušíme nabyté zkušenosti z kurzu.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 1370x (90.17 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript