Ieri ho avuto necessità di mettere il cursore sul primo campo di una form, automaticamente all’apertura della pagina. Il primo campo però che non contenesse la classe “hasDatepicker” (un widget di jQueryUI). Tagliando qui e là alla fine credo di aver risolto con una sola riga di codice che, aldilà del suo scopo, contiene diverse cosette che vorrei condividere. Il codice (che usa jQuery) è questo:
var fi;
(fi = $('form :input').not(".hasDatepicker")) && fi.length && fi[0].focus();
In italiano si legge così: prendi tutti gli elementi input della form, elimina quelli che hanno classe “hasDatepicker”. Dei risultanti (se esistono) prendi il primo e rendilo attivo.
(se hai in mente un modo per ottimizzare ancora, ti prego di leggere la nota alla fine del post)
Cosa possiamo rilevare, di interessante, da una sola righetta di codice?
- Il one liner usa la tecnica del “corto circuito”, visto che JavaScript ce lo permette: un’espressione viene valutata da sinistra verso destra e l’elaborazione viene interrotta appena si raggiunge un valore che non può cambiare. In questo caso, la sequenza di “&&” fa sì che appena una delle tre espressioni è falsa, la valutazione viene interrotta (in questo caso quella che ci interessa in particolare è la seconda, che in pratica verifica se è stato trovato almeno un elemento).
- La prima parte dell’espressione (l’assegnamento) deve essere racchiusa tra parentesi perché altrimenti non sarebbe sintatticamente valida. In questo modo invece si forza l’interprete a valutarla in maniera atomica, so to speak. Le parentesi come “aiutino” sintattico si trovano spesso, in JavaScript.
- jQuery oltre ai selettori standard e quelli usabili anche tramite querySelectorAll, ne ha alcuni suoi molto comodi. Uno di questi è appunto “:input” che permette di selezionare tutti gli elementi input di una form ma anche select e textarea in un colpo solo.
- Ottenuta una selezione di elementi (sempre assimilabile ad un array anche se la selezione è vuota, come si vede dalla seconda e terza parte dell’espressione), è possibile filtrare via degli elementi non voluti tramite il metodo “not”. Esiste anche il metodo controparte filter(), che permette di elminare gli elementi che NON hanno corrispondenza con il suo parametro/i.
Nota: probabilmente c’è un modo ancora più corto per fare questa operazione… la prima cosa che mi viene in mente è usare l’attribute selector, in modo da scrivere qualcosa come $(“form :input[class!=hasDatepicker]:first”). Quello che non mi piace molto, in questo contesto, è che l’operatore “!=” di quel selettore, ritorna true anche quando l’attributo “class” dell’elemento non è presente del tutto, per cui la selezione potrebbe essere “prendi il primo input della form che non ha una classe” che non è esattamente quello che volevo (dovrei fare delle prove, in effetti). Inoltre, probabilmente, questo articoletto non sarebbe mai nato ;)
4 Responses to “Quello che si impara da una linea di codice”
Anche io a volte mi sono trovato a usare [0] per prendere l’oggetto DOM partendo dall’oggetto jQuery. Secondo te è safe?
In realtà sarebbe meglio usare .get(0); In questo modo prendi sicuramente l’oggetto DOM. Usare l’indice dell’array *credo* sia equivalente, ma dovrei fare delle prove…
Un input della form che non ha una classe, è comunque un input della form che non ha come classe “hasDatepicker”, quindi sarebbe corretto che ti selezionasse quello. Di fatto, è quello che fa anche la linea di codice che hai preso in esame in questo post.
Il problema di avere “class!=hasDatepicker” è piuttosto un altro. In questo modo controlli che l’attributo class non sia uguale a “hasDatepicker”, il che significa che se contiene più di una classe – tra cui “hasDatepicker” – ottieni un “true” (ovviamente “redbox hasDatepicker” non è uguale a “hasDatepicker”).
Per ovviare a questo, ti basterebbe usare come selettore:
“form :input:not(.hasDatepicker):first”
Se invece la logica dovesse essere “prendi il primo elemento input della form che ha sì una classe, ma non ha hasDatepicker” allora anche il codice originale fallisce, e dovresti cambiarlo. Usando i selettori, sarebbe qualcosa tipo:
“form :input[class]:not(.hasDatepicker):first”
Hope it helps.
Ottime (e preziose) osservazioni, zer0. Diciamo che io non ero stato troppo a sottilizzare sul primo selettore. Grazie :)
Additional comments powered by BackType