Logo JsDir.com [niente]
Bring Your Site To Life!!!
9 Maggio 2008 - 11:20:58 PM - 26 scripters on-line! - La tua posizione nel sito: StaffScripts/Script088/
BedInLecce
StaffScripts
Drop Down Menu

Autore: Riccardo Data: 25/02/2004 Downloads: 6157 Voto: 3.2/5 Download P r o v a


Drop Down Menu

      A distanza di quasi due mesi dall'ultimo script pubblicato, arriva finalmente il momento di un nuovo script.
      L'occasione è data dalla necessità di sostituire il "Menu a discesa" di Fabiano: ormai ho assodato (grazie a numerose e-mail di richiesta d'aiuto) che non vale la pena di metterci mano per modificarlo, soffre di alcuni bug che comporterebbero comunque la riscrittura.

      Incalzato da una ventina di e-mail solo negli ultimi quattro mesi, ho ripreso il mitico Menu a tendine che mi offriva già una solida e collaudata base di array e funzioni di contorno e ne ho ricavato un nuovo menu a discesa.
      Ve lo presento in questa scheda che mi ha consentito, finalmente, di riprendere a scrivere per JsDir.com come ho sempre voluto e nel modo che più mi piace: cioè non lasciare uno script da personalizzare e scopiazzare (giò lo fanno in troppi!!!), ma cercando di insegnare qualcosa e fare di uno StaffScript non solo uno script ma, sopratutto, un tutorial!
      Auguro buona lettura a chi avrà la pazienza e l'interesse a seguirmi in questo lungo pezzo, dal canto mio ci ho lavorato, alla fin dei conti, un giorno intero, ma spero che ne sia valsa la pena; come al solito se trovate qualche strafalcione sarò ben lieto di correggermi.
      Il menu fa ampio uso della struttura e degli script che molti hanno già visto per il Menu a tendine.
      Di questo infatti eredita le classi di stile usate per l'aspetto grafico dei menu, gli array per i links e buona parte delle funzioni di contorno.

      Se si confrontano i due script ci si accorge infatti che le uniche parti che hanno subito cambiamenti sostanziali, sono state le variabili di posizionamento, e le funzioni che gestivano l'apertura delle tendine, qui sostituite da un'unica funzione (DDMenuApri()); tutto il resto è pressoché immutato.

      Lo script è suddiviso in due parti: una da configurare per la personalizzazione ed una da lasciare immutata; è quest'ultima la parte "attiva" del menu ed andrebbe modificata solo se si è abbastanza esperti di JavaScript.

      Per la parte da personalizzare vi rimando alla descrizione del Menu a tendine per quanto riguarda gli array delle voci del menu, che sono praticamente indentici in tutti e due gli script. Idem per le classi del foglio di stile che andranno configurare come qualsiasi css.
      La classe per il tag "a" è stata inserita nell'esempio a solo scopo dimostrativo, anche perché viene usata, di fatto, solo per la parte che concerne Netscape 4 in cui è necessario usare il link per eseguire l'apertura dei livelli, per tutti gli altri browser è l'event-handler onclick che si occupa della gestione del menu.

      Di tutto lo script la parte più importante è la funzione DDMenuApri() che gestisce i livelli.
      Ed è proprio questa, insieme alle variabili di contorno, che sarà oggetto della parte più importante di questa scheda.
      Vediamone il codice:
var nn=document.layers?true:false
var w3c=document.getElementById?true:false
var last=-1;

