In questo articolo vedremo come inserire nel proprio sito un piccolo editor in grado di formattare il testo con delle barre degli strumenti - stile Word Processor - e ricavare del codice HTML.
Può essere utile sia come vero e proprio generatore di HTML, sia come campo di testo per scrivere e-mail, sia può essere utilizzato all'interno di sistemi di Content Management per favorire la formattazione del testo ad utenti inesperti.
Il codice è basato sulla proprietà designMode, che trasforma i documenti HTML in editor di testo. Questa proprietà, in passato supportata soltanto da Microsoft Internet Explorer, è ora finalmente implementata anche da Mozilla 1.3.
Realizzazione del modulo HTML
Presentiamo il codice HTML per la realizzazione della prima barra degli strumenti, quella su cui bisogna agire per modificare il testo. La barra è raffigurata nell'immagine qui sotto:
<div ID="ParaToolbar">
<select ID="ParagraphStyle"
onchange="formatC('formatBlock',this[this.selectedIndex].value);this.selectedIndex=0">
<option selected>Paragrafo
<option value="<H1>">Titolo 1 <H1>
<option value="<H2>">Titolo 2 <H2>
<option value="<H3>">Titolo 3 <H3>
<option value="<H4>">Titolo 4 <H4>
<option value="<H5>">Titolo 5 <H5>
<option value="<H6>">Titolo 6 <H6>
<option value="<PRE>">Formattato <PRE>
</select>
<select ID="FontName" onchange="formatC('fontnamÈ,this[this.selectedIndex].value);this.selectedIndex=0">
<option class="heading" selected>Tipo Carattere
<option value="Arial">Arial
<option value="Arial Black">Arial Black
<option value="Arial Narrow">Arial Narrow
<option value="Comic Sans MS">Comic Sans MS
<option value="Courier New">Courier New
<option value="System">System
<option value="Times New Roman">Times New Roman
<option value="Verdana">Verdana
<option value="Wingdings">Wingdings
</select>
<select ID="FontSize" onchange="formatC('fontsizÈ,this[this.selectedIndex].value);this.selectedIndex=0">
<option class="heading" selected>Dimensione
<option value="1">1
<option value="2">2
<option value="3">3
<option value="4">4
<option value="5">5
<option value="6">6
<option value="7">7
</select>
<select ID="FontColor" onchange="formatC('forecolor',this[this.selectedIndex].value);this.selectedIndex=0">
<option class="heading" selected>Colore Testo
<option value="red">rosso
<option value="blue">blu
<option value="green">verde
<option value="black">nero
</select>
<select ID="FontBackColor" onchange="formatC('backcolor',this[this.selectedIndex].value);this.selectedIndex=0">
<option class="heading" selected>Colore Sfondo
<option value="red">rosso
<option value="blue">blu
<option value="green">verde
<option value="black">nero
<option value="yellow">giallo
<option value="">bianco
</select>
</div>
Il codice è formato da cinque selectbox che impostano, nell'ordine, il tipo di paragrafo, il tipo di carattere, la dimensione del font, il colore del testo e il colore dello sfondo.
La prima opzione delle selectbox è sempre costituita dalla spiegazione della sua funzione. Andando a selezionare un valore (evento onChange) verrà eseguita la funzione javascript formatC - che vedremo in dettaglio più avanti - seguita dall'automatico ritorno alla prima opzione (indice 0).
La parola chiave this restituisce l'oggetto stesso (in questo caso la selectbox), quindi this.selectedIndex indica l'indice dell'opzione selezionata nella selectbox.
Vediamo ora la seconda barra degli strumenti, raffigurata anch'essa dalla seguente immagine:
<div ID="EditMode">
<input type=checkbox name="switchMode" onclick="setMode(switchMode.checked)">
<font color="#000000"><strong>Visualizza HTML</strong> | </font>
<a href="Javascript:formatC('removeFormat')"><strong>Rimuovi Formattazione Carattere</strong></a>
</div>
Il primo campo è una casella di controllo che permette di passare dalla visualizzazione in modalità testo alla visualizzazione in modalità HTML. Richiama la funzione javascript setMode passandogli come parametro il valore true se la casella è selezionata e false altrimenti.
Il link successivo permette di rimuovere la formattazione del testo. Richiama la funzione formatC, come i campi selectbox già visti.
Nella terza barra degli strumenti abbiamo un'insieme di immagini con varie funzioni di formattazione del testo. Tutte quante richiamano la funzione formatC che, come avrete capito, è l'elemento portante dello script. Vediamo il codice:
<table class="tabella">
<tr>
<td>
<div title="Grassetto"
onclick="formatC('bold');">
<img src="img/bold.gif">
</div>
</td>
<td>
<div title="Corsivo" onclick="formatC('italic')">
<img src="img/italic.gif">
</div>
</td>
<td>
<div title="Sottolineato" onclick="formatC('underlinÈ)">
<img src="img/under.gif">
</div>
</td>
<td>
<div title="Allinea a sinistra" onclick="formatC('justifyleft')">
<img src="img/aleft.gif">
</div>
</td>
<td>
<div title="Centra" onclick="formatC('justifycenter')">
<img src="img/center.gif">
</div>
</td>
<td>
<div title="Allinea a destra" onclick="formatC('justifyright')">
<img src="img/aright.gif">
</div>
</td>
<td>
<div title="Elenco puntato" onclick="formatC('insertorderedlist')">
<img src="img/nlist.gif">
</div>
</td>
<td>
<div title="Elenco numerato" onclick="formatC('insertunorderedlist')">
<img src="img/blist.gif">
</div>
</td>
<td>
<div title="Riduci rientro" onclick="formatC('outdent')">
<img src="img/ileft.gif">
</div>
</td>
<td>
<div title="Aumenta rientro" onclick="formatC('indent')">
<img src="img/iright.gif" >
</div>
</td>
<td>
<div id="taglia" title="Taglia" onclick="formatC('cut')">
<img src="img/cut.gif">
</div>
</td>
<td>
<div id="copia"title="Copia" onclick="formatC('copy')">
<img src="img/copy.gif">
</div>
</td>
<td>
<div id="incolla" title="Incolla" onclick="formatC('pastÈ)">
<img src="img/paste.gif">
</div>
</td>
</tr>
</table>
A questo punto troviamo il campo dove inserire il testo. Possiamo notare che in realtà non è una textarea, come si sarebbe potuto supporre, ma un iframe:
<IFRAME class="Composition" width="100%" ID="Composition" height="190"></IFRAME>
Un iframe è una parte di una pagina Web che può essere trattata come documento a sé stante. Vediamo come renderlo editabile con javascript.
Inizializzazione dell'iframe contenente il testo
Per prima cosa abbiamo bisogno di settare le variabili globali, cioè quelle variabili che agiscono sull'intera pagina:
var bHtmlMode = false;
var str_iFrameDoc = (document.all)?
"document.frames("Composition").document;":
"document.getElementById("Composition").contentDocument;";
Creiamo la variabile booleana bHtmlMode per indicare se ci troviamo in modalità di visualizzazione del codice HTML. Per default la impostiamo su false.
La variabile str_iFrameDoc ci serve, invece, per realizzare la compatibilità tra i browser. Infatti una grande differenza tra Mozilla e IE è la modalità di accesso all'oggetto document di un iframe.
Mozilla usa lo standard W3C:
document.getElementById("nome_frame").contentDocument
mentre Internet Explorer richiede:
document.frames("nome_frame").document
Quindi, a seconda del browser utilizzato dal client, memorizziamo nella stringa str_iFrameDoc il giusto codice.
Per far ciò utilizziamo l'operatore condizionale condition ? expr1 : expr2. In pratica se l'espressione document.all restituisce il valore true sappiamo che il client sta utilizzando Internet Explorer ed utilizziamo la sua sintassi, altrimenti utilizziamo quella aderente allo standard W3C.
Analizziamo ora il seguente script:
onload = initialize;
function initialize() {
  iFrameDoc = eval(str_iFrameDoc);
 iFrameDoc.open();
  iFrameDoc.write("<HTML><BODY MONOSPACE STYLE='font:10pt arial,sans-serif'></BODY></HTML>");
  iFrameDoc.close();
  iFrameDoc.designMode = "On";
 Â
  document.getElementById("switchMode").checked = false;
  if (!document.all) {
  document.getElementById("taglia").style.visibility = "hidden";
  document.getElementById("copia").style.visibility = "hidden";
  document.getElementById("incolla").style.visibility = "hidden";
  }
}
La funzione initialize viene richiamata quando si attiva l'evento onload, cioè quando la pagina viene caricata.
Con la funzione eval valutiamo una stringa JavaScript. Nel nostro caso assegniamo alla parola-chiave iFrameDoc il riferimento al documento dell'iframe usando la stringa str_iFrameDoc costruita dinamicamente. In questo modo potremo accedere ad esso utilizzando soltanto questo parola.
Apriamo quindi per la scrittura il nostro iframe e al suo interno scriviamo il tag body, dandogli uno stile monospazio per renderlo simile ad una textarea. Infine richiudiamo l'iframe.
La proprietà designMode indica se un documento può essere editato. Il valore di default è Off, per renderlo editabile occorre, invece, impostarla su On. Impostiamo allora su On la proprietà designMode relativa al documento contenuto nell'iframe. In questo modo l'utente potrà scrivere al suo interno come se si trattasse di una textarea.
Impostiamo ora su false il valore della proprietà checked della chekbox relativa al cambio di modalità di visualizzazione. Ciò è necessario perché Mozilla non reimposta automaticamente questo campo al refresh della pagina. Possiamo notare che in questo caso anche IE accetta la sintassi del W3C.
Infine, se il browser utilizzato dal client non è IE, nascondiamo i div che contengono i tastini taglia, copia e incolla. Facciamo ciò perché questi comandi non sono supportati da Mozilla.
Formattazione del testo
A questo punto vediamo come formattare il testo. Abbiamo bisogno, prima di tutto, di creare un controllo per disabilitare gli strumenti della toolbar in caso di visualizzazione in modalità HTML.
function validateMode() {
if (!bHtmlMode) return true;
alert("Deselezionare "Visualizza HTML" per utilizzare le barre degli strumenti");
setFocus();
return false;
}
Ricordiamo che la variabile bHtmlMode indica se ci troviamo in modalità HTML. Quindi, nel caso questa sia impostata sul valore true, la funzione validateMode restituirà un messaggio di errore (vedremo in seguito come passare da una modalità all'altra), dopodiché chiamerà la funzione setFocus:
function setFocus() {
if (document.all)
document.frames("Composition").focus();
else
document.getElementById('Composition').contentWindow.focus()
return;
}
Questa funzione altro non fa che riportare il focus all'interno dell'iframe. Anche qui il problema è dato dall'incompatibilità tra i browser, come si può facilmente vedere. Osserviamo ora la funzione formatC:
function formatC(what,opt) {
if (!validateMode())Â return;
iFrameDoc = eval(str_iFrameDoc);
iFrameDoc.execCommand(what,false,opt);
setFocus();
}
Per prima cosa la funzione formatC controlla la modalità della visualizzazione, chiamando la funzione validateMode. Se siamo in modalità HTML esce senza fare niente, altrimenti richiama il metodo execCommand dell'oggetto document, il cuore del nostro esempio.
Il metodo execCommand consente di eseguire comandi per manipolare i contenuti di una regione editabile. È disponibile, quindi, soltanto quando la proprietà desingMode di un documento è impostata sul valore on. Alcuni comandi, come bold e italic, modificano la parte selezionata del documento, altri inseriscono nuovi elementi o hanno effetto su un intero paragrafo. Tutti i comandi che richiamiamo in questo script sono solo una minima parte del grande numero di cose che questo metodo ci consente di fare.
execCommand accetta tre parametri. Il primo, obbligatorio, specifica il comando da eseguire. Il secondo parametro è un booleano opzionale. Specifica se visualizzare un'interfaccia utente, se il comando la supporta. L'opzione di default è false. Il terzo parametro, opzionale, specifica il valore da assegnare al comando.
Se torniamo ad osservare la parte HTML dello script, possiamo notare che soltanto le funzioni contenute nelle selectbox passano questo valore alla funzione formatC, mentre le funzioni della terza barra degli strumenti passano solo la prima opzione, non avendo bisogno di un valore da assegnarli.
Visualizzazione dell'HTML
Vediamo ora la funzione che ci consente di passare dalla visualizzazione del testo formattato a quella dell'HTML.
function setMode(newMode) {
var testo;
bHtmlMode = newMode;
iFrameDoc = eval(str_iFrameDoc);
riquadro = iFrameDoc.body;
if (document.all) {
if (bHtmlMode) {
testo = riquadro.innerHTML;
riquadro.innerText = testo;
} else {
testo = riquadro.innerText;
riquadro.innerHTML = testo;
}
} else if(document.getElementById && document.createTextNode) {
if (bHtmlMode) {
testo = document.createTextNode(riquadro.innerHTML);
riquadro.innerHTML = "";
riquadro.appendChild(testo);
} else {
testo = document.createRange()
testo.selectNodeContents(riquadro);
riquadro.innerHTML = testo.toString();
}Â Â
}
setFocus();
}
Abbiamo visto che selezionando o deselezionando la casella di controllo switchMode passiamo alla funzione setMode il valore true o false. In questo caso impostiamo anche la variabile bHtmlMode sul valore true, per bloccare l'utilizzo dei tasti di formattazione, o sul valore false per riabilitarlo.
A questo punto dobbiamo passare da una modalità di visualizzazione all'altra all'interno del riquadro editabile. Anche qui le maggiori difficoltà ci saranno date dalla compatibilità tra i browser. Per comodità utilizziamo il riferimento all'oggetto body del documento contenuto nell'iframe. Lo chiamiamo riquadro.
Analizziamo il codice dei due browser separatamente.
Nel caso di IE basta ricorrere a due semplici proprietà: innerHTML e innerText. Vediamo meglio di cosa si tratta.
- innerHTML rappresenta il codice HTML contenuto tra l'inizio e la fine dei tag dell'oggetto.
- innerText rappresenta, invece, il testo contenuto tra l'inizio e la fine dei tag dell'oggetto.
Quindi mentre con innerHTML tutti i tag all'interno dell'oggetto saranno interpretati come codice, con innerText verranno visti semplicemente come testo.
In pratica se abbiamo selezionato la casella di controllo, e quindi vogliamo vedere il codice, impostiamo la proprietà innerText dell'oggetto body dell'iframe con il contenuto della proprietà innerHTML dello stesso, cioè prendiamo il codice HTML e lo reinseriamo nel riquadro sotto forma di testo.
Chiaramente quando deselezioniamo la casesella di controllo eseguiamo il procedimento contrario: prendiamo il testo e lo reinseriamo sotto forma di codice HTML.
Con Mozilla il codice è più complesso, perché la proprietà innerText non è più supportata e dobbiamo ricorrere a qualche altro espediente. Fortunatamente, però, il W3C ci offre un'ampia gamma di strumenti per accedere agli elementi della pagina.
In particolare, per passare dalla visualizzazione dell'HTML, ci basterà utilizzare i metodi createTextNode e appendChild.
Il metodo createTextNode permette di creare un nuovo nodo di testo. La stringa da inserire in tale nodo sarà, ovviamente, il codice contenuto nell'iframe. Per ricavarcelo possiamo continuare ad utilizzare innerHTML. createTextNode ci restituisce il riferimento al nodo di testo creato.
Il metodo appendChild inserisce il nuovo nodo. Come nel caso di innerText, con appendChild i tag non vengono interpretati come codice ma come semplice testo. L'unico problema è che questo metodo non rimpiazza il testo preesistente, ma inserisce il nuovo nodo alla fine della lista dei figli del nodo al quale è applicato. Questo problema è però presto risolto: prima di utilizzare il metodo ripuliamo il riquadro ponendo il suo innerHTML uguale a stringa vuota.
Vediamo ora come ritornare alla visualizzazione del testo formattato. Il procedimento è un po' più complesso.
Il metodo createRange crea un oggetto TextRange, che rappresenta un intervallo di testo.
Il metodo selectNodeContents imposta a un intervallo di testo di contenere il contenuto di un nodo. Quindi imposta al nostro oggetto TextRange il testo contenuto nel riquadro. Infine, convertendo il nostro oggetto in stringa, lo inseriamo all'interno del riquadro con il metodo innerHTML che già conosciamo.
Differenze nella creazione del codice HTML
Abbiamo visto come per arrivare agli stessi risultati in Mozilla e in Internet Explorer siamo dovuti riccorre a codici molto a sé stanti. Occorre a questo punto precisare, però, che la maggiore differenza tra i due browser è data dall'HTML generato nel documento editabile: mentre Internet Explorer produce i vari tag HTML (em, i, ecc.), Mozilla 1.3 genera tag di tipo span contenenti regole CSS.
Ad esempio se scriviamo nel riquadro di testo "HTML.IT" e clicchiamo sui tasti "Grassetto", "Corsivo" e "Sottolineato", con IE otterremo il seguente codice:
<strong><em><u>HTML.it</u></em></strong>
Mentre con Mozzilla avremo il più attuale:
<span style="font-weight: bold; font-style: italic; text-decoration: underline;">HTML.IT</span>