itaeng

Scrivere un semplice accordion con jQuery

Mi è recentemente capitato di dover utilizzare un accordion per un semplice menu, con poche voci, per cui non volevo utilizzare jquery-ui o plugin simili. In giro è pieno di articoli che spiegano come creare un accordion ma, vista la facilità con cui si può realizzare una cosa simile, volevo provare a cimentarmi io stesso e soprattutto volevo imparare a creare un plugin, cosa che non ho mai provato a fare.

Il risultato si può vedere su jsFiddle (un ottimo tool per esperimenti di questo tipo) a questo indirizzo: http://jsfiddle.net/WpAXA/.

Analizziamo quello che ho fatto. Tralascio la parte di Html e di Css, che sono davvero banali e mi concentro sul codice javascript:

(function($) {
    $.fn.tinyAccordion = function(options) {

    }
})(jQuery);

Questo è il wrapper che ci permette di creare il nostro plugin. Sfruttando questo, posso eseguire la funzione lanciandola in questo modo:

$('#accordion').tinyAccordion({
    'bookmark': 'h3',
    'content': 'ul'
});

Come hai visto, ho passato delle opzioni che sono 'bookmark' e 'content', rispettivamente per indicare quale elemento deve essere la parte cliccabile dell'accordion (i tag h3) e quali parti devono essere visualizzate o nascoste, a seconda di cosa clicco (i tag ul). Come fare per dire alla mia funzione "Hey! Ti ho passato delle opzioni, usa quelle!", e come fare se invece non le avessi specificate? Ci pensa questo pezzetto di codice:

// valori di default
var config = {
    'bookmark': 'h3',
    'content': 'div'
};
$this = this.selector;
if (options) $.extend(config, options);

In particolare, nella variabile config imposto i valori di default (quelli che verrebbero utilizzati nel caso in cui non li avessi specificati io), mentre l'ultima riga "unisce" gli array delle due variabili. Naturalmente quelle specificate a mano hanno la priorità sulle altre.

E la penultima riga? Quella mi serve per impostare in una variabile qual è il nome dell'elemento per cui deve essere eseguita questa funzione (#accordion, nel mio esempio).

bookmark = $($this + " " + config.bookmark);
content = $($this + " " + config.content);

Come prima, anche qua ho creato delle variabili per sapere su quali elementi devo agire. In particolare, quando javascript eseguirà questo blocco le variabili avranno come valore rispettivamente $("#accordion h3") e $("#accordion ul").

var i = 1;
bookmark.each(function() {
    $this = $(this);
    $this.attr("data-accordion-switch", "el" + i);
    $this.next(config.content).attr("data-accordion-list", "el" + i);
    i++;
});

Gli ultimi preparativi prima del via. In questo blocco cerco tutti gli elementi che corrispondono alla variabile bookmark (te lo ricordo, è $("#accordion h3")), e per ognuno creo un attributo data. Lo stesso valore viene utilizzato per il rispettivo contenuto. Questo serve per creare una corrispondenza fra un bookmark e ciò che dovrà visualizzare o nascondere.

bookmark.on('click', function() {
    $this = $(this);
    var element = $(config.content + '[data-accordion-list="'+ $this.data('accordionSwitch') +'"]');
       
    content.slideUp('fast');
    if (element.is(":hidden")) {
        element.slideDown('fast');
    }
});

Ora viene il bello: il blocco precedente è quello che si occupa di aprire o chiudere i contenuti in base al bookmark che hai cliccato. Tutto li? Tutto li!

La prima riga crea una funzione che viene lanciata ogni volta che clicco su un bookmark. Subito dopo, avrai notato che inizializzo una variabile $this. Di nuovo? Non sono impazzito: questa variabile mi serve solo all'interno di questa funzione (ed esiste solamente li) ed ha uno scopo ben preciso che non è solo la comodità come nel caso precedente: Non tutti sanno, infatti, che ogni volta che utilizzi $(elemento) viene eseguita una funzione di jQuery che scorre il DOM per trovare il riferimento all'elemento corrispondente. Se invece metto il riferimento stesso in una variabile, risparmio questo lavoro al processore. Detto in linguaggio più umano: sfruttare questa tecnica è come creare un indice ad un libro, permettendo al lettore di trovare immediatamente quello che serve senza dover scorrere tutte le pagine. In realtà la differenza in termini di tempo è piccolissima perchè i computer odierni sono veloci, ma è una buona abitudine usare questa tecnica tutte le volte che si può.

Proseguiamo: alla terza riga creo una variabile che, una volta eseguita, avrà un valore simile a $("#accordion ul[data-accordion-list=el1]"). Questo è il selettore del blocco che corrisponde al bookmark che ho cliccato.

Successivamente, eseguo uno slideUp su tutti i contenuti e, se l'elemento selezionato era nascosto al momento deli click, lo faccio visualizzare, se invece era già visualizzato lascio che la funzione precedente lo faccia chiudere.

Ecco l'accordion!

Forse hai notato che prima di chiudere la funzione c'è un 

return this;

Questo non è fondamentale nel nostro esempio, ma è un'altra di quelle cose che è buona norma inserire, quando possibile. Serve infatti a concatenare altre funzioni successivamente alla nostra. Per farti un esempio, parlo di concatenazioni tipo 

$('#accordion').tinyAccordion().azione().altrazione().css(...)

Non è poi così complicato creare un plugin che implementi un accordion, vero?