Jak na jQuery plug-in

JavaScript jQuery Jak na jQuery plug-in

Jak na vlastní plug-in do jQuery

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 je John Resig.

Dost bylo teorie o jQuery, teď půjdeme programovat. Jak už napovída titulek článku, napíšeme si vlastní plug-in do jQuery. Určitě jste se už s nějakým takovým pluginem setkali. Volá se stejně jako jakákoli funkce v této knihovně a to například takto $(element).fun­kce(). Pluginů je na internetu spoustu, možná také proto je framewok 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í doplňek, 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. Laitsky ř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').na­zevMeFunkce(), bude v dané funkci klíčové slovo this element s id="obsah". Bude však přístupný jako objekt HTMLElement, ne jako jQuery. Pokud bychom ho chtěli použít jako objekt jQuery jednoduše uvedeme this jako parametr: jQuery(this) nebo $(this) (toto jsou totožné zápisy).

Když už víme, kam vožit náš plug-in, můžeme začít psát. Obecně je nejlepší náš doplňě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 sám zavolá methods.init. Toto je jen jedn 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 už jsem zmínil výše. Funkce apply zavolá danou funkci s kontextem, jakým 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. Tady 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 postraní š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 je 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 a první argument je cílený objekt, kam chceme výsledek uložit (víc 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, kterou nemusím nijak zvlášť komentovat. Snad jen její význam. Krom jednoduššího zápisu, který nám možná ušetří desitinu sekundy jsou ve funkci methods.init definování 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é elemnty, ale pro každý tento element provede nastavení plug-inu. Jdeme 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 pak tu máme pomocnou proměnnou, ve které je počet elementu 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řív 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. Teď už 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ž říct skriptu, co se má stát po kliknutí na tyto šipky. Použijeme předem nadefinované funcke 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 funkce 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 už 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.ap­ply(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).ea­sySlider("nazev_fun­kce", ["první parametr", "druhý parametr", ...]); (hranaté závorky naznačují jen to, že jsou tyto argumenty nepovinné - v normálním skriptu tam nejsou!). Jsou tedy veřejné. Můžeme také vytvořit prvátní funkce a to tak, že je uložíme mimo objekt methods do té samé anonymní funkce. Takové funkce budou přístupny jen v naší anonymní funkci a zvenčí přístupné nebudou. V tomto případě privátní metody nepoužívám, avšak někdy jsou velmi užitečné.

Teď už 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 buď předchozího nebo 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 activní 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)

Teď už jen rychle rozeberu 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ý je nepovinný direction (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í zavislý. Pokud budete chtít použít jen čistě jQuery, můžete je nahradit funcemi 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 není další, 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 váše pluginy dále vyvíjet a upravovat i jiní pokud to nebude v rozporu s vámi danou licení. Pro minimalizování kódu doporučuji skript Deana Edwardse - Packer
  • Zaregistrujte svůj plugin v regstru jQuery pluginů.
  • Do uživatelského nastavení vložte co nejvíce věcí. Sepiště 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 JavaScrip 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 1110x (90.17 kB)
Aplikace je včetně zdrojových kódů v jazyce javascript

 

  Aktivity (1)

Článek pro vás napsal Drahomír Hanák
Avatar
Autor v současné době studuje Informatiku. Zajímá se o programování, matematiku a grafiku.

Jak se ti líbí článek?
Celkem (7 hlasů) :
4.571434.571434.571434.571434.57143


 


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
Hra v jQuery - Střelba na terč

 

 

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

Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovědět 25.4.2012 21:18
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Drahomír Hanák:

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
albertpatera:

to by mohlo bejt fajn :D

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

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:

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_:

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_:

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
pavlik.junker:

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

 
Odpovědět 16.8.2014 13:08
Avatar
kxmx
Redaktor
Avatar
kxmx:

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