KISSlider: slider responsive, semantico e minimale

Ieri un amico mi ha chiesto aiuto per uno slider in jQuery: lui si appoggiava a un plugin che non capiva come funzionava (Responsive Slider, stranamente famoso). Guardando il codice non capivo come potesse servire un simile pachiderma (divisione in categorie, markup inutile, 200 righe di Javascript) per creare un semplicissimo slider di immagini.
Così mi è venuto lo stuzzichio di scriverne uno da solo, e nel giro di 20 minuti era pronto. Responsive, semantico e con circa 30-40 righe di codice in totale.
Sono rimasto stupito pure io (sono io fenomeno o loro ritardati?).

Il markup
Il markup è fatto per essere semplice, semantico e facile da scrivere anche a mano, quindi concreto e pulito.
Il vantaggio è che spesso un markup semantico è anche più human-readable e viceversa: due piccioni con una fava.
<div class="slider">
<div class="slides">
<img src="images/1.jpg" />
<img src="images/2.jpg" />
<img src="images/3.jpg" />
<img src="images/1a.jpg" />
<img src="images/1b.jpg" />
<img src="images/1c.jpg" />
<img src="images/2a.jpg" />
<img src="images/2b.jpg" />
</div>
<div class="commands">
<button class="slide-command back">&lt;</button>
<button class="slide-command next">&gt;</button>
</div>
</div>

Il div slider fa da contenitore: in slides abbiamo tutte le immagini che compongono lo slider, mentre in commands due button per navigare lo slider.
Come vedremo più avanti, lo slider non usa le immagini ma lo sfondo di slides per mostrare le slide, i tag immagine hanno una funzione semantica e intuitiva per la costruzione dell’HTML. Passare un array con i percorsi delle immagini infatti è decisamente poco semantico, mentre un markup più elaborato diventa pesante & difficile da impostare manualmente e complicato anche da mantenere.

