C'è un Ouroboros (che in questo caso, tralasciando parzialmente il suo simbolismo tradizionale, sta a raffigurare la creatività, che sempre rinasce e si rinnova) da cui si originano i tre link principali dedicati alle tre aree creative principali di cui mi occupo, più un link informativo. Stavolta non si divaga!
I link non aprono altre pagine, bensì delle finestre sullo stile di quelle dei sistemi operativi, che si utilizzano per esplorare il contenuto del proprio computer.
Le caratteristiche da riprodurre sul web di tali finestre sono:
- Trascinabilità, tramite il classico click+drag del mouse;
- Sovrapponibilità, con possibilità di portare "di fronte" una finestra che sta sotto ad un'altra, cliccandola;
- Ridimensionabilità, trascinandone uno dei bordi o uno degli angoli.
Vediamo nel dettaglio il markup XHTML, il CSS e il codice jQuery necessari per realizzare questo progetto.
1. Codice XHTML
Ciascuna finestra avrà una struttura di questo tipo:
<div class="window" id="finestra1"> <span class="close">X</span> <h2>titolo</h2> <p>contenuto</p> </div>
La classe window serve per specificare gli attributi essenziali comuni ad ogni finestra, mentre l'ID univoca per le caratteristiche variabili, come sfondo e posizione iniziale.
Lo span "close" non è altro che il pulsante di chiusura della finestra, che realizzeremo interamente con jQuery.
Per lanciare le finestre abbiamo bisogno di un elenco di link, ad esempio:
<ul> <li><a id="a1" class="btn" href="#">finestra1</a></li> <li><a id="a2" class="btn" href="#">finestra2</a></li> <li><a id="a3" class="btn" href="#">finestra3</a></li> </ul>
Vediamo adesso quali sono le caratteristiche fondamentali da attribuire ai blocchi finestra tramite CSS.
2. Proprietà CSS
Quello che segue è il CSS che ho utilizzato sul mio sito:
.window{ z-index: 10; position: absolute; width: 500px; height: 400px; padding: 0; color: #000; opacity: .95; filter: alpha(opacity=95); border: 3px solid #555; -moz-border-radius-topright: 12px; -webkit-border-top-right-radius: 12px; border-top-right-radius: 12px; -moz-border-radius-bottomleft: 12px; -webkit-border-bottom-left-radius: 12px; border-bottom-left-radius: 12px; }
Di tutto questo, le uniche proprietà realmente fondamentali sono position: absolute e z-index. Quest'ultima ci risolverà il problema dell'impilazione delle finestre.
È interessante sfruttare delle capacità avanzate come la trasparenza e i bordi stondati, che migliorano molto l'estetica con un minimo sforzo.
La trasparenza è bene sia minima per non confondere i contenuti con lo sfondo. Riguardo ai bordi stondati, ne accenno in quest'altro post.
Ciascuna finestra, poi, avrà delle caratteristiche personali, come la posizione iniziale all'apertura, lo sfondo, le dimensioni, e quant'altro; ad esempio:
#finestra1{ background: #fff url(images/bg1.gif) bottom right no-repeat; width: 400px; top: 120px; left: 40px; }
Per avere una disposizione elegante delle finestre l'una rispetto alle altre, ho scalato di 40px per volta la posizione di ciascuna finestra:
#finestra1{ top: 120px; left: 40px; } #finestra2{ top: 80px; left: 80px; } #finestra3{ top: 40px; left: 120px; }
Sono andato "verso l'alto" per ridurre rischi di comparsa di scrollbar con dimensioni ridotte della finestra del browser.
Per il pulsante di chiusura delle finestre, ho usato l'immagine di una "X", con questo CSS:
.close{ cursor: pointer; position: absolute; top: 3px; right: 3px; background: transparent url(images/close.png) center center no-repeat; height: 25px; width: 25px; display: block; text-indent: -999em; }
I titoli delle finestre sono stati tutti trattati con una tecnica qualsiasi di image replacement per renderli conformi allo stile generale della pagina.
Adesso si deve stendere il codice jQuery per consentire l'azionamento delle finestre, e conferirgli tutte le caratteristiche d'interattività desiderate.
3. Codice jQuery
3.0 Preliminari: nascondere e pulsante chiusura
A monte di tutto, si devono nascondere le finestre:
$('.window').hide();
Poi, diamo funzionalità al pulsante di chiusura:
$('.close').click( function(){ $(this).parent('.window').hide('normal'); } );
3.1 Attivazione delle finestre e funzione "on top"
L'idea è questa: cliccando su un link si apre la finestra corrispondente, ma se essa è già visibile, viene chiusa (i link sono degli switch, in sostanza).
Poiché in seguito renderemo le finestre trascinabili, la loro posizione finale potrà essere differente da quella iniziale, ma io voglio che esse si aprano sempre nello stesso punto deciso sopra tramite CSS.
Inoltre, l'ultima finestra attivata deve passare in cima a tutte le altre.
La prima cosa da fare è capire quale finestra vuole attivare l'utente, e salvarne l'ID in una variabile per identificare la finestra "attiva".
/* window popups launch */ $('.btn').click(function(){ var active = '#finestr' + $(this).attr("id"); });
Ogni volta che si clicca su un link, nella variabile active si va a memorizzare l'ID della finestra corrispondente, con tanto di "#", perché utilizzeremo subito questa variabile a fini di selezione.
Ad esempio, cliccando sul secondo link (ID = a2) avremo: #finestr + a2 = #finestra2.
Adesso, controlliamo se la finestra è già visibile. Se lo è, la nascondiamo, e finiamo lì. Altrimenti ci sono due cose da fare prima di mostrarla:
- Resettare la sua posizione a quella iniziale;
- Attribuirgli uno z-index maggiore di tutte le altre finestre per farla andare "on top".
Ecco come si fa:
/* window popups launch */ $('.btn').click(function(){ var active = '#finestr' + $(this).attr("id"); if($(active).is(':visible')){ $(active).hide('normal'); } else { switch(active){ case '#finestra1': $(active).css({ 'top' : '120px', 'left' : '40px' }); break; case '#finestra2': $(active).css({ 'top' : '80px', 'left' : '80px' }); break; case '#finestra3': $(active).css({ 'top' : '40px', 'left' : '120px' }); break; } $('.window').not(active).css('z-index', '10'); $(active).css('z-index', '100').show('normal'); } });
Il ciclo switch - case serve per assegnare istruzioni diverse a seconda della finestra, ed è qui che avviene il reset alle condizioni iniziali (che possono anche coinvolgere più proprietà oltre alla posizione).
Le ultime due istruzioni servono per mandare in cima la finestra cliccata e visualizzarla.
Si attribuisce lo z-index di default a tutte le finestre che non siano quella cliccata, poi si dà indice massimo a quella attiva e infine la si visualizza.
Per mandare in cima una finestra già visibile che sia sotto ad altre, basta scambiare i valori di z-index tra le finestre alla pressione del pulsante del mouse:
/* on top windows */ $('.window').mousedown( function() { $(this).css('z-index', '100'); $('.window').not(this).css('z-index', '10'); } );
L'evento è bene che sia mousedown (esatto, senza l'"on"!) perché ci tornerà utile in congiunzione con l'effetto di trascinabilità che implementeremo subito.
3.2 Trascinabilità
Qui siamo fortunati, perché la trascinabilità è un effetto già presente nella libreria jQuery UI: basta aggiungere due file nello <head> della pagina web:
<script src="http://jqueryui.com/latest/ui/ui.core.js" type="text/javascript"></script> <script src="http://jqueryui.com/latest/ui/ui.draggable.js" type="text/javascript"></script>
Poi, per rendere le finestre trascinabili, è sufficiente questa dichiarazione:
$('.window').draggable();
3.3 Ridimensionabilità
Io non ho implementato la ridimensionabilità sul mio sito, perché nel mio caso superflua e necessitante un altro approccio di design. Però anche per questa proprietà il passo è breve: basta allegare i file necessari, e dichiarare le finestre "resizable".
I file da aggiungere in questo caso sono:
<link type="text/css" href="http://jqueryui.com/latest/themes/base/ui.all.css" rel="stylesheet" /> <script src="http://jqueryui.com/latest/ui/ui.core.js" type="text/javascript"></script> <!-- già visto sopra --> <script src="http://jqueryui.com/latest/ui/ui.resizable.js" type="text/javascript"></script>
A questo punto, conviene combinare tre istruzioni in una, e dichiarare subito all'inizio dello script:
$('.window').draggable().resizable().hide();
Adesso si potranno trascinare i bordi delle finestre per ridimensionarle, e comparirà (nell'angolo in basso a destra) una piccola icona il cui trascinamento ridimensionerà la finestra lungo i due assi X e Y contemporaneamente - come una qualsiasi finestra da sistema operativo.
4. Script completo
$(document).ready(function(){ $('.window').draggable().resizable().hide(); /* close button */ $('.close').click( function(){ $(this).parent('.window').hide('normal'); } ); /* window popups launch */ $('.btn').click(function(){ var active = '#finestr' + $(this).attr("id"); if($(active).is(':visible')){ $(active).hide('normal'); } else { switch(active){ case '#finestra1': $(active).css({ 'top' : '120px', 'left' : '40px' }); break; case '#finestra2': $(active).css({ 'top' : '80px', 'left' : '80px' }); break; case '#finestra3': $(active).css({ 'top' : '40px', 'left' : '120px' }); break; } $('.window').not(active).css('z-index', '10'); $(active).css('z-index', '100').show('normal'); } }); /* on top windows */ $('.window').mousedown( function() { $(this).css('z-index', '100'); $('.window').not(this).css('z-index', '10'); } ); });
Nota sull'impilabilità
Il semplice metodo proposto per portare "on top" una finestra ha il pregio dell'estrema concisione: due brevissime istruzioni e siamo a posto qualunque sia il numero delle finestre.
Però questa semplicità tralascia un dettaglio: l'ordine delle finestre in secondo piano. Esso infatti non rimane inalterato, ma si ripristina a quello di default a cascata del codice HTML: in sostanza, tra le finestre in secondo piano, l'ultima in ordine di inserimento nel codice della pagina sarà quella che starà sopra a tutte, direttamente sotto a quella "on top".
Ciò accade perché attribuiamo a tutte le finestre in secondo piano lo stesso z-index, a parità del quale vige la regola dell'ordine nel codice.
La soluzione è concettualmente semplice: determinare di volta in volta qual è la finestra con z-index più elevato ed attribuire a quella da mandare "on top" uno z-index maggiorato di una unità, lasciando tutti gli altri inalterati.
Questo però richiede un po' più di scripting, perché si deve iterare tra gli attributi delle finestre ed eseguire le necessarie operazioni.
Magari lo farò nei prossimi giorni...