scritto da ghiaccio il 06/06/2007
Avete mai avuto a che fare con aree di una pagina web mappate e linkate?
Siete mai riusciti ad eseguire il roll over delle varie aree senza ricorrere al solito Macromedia Flash?
Se la risposta a quest'ultima domanda è no (e si alla prima ), allora troverete molto interessante questo articolo che illustra una tecnica molto semplice per eseguire un roll over sulle aree mappate di una immagine senza aver bisogno di una quantità di memoria eccessiva per le immagini.
Naturalmente il cuore della tecnica è JavaScript e le regole dei fogli di stile CSS.
Fermi tutti però, vi anticipò subito che ci sono un paio di lati negativi ad usare questa tecnica.
1) Internet Explorer (qualcuno abolisca questo maledetto browser) gestisce male il preload delle immagini e peggio ancora gli eventi legati al mouse (la reazione è molto lenta) perlomeno fino alla versione 6, non ho ancora testato sulla 7.
2) E' necessario mappare molto bene le aree
Detto questo, inizierò illustrando i concetti principali della tecnica per poi passare alla pratica.
L'uso principale con cui si usa (e si dovrebbe) usare le immagini mappate (tag html map contenente tag area) è per le cartine geografiche in cui si linka ogni area ad una apposita pagina.L'idea è molto semplice: abbiamo l'immagine dell' area geografica a cui vogliamo applicare la mappatura e tante immagini più piccole trasparenti che corrispondano ad ogni area mappata e la porzione di tale immagine corrispondente all'area mappata, colorata con il colore desiderato.
Requisito fondamentale e che l'immagine di partenza sia trasparente in corrispondenza delle aree a cui si vuole applicare il roll over.
Nell'HTML inseriamo l'immagine mappata all'interno di un div contenitore a cui via Javascript facciamo mutare l'immagine di sfondo (proprietà CSS background-image) ogni volta che si passa su una area.
L'immagine di partenza è trasparente nelle porzioni contententi le aree mappate quindi lo sfondo del div contenitore sarà visibile e si può applicare un colore 'di default' alle parti mappate dell'immagine assegnando un colore di sfondo al div contenitore.
Il colore di sfondo sarà visibile sempre mentre al passaggio su un'area, lo sfondo del div sarà visibile comunque sopra il colore di sfondo mentre le altre aree resteranno del colore di sfondo del div.
Ho visto qualche sito che suggeriva di creare tante immagini quante sono le aree mappate, della stessa dimensione e con contenuto identico a differenza dell'evidenziazione dell'area specifica. Questo per far in modo di cambiare l'immagine originale, ad ogni passaggio del mouse su un'area, con quella corrispondente all'area su cui si sta passando con il mouse.
Questo tecnica è semplice da realizzare ma c'è un enorme svantaggio:
Se l'immagine di partenza è di 10Kb e ci sono 20 aree mappate, saranno necessarie 21 immagini pari a 210Kb!!

La tecnica che voglio illustrare è una variante di questa ed anche se è più complicata, necessita di quantità di dati da scaricare nettamente inferiore.
Tornando all'esempio precedente, con un'immagine di 10Kb e 20 aree mappate saranno necessarie sempre 20
immagini ma con dimensione sicuramente molto inferiore a 10Kb più l'originale da 10Kb.Adesso non rimane che spiegare come si fa a mostrare l'immagine di sfondo del div contenitore alla posizione giusta.
Per non stare a creare lunghe e noiosi switch contenenti i valori della posizione dello sfondo (proprietà
CSS background-position) per ogni area, si usano semplicemente le coordinate già presenti nella mappatura ovvero nella proprietà coords di ogni tag area.I valori accettabili per la proprietà CSS background-position sono 2: la coordinata X e quella Y.
Quindi bisogna scorrere le coordinate dell'area mappata e rilevare le coordinate del punto X più a sinista e di quello Y più in alto (i valori più bassi), per poi applicarli a background-position del div contenitore.
Adesso si capisce perchè è richiesto mappare molto bene ogni area (perlomeno il punto più alto e quello più basso).
Passiamo alla pratica, ecco la struttura XHTML di un esempio di cartina italiana mappata inserita in un div contenitore:
<div>
<img src="img/scheletro_italia.gif" alt="" usemap="#Map" style="border:0px;" />
<map name="Map" id="Map">
<area shape="poly" coords="119,138,131,142,139,131,132,126,122,132" href="#" alt="Molise" title="Molise" />
<area shape="poly" coords="108,109,99,117,87,108,94,96,94,89,105,98" href="#" alt="Umbria" title="Umbria" />
...............
...............
...............
<area shape="poly" coords="106,125,120,133,132,125,116,105,105,112" href="#" alt="Abruzzo" title="Abruzzo" />
<area shape="poly" coords="17,28,27,27,32,34,19,35" href="#" alt="Valle d'Aosta" title="Valle d'Aosta" />
</map>
</div>
<img src="img/scheletro_italia.gif" alt="" usemap="#Map" style="border:0px;" />
<map name="Map" id="Map">
<area shape="poly" coords="119,138,131,142,139,131,132,126,122,132" href="#" alt="Molise" title="Molise" />
<area shape="poly" coords="108,109,99,117,87,108,94,96,94,89,105,98" href="#" alt="Umbria" title="Umbria" />
...............
...............
...............
<area shape="poly" coords="106,125,120,133,132,125,116,105,105,112" href="#" alt="Abruzzo" title="Abruzzo" />
<area shape="poly" coords="17,28,27,27,32,34,19,35" href="#" alt="Valle d'Aosta" title="Valle d'Aosta" />
</map>
</div>
Iniziamo a spiegare il codice Javascript necessario.
function initializeMap(mapId)
{
area=document.getElementById(mapId).childNodes;
for(i=0;i<area.length;i++)
if (area[i].nodeType!=3) // Firefox rileva anche i nodi di testo di tipo 3 che vanno esclusi
{
preload=document.createElement('img');
preload.src='img/aree/'+(area[i].title.toLowerCase().replace(/'/g,'_').replace(/ /g,'_'))+'.gif';
area[i].onmouseover=mapRollOver;
area[i].onmouseout=function() { document.getElementById(mapId).parentNode.style.backgroundImage=''; }
}
}
area=document.getElementById(mapId).childNodes;
for(i=0;i<area.length;i++)
if (area[i].nodeType!=3) // Firefox rileva anche i nodi di testo di tipo 3 che vanno esclusi
{
preload=document.createElement('img');
preload.src='img/aree/'+(area[i].title.toLowerCase().replace(/'/g,'_').replace(/ /g,'_'))+'.gif';
area[i].onmouseover=mapRollOver;
area[i].onmouseout=function() { document.getElementById(mapId).parentNode.style.backgroundImage=''; }
}
}
Per prima cosa rileviamo tutti i tag area attraverso la proprietà childNodes dell'oggetto map a cui possiamo facilmente riferirci in quanto gli assegnamo un id.
Successivamente per ogni area eseguiamo il preload delle immagini creando semplicemente un oggetto DOM img a cui si assegna come attributo src il path dell'immagine di cui fare il preload. Il preload è già eseguito quindi l'oggetto viene riscritto per ogni immagine.
Per quanto riguarda il nome dell'immagine, ho seguito una convenzione, ovvero il nome dell'immagine (che è sempre una gif) è costituito dal contenuto del tag title dell'area, sostituendone gli spazi e gli apici con un underscore ( _ ).
preload.src='img/aree/'+(area[i].title.toLowerCase().replace(/'/g,'_').replace(/ /g,'_'))+'.gif';
Come si può vedere per entrambe le sostituzioni ho usato una espressione regolare globale (g).
Poi assegnamo ad ognuna delle aree l'evento onmouseover ad una funzione che spiegherò successivamente e ad onmouseout una semplice funzione che non fa altro che eliminare lo sfondo al div contenitore.
Nel caso in cui da una area si passi ad un'altra tutto ciò è superfluo in quanto lo sfondo verrà subito cambiato dall'altra area mentre se non si passa ad un'altra area mappata, questo fa si che non rimanga 'selezionata' l'ultima area in cui si è passati, tuttavia, questo potrebbe essere anche un effetto voluto.
In questo caso basta eliminare questa riga di codice.
area[i].onmouseout=function() { document.getElementById(mapId).parentNode.style.backgroundImage=''; }
Notare come ci si riferisce al div contenitere con il padre (parentNode) del tag map.
Passiamo adesso alla funzione fondamentale mapRollOver che gestisce il mouseover nell'area
function mapRollOver()
{
point=this.coords.split(',');
x=false;
y=false;
for(i=0;i<point.length;i=i+2)
{
if (parseInt(point[i]) < x || x===false) x=parseInt(point[i]);
if (parseInt(point[i+1]) < y || y===false) y=parseInt(point[i+1]);
}
containerDiv=this.parentNode.parentNode.style; //div contenitore
paddingLeft=containerDiv.paddingLeft!=''?parseInt(containerDiv.paddingLeft.replace('px','')):0;
paddingTop=containerDiv.paddingTop!=''?parseInt(containerDiv.paddingTop.replace('px','')):0;
containerDiv.backgroundPosition=(x+paddingLeft)+'px '+(y+paddingTop)+'px';
containerDiv.backgroundRepeat='no-repeat';
containerDiv.backgroundImage='url(img/aree/'+this.title.toLowerCase().replace(/'/g,'_').replace(/ /g,'_')+'.gif)';
}
{
point=this.coords.split(',');
x=false;
y=false;
for(i=0;i<point.length;i=i+2)
{
if (parseInt(point[i]) < x || x===false) x=parseInt(point[i]);
if (parseInt(point[i+1]) < y || y===false) y=parseInt(point[i+1]);
}
containerDiv=this.parentNode.parentNode.style; //div contenitore
paddingLeft=containerDiv.paddingLeft!=''?parseInt(containerDiv.paddingLeft.replace('px','')):0;
paddingTop=containerDiv.paddingTop!=''?parseInt(containerDiv.paddingTop.replace('px','')):0;
containerDiv.backgroundPosition=(x+paddingLeft)+'px '+(y+paddingTop)+'px';
containerDiv.backgroundRepeat='no-repeat';
containerDiv.backgroundImage='url(img/aree/'+this.title.toLowerCase().replace(/'/g,'_').replace(/ /g,'_')+'.gif)';
}
Otteniamo tutti i valori del tag coords, tramite la funzione split, in un array ed eseguiamo il ciclo per rilevare la coordinata x ed y più bassa.
Le variabili x ed y sono inizializzate a false in modo che vengano riassegnate alla prima esecuzione del ciclo con la prima coordinata.
Le due condizioni contenute nel ciclo controllano semplicemente che i valori contenuti nella x ed y di ogni coordinata siano minori di quelli nelle variabili x ed y, se ciò non è vero, le variabili x ed y vengono riassegnate.
Notare che nel tag coords di ogni area le coordinate sono a coppie, la prima è la x e la seconda è la y,quindi il ciclo va avanti di 2 coordinate alla volta.
A questo punto ci si riferisce al div contenitore con this.parentNode.parentNode, infatti this è relativo al tag area, quindi il padre del padre del tag area è il div contenitore.
Le due istruzioni servono per rilevare un eventuale padding presente nel div contenitore. Infatti se non si tiene conto del padding sinistro e superiore, il posizionamento delle immagini di sfondo è errato in quanto l'immagine mappata è spostata in basso e/o a destra mentre lo sfondo del div no.
Tali valori nelle due variabili sono aggiunti alla x ed alla y nella linea di codice successiva per compensare il posizionamento.
Naturamente questo funziona solo per valori in pixel ed inoltre viene rilevato solamente il padding applicato inline ovvero direttamente nell'attributo style del div contenitore.
Poi si imposta la proprietà background-repeat ovviamente a no-repeat e si assegna l'url dell'immagine a background-image utilizzando la stessa convenzione del contenuto dell'attributo title vista sopra.
Questo è tutto, bisogna solo avviare tutto il processo chiamando la funzione initializeMap() passandogli l'id del tag map, tipicamente al caricamento della pagina (onload):
window.onload=function() { initializeMap('Map'); }
Pubblica il tuo commento per questo articolo
Lista commenti
- Nessun risultato
Scarica esempio dell'articolo

