IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 = $('&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.

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 1360x (90.17 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

Předchozí článek
Stylování v jQuery
Všechny články v sekci
jQuery - Dynamické doplňky webu snadno a rychle
Přeskočit článek
(nedoporučujeme)
Kvíz - Knihovna jQuery
Článek pro vás napsal Drahomír Hanák
Avatar
Uživatelské hodnocení:
73 hlasů
Autor v současné době studuje Informatiku. Zajímá se o programování, matematiku a grafiku.
Aktivity