Il CSS
.slider {width: 100%;}
.slider > * {width: 100%; height: 500px;}
.slides {background: no-repeat center #fff; background-size: 100%;}
.slides img {width: 100%; display: none;}
.commands {position: absolute; top: 0; left: 0;}
.slide-command {position: absolute; width: 90px; height: 100%; font-size: 80px; padding: 0 15px; font-weight: bold; background: none; cursor: pointer; color: #000;}
.slide-command:hover {background: rgba(255, 255, 255, .1);}
.slide-command.back {left: 0;}
.slide-command.next {right: 0;}

Il CSS si occupa di disporre il layout dello slider: nascondiamo le immagini, portiamo i comandi ai due lati dello schermo e definiamo come mostrare lo sfondo con l’effettiva slide. Con no-repeat mostriamo la slide solo una volta e non ripetuta lungo tutto il background, con center la portiamo al centro, e specialmente impostiamo la larghezza dello sfondo al 100%, in modo da allargarsi lungo tutto lo slider ma conservando le proporzioni.
Un analogo effetto si ottiene con il valore cover, ma in questo modo avremo un supporto browser limitato.
Usando lo sfondo con queste proprietà riusciamo a ottenere un layout responsive: l’immagine sarà sempre centrata e scalata alla larghezza massima dello slider senza perdere il rapporto d’aspetto originale.

Javascript
$(function() {
$(".slides").css("background-image", "url('" + $(".slides img:first-child").addClass("selected").attr("src") + "')");
$(".slide-command").click(function() {
var slide = $(this).hasClass("next") ?
(!$(".slides .selected").next().length ? $(".slides img").first() : $(".slides .selected").next()) :
(!$(".slides .selected").prev().length ? $(".slides img").last() : $(".slides .selected").prev());
$(".slides .selected").removeClass("selected");
$(".slides").animate({opacity : 0}, function() {$(this).css("background-image", "url('" + slide.addClass("selected").attr("src") + "')").animate({opacity: 1});});
});
});

Per il codice js ci appoggiamo a jQuery (ma essendo ormai incluso nella maggior parte dei progetti e molto utile anche per piccole cose, non lo vedo come un handicap), rendendo così il codice ancora più conciso.
Passando a $() una funzione la registriamo per l’evento ready della pagina, così da essere eseguita subito (un equivalente evoluto dell’onload insomma).
Questa funziona si occupa innanzitutto si mostrare la prima slide, impostando la src del tag corrispondente come background-image di slides.
A questo punto registra gli eventi click dei due pulsanti di navigazione: gli handler trovano la nuova immagine da mostrare, controllando se è alla fine o all’inizio ed eventualmente facendolo proseguire in loop, quindi la impostiamo sempre come sfondo di slides ma con un’animazione di cross-fading. Prima portiamo l’opacità a zero, quindi nel callback settiamo lo sfondo e rianimiamo l’opacità fino a 1.

Trovate una demo qui

La minificazione per ottimizzare le pagine web

Cos’è la minificazione?
Si tratta di un processo per ridurre la dimensione di un codice sorgente rimuovendo da esso elementi inutili al compilatore (tipo le tabulazioni per indentare il codice, commenti, e altri elementi a seconda del linguaggio considerato) ma magari utili al programmatore durante la fase di produzione del codice.
Questo processo riduce quindi le dimensione del file sorgente, e in ambito web è una cosa piuttosto utile, poichè meno è pesante un file da inviare al browser, meno tempo la pagina ci metterà a caricarsi.
La minificazione di solito si fa quindi con script Javascript o fogli CSS, raramente con HTML, poichè questi file vengono inviati direttamente al browser.

Minificazione in pratica
Un ottimo tool per la minificazione di Javascript e CSS è YUI Compressor (qui un’implementazione online).
Per minificare il codice YUI Compressor sfrutta diverse particolarità di Javascript e CSS.
Per quanto riguarda Javascript, applica una serie di piccole ottimizzazioni sostituendo certe sintassi con altre più stringate (es. object[“property”] diventa object.property, risparmiando 3 bytes), e inoltre sostituisce i nomi delle variabili con altri più corti, ma che non cambiano l’esecuzione del programma.
Ad esempio:

function prova(primo_argomento, secondo_argomento) {
    return primo_argomento + secondo_argomento;
}

Diventa:

function prova(a,b){return a+b;}

Che è assolutamente equivalente, ma molto più corta.
Per migliorare ulteriormente l’utilizzo di questo minificatore, vi consiglio la lettura di questo articolo su A List Apart (un sito da adorare, dove scrivono grandissimi programmatori di tutto il mondo), scritto proprio dall’ingegnere di YAHOO! che ha inventato YUI Compressor. Descrive semplici regole da usare per rendere il compito più facile al compressore, oltre ad altre buon tecniche di scrittura del codice per diminuire le dimensioni dei sorgenti che il compressore non può applicare.
In quanto ai CSS, YUI Compressor oltre a rimuovere totalmente spazi (tranne dalle dichiarazioni dei selettori) e commenti, esprime i colori nella forma più breve possibile. Ad esempio #FFFFFF, il bianco, diventa #FFF, oppure rgb(10, 10, 10) diventa #AAA.

Il PHP, come spiegato diversi post fa, invece, è interpretato lato server, quindi prima viene eseguito, poi viene inviato il suo output sottoforma di HTML al browser, che non vedrà assolutamente la differenza tra una pagina .php e una .html
Quindi, perchè minificare uno script PHP?
Intanto, si risparmia spazio sul server, e poi non dimentichiamoci che PHP, normalmente, viene compilato in bytecode ad ogni chiamata dello script, e quindi minore è la dimensione del file, minore sarà il tempo di swap su disco e di parsing del codice.
Script molto voluminosi possono essere formati in buona percentuale da spazi e tabulazioni piuttosto che dal codice vero e proprio. Più che gli script che si si scrivono da soli, è utile minificare librerie esterne, che spesso sono largamente commentate. Magari durante la fase di sviluppo la si tiene “normale” e la si minifica solo quando si è finito di lavorarci e la si carica sul server.
Per farlo, dobbiamo usare la direttiva -w chiamando php.exe, e specificando come secondo argomento il percorso del file da minificare.
Tradotto in pratica (qui la procedura per farlo su Windows), ci serve un interprete PHP installato sul proprio PC, con Wamp Server, Xampp, Lamp, EasyPHP o affini.
Cercate nelle cartelle dell’interprete fino a trovare il programma php.exe, quindi aprite blocco note e scrivete:

php.exe -w "C:\pippo\script_lungo.php" > "C:\pippo\script_minificato.php"

E salvatelo come minify.bat nella cartella dove avete trovato php.exe. Quindi apritelo facendoci doppio click sopra e.. volià! Troverete nella cartella pippo il vostro script minificato script_minificato.php

Javascript e PHP: inviare variabili POST tramite scrittura dinamica del codice

Siccome ho implementato il contatore PHP che (se avete letto) utilizza un variabile POST per decidere se aumentarsi o no, ho avuto anche la necessità di trovare un modo per poter inserire dei link interni che mi inviasse la variabile POST che mi serviva.

Siccome ciò non avviene in modo esplicito tramite una query string (sarebbero variabili GET), non posso inserire i valori nell’attributo href del link.
Dopo un po’ di ricerche ho trovato una buona idea: inviare i dati utilizzando una form (nascosta) contenente le variabili da inviare, generando il submit tramite Javascript inserendo il codice nell’evento OnClick del link.


<form method="post" name="data_form" action="index.php">
	<!-- variabile per il contatore -->
	<input type="hidden" name="increase_counter" value="no" />
	<!-- variabile per la pagina da caricare -->
	<input type="hidden" name="p" value="pagina3" />
</form>
<!-- al click sul link, invio i dati della form (che ha metodo POST) -->
<!-- 'return false;' serve per "fermare" il link, che altrimenti, dopo l'esecuzione del codice in OnClick -->
<!-- cercherebbe di andare al documento pagina3, generando ovviamente un errore 404 -->
<a href="pagina3" onclick="document.data_form.submit(); return false;">Vai a pagina 3</a>

Ora però, nonostante il tutto funzionasse, mi trovavo a dover scrivere tutto il codice di una form ogni volta che inserivo un link. Ho quindi scritto una funzione che, prima di inviare il form, impostasse il giusto valore alle variabili a seconda dell’attributo href del link. In questo modo, utilizzo una sola form per tutti i link, con un bel risparmio di codice!


<!-- Nell'head della pagina, inserisco questo tag script: -->
<script language="javascript" type="text/javascript">
//la funzione ha un unico parametro, link_object, che viene passato usando 'this'
//questa rappresenta il link che è stato cliccato
function go_link (link_object) {
	//siccome ai link relativi viene aggiunto automaticamente l'url completo, lo tolgo
	document.data_form.p.value = link_object.href.replace("http://www.example.net/", "");
	document.data_form.submit();
}
</script>
....
....
<!-- All'inizio del body -->
<form method="post" name="data_form" action="index.php">
	<input type="hidden" name="increase_counter" value="no" />
	<input type="hidden" name="p" value="" />
</form>
....
....
<!-- Un link diventa quindi: -->
<a href="pagina3" onclick="go_link(this); return false;">Vai a pagina 3</a>

Una volta completata anche questa operazione, e mentre mi stavo approcciando a farlo in tutte le altre pagine, mi son detto: perchè non implementarlo nel motore PHP?
Così ho aggiunto il codice Javascript una volta sola per tutte le pagine (lo inserisce infatti la pagina PHP) e ho aggiunto una ulteriore funzione che al caricarsi della pagina aggiunge ai link interni (escludendo ancore e link esterni) l’attributo onload che richiama la funzione go_link()
In questo modo ho semplificato enormemente la cosa: riesco a lavorare in background (usando le variabili POST), senza dover riscrivere lo stesso codice per ogni link, grazie a Javascript.


<head>
<script language="javascript" type="text/javascript">
function insert_links_attributes () {
	//grazie all'array links, ho tutti i link contenuti nella pagina
	for (n in document.links) {
		//modifico il link solo se è interno (contiene quindi l'url del mio sito nell'attributo href
		//oppure se è un ancora (link a un punto diverso della stessa pagina, contraddistinto dal carattere #
		if ((document.links[n].href.indexOf("http://www.example.net/") != -1) && (document.links[n].href.indexOf("#") == -1)) {
	 		document.links[n].setAttribute("onclick", "go_link\(this\)\; return false\;", 0);
	 	}
	}
}
</script>
</head>
<!-- Chiamo la funzione insert_links_attributes una volta completato il caricamento della pagina -->
<body onload="insert_links_attributes();">