if (nn)
   {
   for (var i = 0 ; i<voci.length; i++)
      {
      document.write("<layer ... name='pr"+i+"' ...
      for (var ii = 1 ; ii < voci[i].length ; ii++ )
         document.write("<layer ... name='sc"+i+"_"+ii+"' ... 
      }
   }
else
   {
   . . . . . .
   }

var beg=nn?"document.layers":w3c?"document.getElementById(":"document.all";
var mid=nn?"":w3c?").style":".style";

function DDMenuApri(quale)
   {
   for (var i=0 ; i<voci.length ; i++ )
      {
      eval(beg+"['pr'+"+i+"]"+mid+".top=eval(top+alto*"+i+"); ")
      for (var ii = 1 ; ii < voci[i].length ; ii++)
         eval(beg+"['sc'+"+i+"+'_'+"+ii+"]"+mid+".visibility='hidden'; ")
      }
   if (last != quale)
      {
      if ( quale++ < voci.length)
         for (var i=quale ; i<voci.length ; i++ )
            eval(beg+"['pr'+i]"+mid+".top=eval(top+alto*(voci[quale-1].length+i))");
      quale--;
      for (var i = 1 ; i < voci[quale].length ; i++)
         eval(beg+"['sc'+"+quale+"+'_'+"+i+"]"+mid+".visibility='visible'    ");
      last=quale;
      }
   else
      last=-1
   }
      Le prime due variabili nn e w3c costituiscono il browser-sniffer, saranno vere (true) rispettivamente se il browser è Netscape 4 o se il browser supporta "getElementById", cioè se ci troviamo davanti a IE5 o superiore, Mozilla e derivati ed altri browser più recenti. Quando sono false tutte e due saremo in presenza di IE4.
      In base a queste due variabili, vengono costruite altre due variabili (le prime due righe in rosso): beg e mid.
      A cosa servano è presto detto, per eliminare una serie di if () {} nella funzione di gestione ho fatto ricorso ad eval() per far fare al browser il lavoro di identificazione della giusta sintassi da usarsi per identificare ed interagire con i livelli.
      Dovremmo ormai sapere che per gestire i livelli bisogna usare:
      - per Netscape 4 document.layers;
      - per Internet Explorer 4 document.all;
      - per IE5 e successivi e per Mozilla document.getElementById.
      Quest'ultimo valido anche per tutti i browser basati su Gecko.

      La funzione DDMenuApri() si occupa principalmente di rendere visibili/invisibili i livelli delle voci del menu e di spostare i livelli delle voci principali per far posto alle secondarie.
      Per un generico livello "pippo" creeremo:
      - per Netscape 4 un tag <layer name='pippo' ... >;
      - per gli altri browser: <div id='pippo' ... >
      Posto che il posizionamento deilivelli sarà assoluto come in tutti gli altri menu, avremo dunque bisogno di queste sintassi per gestire i livelli con i diversi browser:

Netscape 4Rendere visibiledocument.layers.pippo.visibility='show'
document.layers.pippo.visibility='visible' (*)
Rendere invisibiledocument.layers.pippo.visibility='hide'
document.layers.pippo.visibility='hidden' (*)
Spostare a 20px dal
margine superiore
document.layers.pippo.top=20
 
Internet Explorer 4Rendere visibiledocument.all.pippo.style.visibility='visible'
Rendere invisibiledocument.all.pippo.style.visibility='hidden'
Spostare a 20px dal
margine superiore
document.all.pippo.style.top=20
 
Altri browserRendere visibiledocument.getElementById("pippo").style.visibility='visible'
Rendere invisibiledocument.getElementById("pippo").style.visibility='hidden'
Spostare a 20px dal
margine superiore
document.getElementById("pippo").style.top=20 (**)

      Tornando alle due variabili:
var beg=nn?"document.layers":w3c?"document.getElementById(":"document.all";
var mid=nn?"":w3c?").style":".style";
      notiamo che la "beg" (che ovviamente sta per "begin") assume, sotto forma di stringa, la parte iniziale della sintassi necessaria ad identificare il livello (le porzioni in blu nella tabella), mentre l'altra variabile "mid" (che sta per "middle") assume la parte intermedia (quando c'è, per Netscape 4 è assente) evidenziata in verde nella tabella.
      Nella tabella è rimasto:
      - in nero il nome del livello;
      - in rosso la caratteristica (proprietà) vera e propria da modificare per il livello ed il valore da assegnare.

      Supponendo che il browser sia Mozilla, "w3c" sarà true, quindi avremo:
      - beg="document.getElementById(";
      - mid=").style"
      e per rendere invisibile il livello, ricordando che il nome del livello (in questo caso) ed il valore da assegnare (sempre) vanno passati come stringhe se concateniamo la stringa beg più la stringa " 'pippo' " più la stringa mid più la stringa " .visibility='hidden' " (notare l'uso che ho fatto di " e di ' ) otterremo la stringa cercata:
beg'pippo'mid.visibility='hidden'
document.getElementById('pippo').style.visibility='hidden'
      quindi la stringa: "document.getElementById('pippo').style.visibility='hidden' ".

      Passando ad eval(), che compare più volte nella funzione, sappiamo che accetta in ingresso, come argomento, una stringa che contenga una o può più qualsiasi istruzione JavaScript, la interpreta ed esegue (si comporta cioè come il browser che esegue uno script), quindi se passiamo ad eval() quella stringa ne otterremo l'esecuzione.
      Con tre righe di JavaScript (le due variabili "beg" e "mid" e l'istruzione eval() con la stringa volta per volta costruita ad arte) ci siamo risparmiati una serie di if () {} all'interno della funzione (ottenendone inoltre uno snellimento ed una maggiore leggibilità della stessa) ed abbiamo lasciato al browser il compito di scegliere da sè la sintassi di cui necessita per interagire con i livelli.
      Insomma il browser fa il lavoro sporco, come qualsiasi macchina. Il che è esattamente ciò per cui le macchine sono nate a fare.
      Commentare queste tre righe di codice non è stato facile, e spero di essere stato comprensibile, ma come si vede ne vale ampiamente la pena, basta confrontare una porzione della funzione DDMenuApri() (v. le due righe in verde) con una funzione (ad esempio la coloratutti() del menu a tendina) che anche agisce su n livelli per rendersi conto di quanto è più leggibile la DDMenuApri(): senza il giochetto di stringhe e l'uso di eval() sarebbe stato necessario scrivere una funzione, o un bel gruppo di righe di codice all'interno della DDMenuApri(), per ogni occorrenza di eval() nella funzione, cioè per ben quattro volte!!!

      C'è un'altra chicca, sempre in questo menu e sempre nella parte evidenziata in verde, che voglio evidenziare: un eval() dentro l'altro!
      Riprendiamo la riga:
eval(beg+"['pr'+i]"+mid+".top=eval(top+alto*(voci[quale-1].length+i)) ");

      che si riferisce al posizionamento dei livelli principali (proprietà "top" dei livelli il cui id inizia per "pr" [v. dopo]).
      A sinistra del segno di "=" abbiamo la concatenazione delle stringhe che abbiamo appena visto,mentre a destra un altro eval() che determina la posizione verticale dell'i-esimo livello in base alla variabile "top" (distanza del menu dal margine superiore del browser), alla "alto" (altezza in px dei livelli) e numero di livelli principali fino a i-1.
      L'interprete JavaScript esegue l'eval() esterno ricavando la stringa che identifica il livello, durante l'esecuzione di questo si ritrova un altro eval() da eseguire per ricavare il valore (sempre una stringa!!! notate i doppi apici in rosso!!!) che ovviamente eseguirà prima di terminare l'esecuzione di quello esterno
      L'interprete ovviamente non fa una piega... noi abbiamo risparmiato almeno un'altra riga di codice ed un paio di { e }... e pensare che c'è chi pensa che JavaScript serva solo a validare i form! :-)

      Tornando allo script (era ora!) ci eravamo lasciati su var w3c= ... .
      La riga successiva è una variabile di stato che servirà alla funzione DDMenuApri() per capire se c'è un menu principale aperto, subito dopo un if () che tramite document.write() scrive nel nostro documento HTML i necessari tag per i livelli usando LAYER per Netscape4 (porzione che ho riportato in questa scheda) e DIV per tutto il resto (v. il sorgente, qui ho lasciato i puntini di sospensione per non complicare le cose).

      Il ciclo principale viene eseguito per la lunghezza dell'array voci quindi per ogni voce principale.

      Il primo document.write() scrive i livelli per le voci principali: name='pr"+i+"' quindi scriverà nel documento "pr0", "pr1", "pr2" e "pr3" (nell'esempio ci sono 4 voci principali), subito dopo un altro ciclo for () conta e scrive gli elementi di ogni voce principale, quindi gli elementi secondari, e questa volta abbiamo name='sc"+i+"_"+ii+"' ; ad esempio scriverà "sc0_0", "sc0_1, "sc0_2"... La prina cifra (0) viene dal contatore i (ciclo esterno: livelli principali), la seconda dal contatore ii (ciclo interno: livelli secondari), il carattere di underscore è necessario per evitare che i valori di i e di ii vengano sommati piuttosto che concatenati.
      Alla fine dei cicli, ritroveremo scritto nel codice HTML del nostro documento un livello per ogni voce del menu (per gli altri browser invece di "name" si usa "id" ma la sostanza non cambia) ed ogni livello è univocamente determinato e individuabile dall'interprete.
      Nei document.write() viene anche predefinita la posizione (proprietà "left" e "top"), ma non ci interessa discuterne, sono invece molto importanti quei due suffissi: "pr" e "sc" che ritroviamo in tutti gli eval() della funzione DDMenuApri().
      È arrivato infatti (finalmente!) il momento di vedere come funziona.

      Notiamo subito che accetta un parametro ("quale") che è l'identificativo del menu da aprire: quale assumerà valori da 0 a voci.length-1 individuando univocamente uno dei livelli principali, valore che viene scritto direttamente dai document.write().
      La funzione inizia ciclando in tutti gli array e:
      - riporta alla posizione originaria ( top+alto*i, v. il sorgente) i livelli principali tramite il primo eval();
      - nasconde tutti i livelli secondari tramite il secondo eval().

      Poi controlla se il livello cliccato (quale) è diverso dall'ultimo aperto: abbiamo detto poco fa che la last identifica l'ultimo livello che è stato aperto (ed in tal modo viene settata dalla funzione nella penultima assegnazione), altrimenti segnala che non c'è alcun livello aperto (ha valore -1 come da assegnazione iniziale o per assegnazione della stessa funzione nell'ultima riga: l'else dell'if() in oggetto.

      Bene: se last != quale allora si è cliccato: o su un menu diverso da quello aperto, oppure su un menu mentre non ce n'è nessun altro menu aperto. In ogni caso nell'istante in cui la if() viene eseguita nessun menu è aperto (sono stati appena chiusi tutti dal ciclo precedente) quindi il livello cliccato è da aprire.
      La funzione incrementa di 1 unità la variabile quale (quale++) e controlla che non corrisponda all'ultima voce principale. Se non si tratta dell'ultima voce principale sposta in basso (cominciando da quale che a questo punto è il livello succesivo grazie all'incremento) tutti i livelli successivi di quel tanto che basta ad aprire il livello cliccato: cioè di voci[quale-1].length quindi della lunghezza dell'array cliccato: ergo del numero di sottovoci da visualizzare più uno (grazie al length che conta il numero di elementi dell'array). Ovviamente se invece si tratta dell'ultimo menu, niente dev'essere spostato, da qui il controllo.

      Fatto questo quale viene di nuovo decrementata (quale--) per lavorare sull'array di voci secondarie corrispondenti alla voce cliccata, ed il ciclo successivo si occupa di rendere visibili le relative voci.
      Il menu a questo punto è aperto, alla last viene assegnato il valore di quale e la funzione termina.

      Notate che abbiamo ottenuto, senza scrivere codice ad hoc, anche la chiusura del menu.
      Infatti la funzione inizia sempre chiudendo tutto il menu, ed aprendo un livello solo se si è cliccato su un livello diverso dall'ultimo cliccato (il "last" appunto): per cui se in un dato istante si è cliccato (ad esempio) sul livello 2 mentre last valeva -1 o un altro valore diverso da 2, la funzione chiude tutto (primo ciclo) ed apre il livello 2 (perché last è diverso da quale). Al termine assegna a last il valore 2.
      Se clicchiamo di nuovo sul livello 2 la funzione inizia di nuovo chiudendo tutto, ma non apre niente perché la condizione last != quale non è verificata e, per l'esecuzione dell'else, a last viene assegnato di nuovo il valore -1 di partenza.
      Il ciclo così ricomincia dalla situazione iniziale.

      E questo è quanto.
      Le funzioni ed il codice usato sembrerebbero poca cosa a prima vista (ho commentato a malapena 800 byte di codice), ma sembra inverosimile racchiudano invece tanta complessità e tanti automatismi.
      Questo è il modo in cui mi piace codare JavaScritpt, spero che apprezzerete questo mio modo di fare e mi perdonerete, di conseguenza, per il tempo che faccio passare fra un aggiornamento e l'altro del sito.


Note:
(*) Netscape 4: anche se "hide" e "show" sono i valori corretti da usare per settare la visibilità dei layers, "visible" e "hidden" possono comunque essere usati quando lo stato viene variato via JavaScript. "hide" e "show" vanno obbligatoriamente usati solo se lo stato viene impostato in un foglio di stile.

(**) per Mozilla e gli "altri" in generale: il valore per la posizione andrebbe espresso, a rigore, sotto forma di stringa e comprendendo l'unità di misura (ad esempio ...style.top='20px'). Il valore numerico e l'omissione dell'unità di misura non generano comunque errori ed il browser assume come "sottinteso" che il valore (numerico) passato sia espresso in pixel.



Per navigare correttamente il sito è necessario abilitare i cookies.
Enjoy Open Source!
th80_1

    javascript
 
 
 
         Stampa
          Segnala
          Preferiti





thb1202