Jak na vlastní jQuery plug-in

JavaScript jQuery Jak na vlastní jQuery plug-in American English version English version

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

jQuery je jedním z nejoblíbenějších javascriptových frameworků vůbec. Zprvu to byla jen knihovna, která měla poskytovat CSS selektory do jazyka JavaScript, ale poté co Dean Edwards vydal svou výbornou knihovnu cssQuery, se vývoj jQuery posunul trošku jiným směrem. Autorem knihovny jQuery je John Resig.

Dost bylo teorie o jQuery, teď půjdeme programovat. 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 = $('&lt;a /&gt;').attr('href', '#left').addClass('arrowLeft');
                var $arrowRight = $('&lt;a /&gt;').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).ea­sySlider("acti­ve", 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).ea­sySlider("nex­t")

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.

 

Stáhnout

Staženo 1196x (90.17 kB)
Aplikace je včetně zdrojových kódů v jazyce javascript

 

 

Článek pro vás napsal Drahomír Hanák
Avatar
Jak se ti líbí článek?
9 hlasů
Autor v současné době studuje Informatiku. Zajímá se o programování, matematiku a grafiku.
Miniatura
Předchozí článek
Stylování v jQuery
Miniatura
Všechny články v sekci
Základy jQuery
Miniatura
Následující článek
JavaScript zdrojákoviště - jQuery
Aktivity (4)

 

 

Komentáře
Zobrazit starší komentáře (4)

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:25.4.2012 21:18

Ano

Odpovědět 25.4.2012 21:18
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Drahomír Hanák:25.4.2012 21:19

JavaScript je zpracovávaný prohlížečem stejně jako HTML, a proto musí být použit zároveň s HTML. To jestli pak použiješ PHP nebo jiný serverový jazyk už je jedno, pokud se ptáš na tohle :)

 
Odpovědět 25.4.2012 21:19
Avatar
albertpatera
Redaktor
Avatar
 
Odpovědět 25.4.2012 21:27
Avatar
albertpatera
Redaktor
Avatar
albertpatera:6.5.2012 11:34

to by mohlo bejt fajn :D

 
Odpovědět 6.5.2012 11:34
Avatar
Xin_
Člen
Avatar
Xin_:19.1.2014 11:13

kam co přidat aby se po nějaké době sám přesouval?

Odpovědět 19.1.2014 11:13
Zvědavost nás žene vpřed :)
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Odpovídá na Xin_
Drahomír Hanák:19.1.2014 11:57

Javascript má funkci setTimeout, která zavolá funkci v prvním parametru za danou dobu. To ale nebylo cílem článku. Jestli chceš nějakou hotovou komponentu, zkus hledat na internetu. Jsou jich stovky ;)

 
Odpovědět  +1 19.1.2014 11:57
Avatar
Xin_
Člen
Avatar
Odpovídá na Drahomír Hanák
Xin_:19.1.2014 12:55

hledal jsem, ale většina siderů co sem našel byla hrozně složitá, a já hledal co nejvíce jednoduchou, přesně tu co si udělal ty, jen aby se to i samo pohybovalo

Odpovědět 19.1.2014 12:55
Zvědavost nás žene vpřed :)
Avatar
Xin_
Člen
Avatar
Xin_:19.1.2014 12:57

ono totiž já moc JS zatím nerozumím :D takže čím jednodužší kód tím líp

Odpovědět 19.1.2014 12:57
Zvědavost nás žene vpřed :)
Avatar
Regedin the Immortal:16.8.2014 13:08

Tohle se mi opravdu zalíbilo. Je to skvělá inspirace.

 
Odpovědět 16.8.2014 13:08
Avatar
kxmx
Redaktor
Avatar
kxmx:7.11.2014 9:28

Filozofická otázka ohledně html konstrukce pluginů. Můj plugin potřebuje asi takové html

<label for="nejaky-id">Popisek</label>
<div id="nejaky-id">
<input type="text" class="plugin-value">
<table class="plugin-options"></table>
</div>
  • nějaký další bordel. Jaký je správný postup? Nechat uživatele napsat celou html konstrukci nebo jí uvnitř vytvořit sám s tím, že pak je použití jen

<div id="nejaky-id"></div>
$("#nejaky-id").plugin({})

Někde jsem četl nenuťte lidi psát html, to umí js také a hned potom zase, že matlání html v pluginu je barbarství.

 
Odpovědět 7.11.2014 9:28
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 14. Zobrazit vše