Il problema si pone quando abbiamo a che fare con un layout composto da un div contenitore nel quale ci sono due div disposti in colonne affiancate: la prima colonna a sinistra (il div rosso nella foto accanto) è una sidebar di dimensioni p.e. del 30% e la seconda colonna a destra (che flotta a sinistra), è il contenitore del testo principale (il div verde).
javascript
Javascript: controllo di form con le espressioni regolari
L’esigenza era quella di trovare uno script client-side sintetico ed efficace nella validazione dei form HTML. Gli aggettivi sintetico ed efficace portano subito a pensare alle Espressioni regolari! Ora, sebbene devo dire che non sono certo un esperto di espressioni regolari, riconoscendo la loro potenza e la loro versatilità ho cercato di approfondire l’argomento.
Utilizzando questa sintassi, è possibile validare decine di campi di un form HTML, ricorrendo ad una sola funzione Javascript. E’ sufficiente, in un’altra funzione, passare come parametro il giusto pattern per ogni campo del form.
Per utilizzare le Espressioni regolari in Javascript ci sono due modi:
- Assegnazione letterale:
var regExpression = /pattern/;
- Assegnazione mediante costruttore:
var regExpression = new RegExp("pattern");/code>
In questo script ho utilizzato il primo metodo, anche se in effetti sarebbe preferibile il secondo che permette di risparmiare ulteriore codice in quanto il pattern può essere passato come stringa.
I metodi dell’oggetto RegExp sono 2:
test(string)
: esegue un test sulla stringa in base al pattern fornito e ritornaTRUE
oFALSE
se trova o meno la corrispondenzaexec(string)
: esegue una ricerca sulla stringa in base al pattern fornito e ritornaNULL
se non c’è corrispondenza, oppure un array che contiene i risultati.
Per la costruzione del pattern, è necessario conoscere alcuni rudimenti delle Espressioni regolari. Vediamo un breve sommario:
Principali metacaratteri (caratteri speciali)
.
indica qualsiasi carattere (escluso un a capo)*
indica zero o più occorrenze (di un carattere o di un gruppo di caratteri)?
indica zero o una occorrenza (di un carattere o di un gruppo di caratteri){}
(parentesi graffe): indicano il numero esatto, o minimo, o massimo, o l’intervallo di occorrenze (di un carattere o di un gruppo di caratteri)+
indica una o più occorrenze (di un carattere o di un gruppo di caratteri)^
indica l’inizio della stringa (o, se all’interno di una classe di caratteri, la negazione della stessa)$
indica la fine della stringa|
indica l’operatore OR\
carattere di escape dei caratteri speciali()
parentesi tonde, destinate a contenere una sottostringa[]
parentesi quadre, destinate a contenere una ‘classe’ di caratteri
Conoscendo il significato di questi metacaratteri, e con l’aiuto dei numerosi tutorial disponibili in rete (vedi sezione Riferimenti ed approfondimenti), è possibile almeno capire e modificare a nostro uso, i pattern già pronti per il controllo dei più comuni campi che possono essere presenti in un form.
Lo script che propongo utilizza pattern integralmente presi o leggermente modificati, tratti da questo documento
i campi sottoposti a verifica che ho preso in considerazione, e le regole per l’immissione dei dati sono:
Campo | Regola | Pattern |
---|---|---|
Utente | lettere, numeri, e i segni . _ – | /^([a-zA-Z0-9\.\_\-])+$/ |
Password | min 6, max 12 di caratteri, numeri, _ * – + ! ? , : ; . e lettere accentate | /^[a-zA-Z0-9\_\*\-\+\!\?\,\:\;\.\xE0\xE8\xE9\xF9\xF2\xEC\x27]{6,12}/ |
Nome | caratteri, lettere accentate apostrofo e un solo spazio fra le parole | /^([a-zA-Z\xE0\xE8\xE9\xF9\xF2\xEC\x27]\s?)+$/ |
C.A.P. | 5 numeri | /^\d{5}$/ |
caratteri e . _ % – + @ + caratteri compreso . + . + min 2, max 4 caratteri | /^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/ | |
Data | formato mm/gg/aaaa o mm-gg-aaaa o mm.gg.aaaa | /^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[-/.](19|20)\d\d/ |
Codice fiscale | vedi regole su Wikipedia | /^[a-zA-Z]{6}\d\d[a-zA-Z]\d\d[a-zA-Z]\d\d\d[a-zA-Z]/ |
Ecco il codice dello script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | // JavaScript Document //------------------------------------------------------------------------ // checkForm : checks each field for a given pattern of chars function checkForm(frm){ var pattern; // Utente (lettere, numeri, e i segni . _ -) pattern = /^([a-zA-Z0-9\.\_\-])+$/; if(!checkPatternChars('Utente',frm.user.value,pattern,true)) { frm.user.focus(); return false; } // Password (min 6, max 12 di caratteri, numeri, _ * - + ! ? , : ; . // e lettere accentate) pattern = /^[a-zA-Z0-9\_\*\-\+\!\?\,\:\;\.\xE0\xE8\xE9\xF9\xF2\xEC\x27]{6,12}/; if(!checkPatternChars('Password',frm.password.value,pattern,true)) { frm.password.focus(); return false; } // Nome (caratteri, lettere accentate apostrofo e un solo spazio fra le parole) pattern = /^([a-zA-Z\xE0\xE8\xE9\xF9\xF2\xEC\x27]\s?)+$/; if(!checkPatternChars('Nome',frm.name.value,pattern)) { frm.name.focus(); return false; } // C.A.P. (5 numeri) pattern = /^\d{5}$/; if(!checkPatternChars('CAP',frm.cap.value,pattern)) { frm.cap.focus(); return false; } // E-mail pattern = /^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; if(!checkPatternChars('E-mail',frm.email.value,pattern,true)) { frm.email.focus(); return false; } // Data formato mm/gg/aaaa o mm-gg-aaaa o mm.gg.aaaa pattern = /^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[-/.](19|20)\d\d/; if(!checkPatternChars('Data',frm.data.value,pattern)) { frm.data.focus(); return false; } // Codice fiscale pattern = /^[a-zA-Z]{6}\d\d[a-zA-Z]\d\d[a-zA-Z]\d\d\d[a-zA-Z]/; if(!checkPatternChars('Codice fiscale',frm.cf.value,pattern)) { frm.cf.focus(); return false; } return true; } //------------------------------------------------------------------------ // checkPatternChars : checks a specific pattern of typed chars function checkPatternChars(nm,vlu,pattern,required){ if ( required === undefined ) { required = false; } if(!required && vlu==""){ return true; } if (!pattern.test(vlu)){ alert("Il campo "+nm+" non e\' valido!"); return false; } else { return true; } } |
Un esempio è visibile qui il pacchetto è scaricabile qui
Conclusioni:
L’accoppiata javascript – Espressioni regolari fornisce un ottimo strumento di verifica client-side dei dati immessi dagli utenti. La verifica lato client, però, ha dei limiti e serve più che altro ad aiutare l’utente alla compilazione del form. Se si vuole essere davvero sicuri di avere dati corretti da passare alla query, è obbligatorio ripetere il controllo server-side, utilizzando magari gli stessi pattern e le corrispondenti funzioni PHP: ereg()
ed eregi()
Riferimenti ed approfondimenti:
AJAX, JavaScript, PHP e Google maps: Georeferenziazione indirizzi
Questo post ha più di 1 anno. Il contenuto potrebbe essere obsoleto, non più completamente accessibile o mancante di alcune informazioni.
Per concludere il panorama dei possibili utilizzi della classe PHP per georeferenziare indirizzi multipli, vediamo l’impiego della tecnologia AJAX vera e propria.
Come avevo accennato nel precedente articolo, l’approccio non è così lineare per la necessità di invocare più volte l’oggetto XMLHttpRequest
, e ancora peggio, per dover gestire le risposte multiple ed asincrone del server. Si rende infatti necessario, non solo creare tanti oggetti AJAX quante sono le richieste, ma anche altrettanti oggetti che gestiscano separatamente le risposte, mantenendo l’handle della chiamata.
Una buona soluzione mi è sembrata quella illustrata in questo articolo, ed è quella che ho adottato per questo esempio.
La pagina html iniziale comprende un form per l’immissione degli indirizzi e un semplice script javascript che, per ogni tag di tipo input
, invoca una richiesta AJAX. Ecco il codice della funzione:
<script type="text/javascript" src="MultipleAjaxClassSimple.js"></script> <script type="text/javascript"> function getFormValues() { document.getElementById("divResults").innerHTML = ''; var myForm = document.getElementById("myForm").getElementsByTagName('input'); var len = myForm.length; var params = ''; for (i = 0; i < len; i++) { if(myForm[i].type == 'text'){ params = myForm[i].value; ajaxSaveTag(params); } } } </script> |
Lo script AJAX è composto da quattro funzioni:
- ajaxSaveTag()
- httpRequest()
- initRequest()
- ReturnSaveTag()
La prima funzione prende come parametro l’indirizzo da georeferenziare, compone una url con la querystring da passare allo script server e infine chiama la seconda funzione httpRequest()
che si occupa di instanziare l’oggetto XMLHttpRequest
. La terza funzione, initRequest()
ha il compito di gestire le risposte del server, invocando i metodi della funzione di callback (la quarta: ReturnSaveTag()
), che in realtà è un oggetto. Questo permette di creare tante istanze di callback quante sono le chiamate AJAX.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | // JavaScript Document function ajaxSaveTag(tag) { var url = 'ajaxSaveTags.php'; url += '?query='+encodeURIComponent(tag); httpRequest("GET",url,1,new ReturnSaveTag()); } function httpRequest(type,url,asynch,respHandle) { var request; // mozilla if (window.XMLHttpRequest) { request = new XMLHttpRequest(); } // internet explorer else if (window.ActiveXObject) { request = new ActiveXObject("Msxml2.XMLHTTP"); if (!request) { request = new ActiveXObject("Microsoft.XMLHTTP"); } } // send request | error if (request) { initRequest(request,type,url,asynch,respHandle); } else { // ERROR! } } function initRequest(request,type,url,asynch,respHandle) { try { respHandle.setReqObj(request); request.onreadystatechange = respHandle.goResponse; request.open(type,url,asynch); request.send(null); } catch (errv) { // ERROR! } } function ReturnSaveTag() { var reqObj = null; this.setReqObj = setReqObj; this.goResponse = goResponse; function setReqObj(myVal) { reqObj = myVal; } function goResponse() { var request = reqObj; if (request.readyState == 4) { if (request.status == 200) { // Success! var mytext = request.responseText; var myDiv = document.getElementById("divResults"); // display the HTML output myDiv.innerHTML = myDiv.innerHTML + mytext; } else { // ERROR (couldn't find ajax file) } } return true; } // end method } |
Lo script lato server ajaxSaveTags.php crea un header
che non consenta al browser di utilizzare la cache, raccoglie i dati dalla querystring, utilizza la classe PHP geocodeaddr
per georeferenziare l’indirizzo, ed infine stampa il risultato formattato.
1 2 3 4 5 6 7 | <!--?php // make sure the user's browser doesn't cache the result header('Expires: Wed, 23 Dec 1980 00:30:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); require("geocoder4.class.php"); $data = array(); if (!empty($_GET['query'])) { $toGeocode = $_GET['query']; $data = array($toGeocode); } //localhost key: $gmpKey = "ABQIAAAAzr2EBOXUKnm_...";// Inserire la Google key corretta! $test = &amp; new geocodeaddr($data,$gmpKey); $geocodeResults = $test->getAddress(); $res = $geocodeResults[0]['address'].' lat: '. $geocodeResults[0]['lat'].' long: '. $geocodeResults[0]['lng'].'<br ?-->'; // output the result echo $res; ?> |
Questa è la demo
Conclusioni:
L’utilizzo della tecnologia AJAX ha richiesto un codice un po’ più sofisticato e meno facilmente intuibile rispetto alla soluzione pseudo-AJAX. Sarebbe interessante fare un confronto anche in altre situazioni in cui sia necessario effettuare richieste asincrone al server, e verificare efficienza e robustezza di entrambi con carichi gravosi. Un’ultima curiosità: poiché le risposte del server sono asincrone, può capitare che l’ordine dei risultati non sia lo stesso delle chiamate.
Riferimenti ed approfondimenti:
Pseudo-AJAX, JavaScript, PHP e Google maps: Georeferenziazione indirizzi
Questo post ha più di 1 anno. Il contenuto potrebbe essere obsoleto, non più completamente accessibile o mancante di alcune informazioni.
Nelle conclusioni del precedente post, dicevo che “… sarebbe interessante utilizzare la tecnologia Ajax per recuperare in modo asincrono le coordinate …”, ebbene, fatti alcuni test, mi sono ritrovato ad affrontare due difficoltà: compatibilità dell’oggetto XMLhttprequest
e soprattutto la gestione di richieste multiple contemporanee. Cercando sul web, ho trovato diverse soluzioni, alcune molto complicate, altre troppo “deboli”, una di queste interessante, ma alla fine ne ho trovata una, completamente diversa, che mi ha davvero incuriosito: la simulazione di un comportamento AJAX senza utilizzare… AJAX! La soluzione prospettata dall’autore di questo interessante post su W3 Facile, è tanto semplice quanto geniale: utilizzare il tag <script>
per includere nella nostra pagina html, porzioni di codice javascript generati dinamicamente da script server side PHP. In questo modo risulta possibile elaborare richieste a server remoti e ricevere serialmente le risposte, vediamo l’applicazione di questa soluzione al nostro caso:
la pagina iniziale contiene un form per impostare gli indirizzi da georeferenziare e questo codice javascript che effettua le chiamate allo script lato server PHP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <script type="text/javascript"> // Ottieni la base url url = document.location.href; xend = url.lastIndexOf("/") + 1; var base_url = url.substring(0, xend); function chiama_ajax(url) { //Inizia l'url con http? if (url.substring(0, 4) != 'http') { url = base_url + url; } // Crea un nuovo elemento JS var jsel = document.createElement('SCRIPT'); jsel.type = 'text/javascript'; jsel.src = url; //Appende l'elemento JS e quindi esegue la 'chiamata pseudo-AJAX' document.body.appendChild (jsel); } function getFormValues() { document.getElementById("divResults").innerHTML = ''; var myForm = document.getElementById("myForm").getElementsByTagName('input'); var len = myForm.length; var params = ''; for (i = 0; i < len; i++) { if(myForm[i].type == 'text'){ params = myForm[i].value; //per ogni elemento del form, includo lo script js che // viene creato dinamicamente dallo script php chiama_ajax('pseudoajax.php?query='+params); } } } </script> |
In pratica, per ogni elemento input
di tipo text
del form viene richiamata la funzione chiama_ajax()
passandogli la url del file php e il valore impostato in querystring.
Lo script lato server pseudoajax.php:
1 2 3 4 5 6 7 | <!--?php require("geocoder4.class.php"); $data = array(); if (!empty($_GET['query'])) { $toGeocode = $_GET['query']; $data = array($toGeocode); } //localhost key: $gmpKey = "ABQIAAAAzr2EBOXUKnm_..."; // Inserire la Google key corretta! $test = &amp; new geocodeaddr($data,$gmpKey); $geocodeResults = $test->getAddress(); $res = $geocodeResults[0]['address'].' lat: '. $geocodeResults[0]['lat'].' long: '. $geocodeResults[0]['lng'].'<br ?-->'; ?> div = document.getElementById('divResults'); div.innerHTML += '<!--?php echo($res); ?-->'; |
Lo script raccoglie il parametro passato in querystring, valorizza l’array da passare alla classe geocodeaddr
, formatta il risultato e lo rispedisce come elemento code>innerHTML
al client HTML sotto forma di script js. Fatto!
Questa è la demo
Conclusioni:
Concordo pienamente con l’autore che, nei commenti al suo articolo dice “Diciamo che come metodo ha i suoi pro e contro…i pro sono un’enorme leggerezza, compatibilità e semplicità d’uso, i contro dei limiti nel suo utilizzo e l’esclusione per la lettura di valori XML, non utilizzando l’XMLhttprequest”, e conclude dicendo: “…nella programmazione non esiste una soluzione migliore di un’altra SEMPRE, dipende dal problema che dobbiamo risolvere”
Riferimenti ed approfondimenti:
Javascript e CSS per la stampa di record selezionati in una tabella: l’approccio unobtrusive
Ho trovato nel web questo codice javascript per la stampa selettiva di record che possono essere selezionati da una tabella HTML. L’idea è senz’altro interessante e utile, ma non segue la filosofia unobtrusive. Ho cercato di modificarlo seguendo la logica del progressive enhancement.
La regola principale per scrivere codice javascript non intrusivo, banalmente, è pensare che non venga eseguito…! E’ necessario cambiare il punto di vista, pensare che la presentazione della pagina HTML sia sufficiente a sé stessa con il solo ausilio di una progettazione coerente e dell’uso dei CSS. Il codice javascript, servirà soltanto ad integrare le funzionalità offrendo optional ai quali sia possibile rinunciare.
Lo script in questione permette di selezionare attraverso una checkbox una o più righe di una tabella e di stampare, utilizzando l’attributo CSS @media print
solo le righe selezionate. Vediamo, dunque, perché non è unobtrusive: se il supporto javascript è disabilitato o comunque non disponibile, la tabella non verrà mai stampata. Questo succede perché gli elementi <tr>
della tabella appartengono per default alla classe DONTPrint
nella quale la proprietà display
è impostata a none
. La funzione javascript select_row()
provvede a runtime lo scambio della classe di appartenenza dell’elemento <tr>
da DONTPrint
a DOPrint
, che non essendo definita, per default mostra i contenuti.
La logica del progressive enhancement ci fa pensare invece che, non avendo la possibilità di stampare solo i record selezionati, sia almeno disponibile la stampa dell’intera tabella.
Ecco come impostare la pagina secondo questo approccio:
- Assegnamo a tutti gli elementi
<tr>
, per default la classeDOPrint
- Al caricamento del documento, mediante una funzione javascript scambiamo la classe dei
<tr>
, daDOPrint
aDONTPrint
- Mediante la funzione
select_row()
ridefiniamoDOPrint
solo le righe selezionate
Un po’ di codice:
1 2 3 4 5 6 7 8 9 | <!-- Definizione del css per la stampa --> <style type="text/css"> <!-- @media print { .DONTPrint { display:none; } .DONTEverPrint { display:none; } } --> </style> |
Le due funzioni principali javascript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <!-- ... --> <script type="text/javascript"> <!-- // Funzione lanciata in avvio function init(){ var myTableRows = document.getElementsByTagName("tr"); for (i = 0; i < myTableRows.length; i++) { if(myTableRows[i].className=='DOPrint'){ myTableRows[i].className = 'DONTPrint'; } } } // Funzione per la selezione della riga function select_row(row, color) { if (row.value=='on'){ row.value="off"; row.parentNode.parentNode.style.backgroundColor = color; row.parentNode.parentNode.className = 'DOPrint' } else{ row.value="on"; row.parentNode.parentNode.style.backgroundColor = ''; row.parentNode.parentNode.className = 'DONTprint' } } // --> </script> |
Il codice HTML per l’avvio:
<body onload="init()"> |
Il codice HTML per la definizione delle righe della tabella:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- ... --> <tr onmouseover="mouseover(this,'#cc6600','#cc6600');" onmouseout="mouseout(this,'#000000','#000000')" class='DOPrint'> <td><input type="checkbox" class='DONTEverPrint' name="checkbox1" onclick="select_row(this, '#cccccc');"> </td> <td>Arnold </td> <td>M</td> <td>45</td> </tr> <!-- ... --> |
Una demo dell’intero esempio è disponibile qui
Con l’occasione, ho anche modificato sia l’HTML che il codice javascript dell’esempio per renderlo un poco più vicino alle direttive degli standard W3C. Probablimente questo codice è stato scritto qualche anno fa, quando ancora, purtroppo, la sensibilità dei programmatori in tal senso era piuttosto scarsa.
Conclusioni:
L’utilizzo di un approccio unobtrusive è utile non soltanto per matenere struttura e presentazione della pagina nei rispettivi ambiti, ma anche per acquisire una forma mentis in grado di pensare preventivamente le vere necessità dell’utente, le caratteristiche di base dell’applicazione e gli elementi facoltativi. Infine si può affermare che “pensare unobtrusive” vuole dire in qualche modo aver compreso meglio il funzionamento delle tecniche utilizzate.
Riferimenti ed approfondimenti:
Librerie javascript Open-jACOB Draw2D: un tool per visual web applications?
Ero alla ricerca di qualche strumento web di tipo ajax-javascript che permettesse interattivamente di creare delle forme grafiche, di assegnargli delle proprietà, di ancorarle ad una griglia e ottenere un parsing xml del risultato, ed ho trovato Open-jACOB Draw2D.
Mi ha affascinato subito ed ora vi spiego il perché.
Per prima cosa, si tratta di un SDK composto da librerie javascript con licenza LGPL, nate dalle famose “DHTML graphics” del tedesco Walter Zorn.
Il lavoro di Andreas Herz, tedesco anche lui, è stato di implementare caratteristiche per la creazione di diagrammi di flusso e progettazione di circuiti elettrici, ma soprattutto di fornire allo strumento una maggiore flessibilità ed interattività (come p.e.: la gestione dei livelli di undo).
In realtà, queste librerie fanno anche parte di un progetto più complesso che si chiama Open-jACOB e ne costituiscono la componente grafica interattiva.
La cosa più accattivante secondo il mio punto di vista, è che il risultato è un prodotto compatibile con qualsiasi browser, senza alcun bisogno di plugin aggiuntivi perché tutta la grafica è gestita attraverso il DOM con dei DIV
.
Niente SVG o CANVAS, semplicemente DIV
opportunemente elaborati!
Qualcuno potrebbe obiettare che in questo modo la gestione delle forme vettoriali, a parte le linee e i rettangoli, risulta molto complessa e lenta ma lo scopo di questo strumento, come conferma l’autore, non è tanto quello della grafica vettoriale, piuttosto quello di un editor di diagrammi di flusso.
Invece, scavando un poco nel codice, e dalle demo on-line, io ci ho visto un interessante tool per lo sviluppo di applicazioni web interattive e “visual”. Vale a dire, combinando adeguatamente queste librerie javascript con del codice server-side, si potrebbe creare un framework RAD e WYSIWYG per lo sviluppo, totalmente Open source.
Questo tipo di framework, sia per la complessità, che per il tempo impiegato allo sviluppo, sono normalmente appannaggio di grosse ditte e le licenze sono invitabilmente di tipo commerciale.
Esempio pratico
Se la cosa incuriosice anche voi, qui potete provare una piccola demo delle potenzialità di Open-jACOB Draw2D con particolare riferimento allo sviluppo di applicazioni “visual”.
Ho ottenuto questa demo semplicemente abbinando alcune caratteristiche base dei veri esempi forniti dall’autore.
Conclusioni:
Complimenti ad Andreas Herz, davvero un bel lavoro! Credo proprio che continuerò a seguire questo progetto!
Riferimenti ed approfondimenti: