Categorie
JQuery

Creare plugin jQuery

JQuery ci offre la possibilità d essere esteso in maniera piuttosto semplice, attraverso la creazione di plug-in personalizzati. Estendendo la libreria, possiamo sfruttarne il codice di base esistente che jQuery mette a disposizione per noi. Ad esempio, creando nuovi metodi (metodi wrapper), si eredita automaticamente l’uso del potente meccanismo dei selettori jQuery.
Le estensioni di jQuery possono essere fondamentalmente di due tipi:

  1. Funzioni utilità definite direttamente sul $ (alias per jQuery).
  2. Metodi per operare su un wrapped set di jQuery.

Regole di denominazione dei files

Il team di jQuery ci fornisce delle semplici linee guida per evitare conflitti di denominazione:

  • Prefissare il nome del file con jquery.
  • Seguire con il nome del plugin.
  • Facoltativamente, inserire i numeri delle versione del plugin.
  • Concludere con .js.

Per esemio, se volessimo creare un plugin di nome “pippo” il nome del file potrebbe essere: jquery.pippo-1.0.js.

Attenzione: questo per plugin creati per uso personale potrebbe essere sufficiente, ma nel caso li volessimo rendere “pubblici” e quindi disponibili per la comunità, dobbiamo prendere qualche precauzione in più, del resto potrebbe esserci già qualcun’altro che ha creato un plugin di nome “pippo“. In questo caso, oltre a tenere sotto controllo la sezione ufficiale dedicata alle estensioni plugins.jquery.com, sarebbe utile sub-prefissare il plug-in con una nostra sigla o con quella della nostra organizzazione: jquery.miaOrg.pippo-1.0.js

Le stesse precauzioni andrebbero prese con i nomi delle funzioni e dei metodi. A volte un plug-in che nasce privato potrebbe rivelarsi molto utile e ben fatto da spingerci a condividerlo con la comunità. E’ bene quindi prendere subito le giuste precauzioni per evitare possibili conflitti.

Occhio al $

Come noto, un’altra possibile fonte di conflitti, (questa volta con librerie diverse vedi Prototype) potrebbe essere l’alias di jQuery e cioè il $. Visto che non possiamo sapere se l’utente che implementerà il nostro plugin intende utilizzare il jQuery.noConflict() e non vogliamo rinunciare alla comodità del $, possiamo usare una tecnica comune a tutti gli sviluppatori di plugin. Questo idioma è il seguente:

(function($){
  /* plugin body */
})(jQuery);

A prima vista questa notazione potrebbe sembrare più “oscura” di quello che invece è.
Nella prima parte:

(function($) { 
    /* plugin body */ 
})

viene dichiarata una funzione che viene racchiusa tra parentesi tonde, creando un’espressione su essa. Questo causerà che il riferimento ad una funzione anonima viene ritornato come valore dell’espressione. La funzione si aspetta un singolo parametro, che è il nome $; tutto ciò che è passato alla funzione può essere referenziato attraverso l’identificatore $ all’interno del corpo della funzione. Visto le dichiarazioni di parametri hanno la precedenza su qualsiasi nome simile di portata globale, qualsiasi valore definito per $ al di fuori della funzione è sostituito all’interno della funzione da parte l’argomento passato.
La seconda parte dell’idioma:

(jQuery)

esegue una chiamata alla funzione anonima passando l’oggetto jQuery come argomento.
Come risultato, l’identificatore del $ si riferisce all’oggetto jQuery all’interno del corpo della funzione, indipendentemente dal fatto che è già definito da Prototype o qualche altra libreria al di fuori della funzione.

Gestione dei parametri

Quando i plugins sono semplici e richiedono pochi parametri non abbiamo molti problemi, ma quando il loro numero aumenta, e molti di questi risultano essere facoltativi, allora è necessario trovare un modo per gestire la situazione.
Pensiamo ad una firma di funzione piuttosto complessa come la seguente:

function myFun(p1, p2, p3, p4, p5, p6) {

Supponiamo che solo il primo parametro è necessario, cosa succede se volgiamo passare anche il sesto? Siamo costretti ad utilizzare dei segnaposto per gli eventuali parametri omessi:

myFun(v1, null, null, null, null, v6);

e se si volesse passare il primo, terzo e sesto parametro?

myFun(v1, null, v3, null, null, v6);

in questo modo lo sviluppatore è costretto a inserire accuratamente i null per i parametri opzionali. Rendendo il codice più complicato da leggere oltre che soggetto ad errori.
La soluzione è riunire i parametri opzionali in un oggetto, in cui le proprietà fungono appunto da parametri.

/* nuova firma */
myFun(p1,options);

/* chiamate */
myFun(v1, {p6: v6});
//oppure
myFun(v1, {
    p3: v3,
    p6: v6
});

La situazione è decisamente migliorata, non siamo più obbligati a contare ed inserire i null per i parametri opzionali che non abbiamo intenzione di usare.
Ora nel corpo della funzione prevediamo dei valori di default per tutti i parametri opzionali, che andremo a fondere con quelli eventualmente passati (options) con la funzione utilità jQuery.extend():

function myFun(p1,options) {
    var settings = $.extend({
        option1: default1,
        option2: default2,
        option3: default3,
        option4: default4,
        option5: default5
    },options||{});
    // resto della funzione...
}

ci troveremo così con la variabile settings che conterrà un oggetto le cui proprietà con i valori di default saranno sostituite da qualsiasi valore esplicito specificato dallo sviluppatore.

Nota: da notare che ci guardiamo da un oggetto options null o undefined con ||{} che fornisce un oggetto vuoto se options restituisce false.

Aggiungere funzioni utilità

Si tratta di funzioni definite come proprietà di jQuery (e quindi $). Visto che non operano su un wrapped set (l’array di oggetti, cioè elementi del DOM, restituito dalla funzione $()) probabilmente non troveranno molto successo nei nostri scripts, tuttavia possono rappresentare una buona introduzione all’argomento. Le funzioni jQuery.noConflict() e jQuery.extend() viste in precedenza ne rappresentano un esempio.
Possiamo aggiungere una funzione come proprietà di jQuery molto semplicemente, dichiarando una funzione ed assegnandola ad una proprietà dell’oggetto:

$.strReverse = function(str) {
    splitext = str.split("");
    revertext = splitext.reverse();
    reversed = revertext.join("");
    return reversed;
};

document.write($.strReverse('ciao mondo'));

Nell’esempio abbiamo creato una banale funzione che inverte una stringa, tuttavia non dobbiamo dimenticare quanto detto in precedenza riguardo alla possibilità di conflitti con il $. Difatti se il plugin fosse implementato su una pagina che usa Prototype senza l’utilizzo del jQuery.noConflict() avremmo creato un metodo su Prototype.
La soluzione come detto è non usare il $:

jQuery.strReverse = function(str) {
/* ... */

tuttavia per funzioni complesse che usano al loro interno altri metodi jQuery, rinunciare al $ risulta estremamente scomodo, quindi la soluzione più idonea risulta essere quella di avvolgere la dichiarazione all’interno dell’idioma visto in precedenza:

(function($){
    $.strReverse = function(str) {
        splitext = str.split("");
        revertext = splitext.reverse();
        reversed = revertext.join("");
        return reversed;
    };
})(jQuery);

Il team di jQuery invita caldamente ad usare questo modello, anche se può risultare eccessivamente prolisso per funzioni banali (come questa), ci garantisce di poter successivamente estendere la funzione senza preoccuparci del $.
Abbiamo creato la nostra prima estensione di jQuery. 🙂

Aggiungere metodi wrapper

La vera forza di jQuery è tuttavia operate sugli elementi del DOM sfruttando la potenza dei suoi selettori. Ora vedremo la modalità più interessante dell’estensione della libreria, cioè la possibilità di aggiungervi metodi che avranno come obiettivo un wrepped set.
Per aggiungere i metodi dobbiamo assegnarli come proprietà di un oggetto denominato fn, quindi a differenza delle funzioni di utilità, creiamo il nuovo metodo wrapper come una proprietà di $.fn piuttosto che di $.
Come primo esempio vediamo un semplice metodo che imposta il font in grassetto per degli elementi del DOM a cui è applicato:

(function($){
    $.fn.makeItBold = function() {
        return this.css('font-weight','bold');
    };
})(jQuery);

Da notare che, a meno che la funzione sia destinata a restituire un valore specifico, si dovrebbe sempre restituire il wrapped set come valore di ritorno. Questo permette al nostro nuovo metodo di prendere parte ad una catena di metodi jQuery.
Inoltre all’interno del metodo il this si riferisce al wrapped set, quindi possiamo utilizzare su di esso tutti i metodi jQuery che vogliamo. Nell’esempio abbiamo applicato il metodo css() su tutto il wrapped set, ma se volessimo gestire singolarmente gli elementi, allora dobbiamo ricorrere alla funzione each() che ci permette di iterare su ogni singolo elemento del wrapped set:

(function($){
    $.fn.makeItBold = function() {
        return this.each(function(){
            $(this).css('font-weight',$(this).attr('class') == 'current' ? 'bold' : 'normal');
        });
    };
})(jQuery);

Nell’esempio non abbiamo fatto altro che scorrere singolarmente gli elementi del wrapped set in modo da verificarne l’attributo class.

Nota: avremmo potuto ottenere lo stesso risultato anche senza l’uso dell’ each() ma passando semplicemente una funzione come valore del metodo css(), in questo modo la funzione sarà chiamata su tutti gli elementi del wrapped set:

(function($){
    $.fn.makeItBold = function() {
        return this.css('font-weight', function() {
            return $(this).attr('class') == 'current' ? 'bold' : 'normal';
        });
    };
})(jQuery);

Operazioni multiple su un metodo wrapper

Concludiamo con un esempio più completo, il metodo seguente evidenzierà il testo al passaggio del mouse. Possiamo vederlo in azione in questa pagina:

(function($){
    $.fn.highlight = function(options) {
        var settings = $.extend({
            color: 'yellow'
        },options||{});
    
        this.each(function(){
            var words = $(this).text().split(" ");
            var text = words.join("</span> <span>");
            $(this).html("<span>" + text + "</span>");
            $("span").hover(function() {
                $(this).css("background-color", settings.color);
            },
            function() {
                $(this).css("background-color", "");
            });
        });
        return this;
    };
})(jQuery);

Possiamo vedere tutto quello di cui abbiamo parlato finora, cioè le regole basilari per la preparazione di un plugin jQuery: l’uso dell’idioma per prevenire collisioni con il $, la tecnica per la gestione dei parametri, l’utilizzo della funzione each() per iterare singolarmente sugli elementi e ultimo ma non ultimo il fatto che dobbiamo cercare di restituire sempre il wrapped set per il concatenamento.

Conclusioni

A volte il codice segue dei modelli comuni, che desideriamo utilizzare più volte all’interno dei nostri progetti, senza parlare della possibilità di rendere i nostri scripts disponibili alla comunità.
In questi casi è utile catturare questi frammenti di codice riutilizzabile come estensioni di jQuery cercando di mettere in pratica poche e semplici regole che il team di sviluppo ci chiede di seguire.

Una risposta su “Creare plugin jQuery”

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.