Com fer un plugin per a jQuery. Contingut tabulat en pestanyes
dimecres 22 juliol 2009Durant el meu dia a dia professional, treballo (i força) amb jQuery, una meravellosa biblioteca JavaScript (s’entreveuen les meves preferències, oi?). Doncs bé, la segona cosa que cal saber quan s’entra en el món jQuery és desenvolupar, o com a mínim conèixer l’estructura bàsica, d’un plugin.
Per a exemplificar la construcció, crearem un petit giny per a construir contingut tabulat en pestanyes.
Partim d’un fragment HTML com el següent:
<div class="pestanyes">
<h2>Pestanya 1</h2>
<div>
...contingut...
</div>
<h2>Pestanya 2</h2>
<div>
...contingut de la segona...
</div>
<h2>Pestanya 3</h2>
<div>
...i una mica més...
</div>
</div>
Si ens centrem en el giny, podríem redactar una petita explicació funcional com la següent:
- En estat de repòs, es mostren les pestanyes amb la primera activa i només es mostra el contingut relacionat amb aquesta.
- Una vegada cliquem a una pestanya, aquesta passa a ser l’activa i el contingut que es mostra passa a ser el relacionat amb aquesta.
Ara només falta traduir això a codi JavaScript.
Comencem creant l’estructura general del plugin:
$('.pestanyes').each(function() {
...
});
I les estructures necessàries per a crear el giny. Primer de tot caldrà amagar les capçaleres i el contingut d’aquestes i crear l’estructura per a les pestanyes que a nosaltres ens sigui còmode de treballar-hi. Per exemple una d’ideal seria una senzilla llista <ul><li>...</li></ul>.
Anem a passar tot això a codi JavaScript:
$('div.pestanyes').each(function () {
// creem una referència a les capçaleres i al contingut de les pestanyes
var $headings = $('h2', this);
var $content = $headings.next();
// amaguem les capçaleres i els continguts
$headings.hide();
$content.hide();
// creem l'estructura de les pestanyes
var tabHtml = '';
$headings.each(function() {
tabHtml += '<li>' + $(this).text() + '<\/li>';
});
// i l'afegim al document
$(this).prepend('<ul class="tabs">' + tabHtml + '<\/ul>');
}
Una vegada creada l’estructura, només queda marcar l’estat inicial amb la primera pestanya i el seu contingut relacionat com a actius:
var $tabs = $('ul.tabs li');
$tabs.eq(0).addClass('active');
$content.eq(0).show();
Ara només ens queda centrar-nos en la interacció amb les pestanyes. Quan un usuari faci clic en una pestanya, cal resetejar l’estat actual (eliminar la marca d’actiu) i marcar la pestanya que s’ha clicat com a activa, juntament amb el seu contingut relacionat.
$tabs.click(function() {
// eliminem la classe i amaguem el contingut
$tabs.removeClass('active');
$content.hide();
// marquem la pestanya activa i mostrem el contingut que calgui
$(this).addClass('active');
var tabIndex = $tabs.index(this);
$content.eq(tabIndex).show();
});
Si ho posem tot junt, podem veure el resultat i una aplicació a un cas a l’exemple.
Però fins aquí no hem fet res de res per construir un plugin. Posem-nos a treballar-hi.
Adaptació del codi per a ser un plugin vàlid
Quan pensem en crear un plugin jQuery, el que ens ve al cap és crear una funcionalitat reutilitzable dins del domini d’un objecte jQuery, és a dir, estendre l’objecte. Així, si ens centrem en el nostre exemple, la idea és acabar creant un codi tal que per poder crear un contingut tabulat, hàgim d’escriure simplement:
$('div.pestanyes').tabs();
Si el que volem és estendre l’objecte jQuery el que haurem de fer, igual que en qualsevol altre objecte JavaScript, és afegir una nova propietat al seu objecte prototype. Així, una cosa d’aquest tipus seria vàlid:
$.prototype.tabs = function () {
// el nostre codi
}
Però si hagués decidit fer-ho així, ja s’hauria acabat l’entrada, i aquesta no és pas la idea.
Si mirem les primeres línies de la biblioteca jQuery, podem veure la creació d’un alies força interessant:
jQuery.fn = jQuery.prototype
Així, si volem usar aquesta notació, podem ampliar directament l’objecte tot fent:
$.fn.tabs = function () {
// el meu codi
return this;
}
S’ha afegit el retorn del propi objecte per a poder permetre l’encadenament de crides:
$('#element')
.tabs()
.mesCoses();
El plugin que estem construint és força senzill, però què passaria si tinguéssim variables globals, funcions auxiliars i demés? Alguna cosa com aquesta:
var foo = 0;
$.fn.myPlugin = function () {
foo += 5;
myMethod();
return this;
}
function myMethod() {
...
}
Amb aquest escenari, algú sense gaires llums (o amb ganes de tocar la pera) podria usar el plugin des la següent forma (i des de l’espai global de noms):
var myMethod = 'bar';
$('#element').myPlugin();
Però per sort, nosaltres tenim un as sota la màniga anomenat closure. Així, podem englobar tot el codi del nostre plugin dins d’una funció anònima:
(function () {
var foo = 0;
$.fn.myPlugin = function () {
foo += 5;
myMethod();
return this;
}
function myMethod() {
...
}
}) ();
Sembla que tot ja està fet, no? Queda un últim punt. Per a assegurar la compatibilitat amb d’altres biblioteques, no podem usar l’àlies de l’objecte jQuery $ així com així, sinó que hauríem d’usar jQuery com a mètode d’accés a l’objecte. Però això no és gaire còmode, no? Tranquils! Aprofitant que tenim la funció anònima englobant el nostre codi, podem crear un àlies segur per a jQuery fàcilment passant-lo com a paràmetre d’aquesta:
(function($) {
...
})(jQuery);
Ara ja estem en condicions de crear el nostre primer plugin. Podeu veure el codi resultant a l’exemple.
Ampliacions
Una vegada que tenim el plugin funcionant, ens podem centrar en fer-lo més real. El primer que ens podem preguntar és: què és un plugin sense opcions? Doncs si en volem, només cal donar-li un parell de voltes i fet.
Considerem dues opcions de configuració:
- Pestanya activada per defecte, que serà un enter (per defecte 0) que indicarà l’index de la pestanya (
activatedIndex) - Efecte visual al canviar de pestanya, que serà el nom d’una funció (per defecte null) dins l’espai de noms de l’objecte jQuery (
animation)
I si ho afegim al codi que ja tenim:
$.fn.tabs = function (options) {
var config = $.extend({
activatedIndex: 0,
animation: null
}, options || {});
...
}
Ara el mètode tabs accepta un paràmetre que és un objecte de configuració. S’usa el mètode $.extend per tal d’estendre un objecte amb els valors per defecte amb els passats com a paràmetre.
Ara, els únics canvis que cal fer són força trivials. Pel cas de l’índex de la pestanya a activar:
// marquem la pestanya i el contingut desat a la configuració com actiu
var $tabs = $('ul.tabs li');
$tabs.eq(config.activatedIndex).addClass('active');
$content.eq(config.activatedIndex).show();
I pel cas de l’animació al canviar de pestanya:
if(config.animation) {
$content.eq(tabIndex)[config.animation]();
} else {
$content.eq(tabIndex).show();
}
I per acabar, només falta veure-ho tot ajuntat i funcionant al seu exemple corresponent.
Fonts
No creieu pas que sóc un geni. Ni molt menys. Aquesta entrada està basada en una de molt millor del meu amic Choan, el qual em va iniciar en el meravellós món del JavaScript. Només he volgut explicar-ho des de la meva òptica, usant alguna que altra alternativa de notació i amb un exemple ben diferent.
Cal deixar ben clar que això no és pas un plugin oficial ni res per l’estil. El codi el teniu disponible per tocar i jugar, però no s’acceptaran cap tipus de culpa per l’ús d’aquest en algun entorn productiu.
El que sí que s’accepten són notes, comentaris i tot el que vulgueu fer sobre el que aquí s’ha exposat.