Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Costruire un web editor WYSIWYG

Esaminiamo le tecniche di base per costruire un'area editabile all'interno di pagine web
Esaminiamo le tecniche di base per costruire un'area editabile all'interno di pagine web
Link copiato negli appunti

L'introduzione di piattaforme per la gestione di contenuti in modo automatizzato (CMS), dai blog ai siti aziendali, ha fatto nascere l'esigenza di un controllo più creativo del contenuto stesso da parte dei content manager che spesso non hanno conoscenze tecniche tali da permettere loro di applicare formattazioni (più o meno complesse) sul testo a cui stanno lavorando.

Nasce da questa esigenza il filone degli editor visuali conosciuti anche come editor WYSIWYG (acronimo di "What You See Is What You Get", quello che vedi è quello che ottieni), un concetto introdotto con le prime versioni visuali degli editor di testo tradizionali (Writer o Word per intenderci). Allo stesso modo e a partire da versioni piu mature dei primi browser, tutte le piattoforme per la navigazione sul web hanno messo a disposizione degli sviluppatori
una serie di strumenti per creare editor WYSIWYG all'interno delle pagine web sfruttando Javascript. Strumenti che nel tempo si sono moltiplicati di pari passo con le nuove tecnologie introdotte (Flash, applet Java o controlli ActiveX) e che forniscono lo stesso risultato: semplificare la vita a chi si deve occupare di formattare testo sul web.

Scopo di questo articolo è la creazione di un semplice editor WYSIWYG cross browser, per introdurre le basi delle tecnologie Javascript a disposizione. Il codice che andremo man mano creando è stato provato con Internet Explorer (dalla versione 5.5 in su), Firefox (versione 2) e Camino (versione 1.04), Opera (versione 9.2) e
Safari (versione 2).

La base è sempre diversa

Come di norma, Microsoft e Mozilla hanno proceduto su strade diverse: per Internet Explorer il programmatore ha a disposizione il controllo
MSHTML mentre per Firefox, Safari ed Opera (che comunque meriterebbe un discorso a parte, visto
che questo browser supporta anche una proprietà peculiare di MSHTML) il controllo è Midas. Il solito accorgimento
per una soluzione cross-browser è abbastanza semplice e indolore.

Ma procediamo con ordine e supponiamo che si voglia usare un iframe come foglio di lavoro e a cui assoceremo l'id editArea.
Il browser deve "capire" che questa sarà l'area dove andremo a creare il testo formattato grazie al nostro editor e per farlo occorrerà settare la proprietà designMode su "on". Ed è qui che MSHTML e Midas differiscono:
infatti, nonostante la proprietà abbia lo stesso nome, in entrambi i controlli, le modalità per accedervi sono leggermente diverse:

  • Mozilla: document.getElementById("editArea").contentDocument.designMode="on";
  • Internet Explorer: document.editArea.contentDocument.designMode="on";

Per completezza, occorre aggiungere che Internet Explorer (ed Opera, almeno dalla versione 9) può attivare l'editing su un elemento della pagina HTML (escluso il tag iframe), anche
utilizzando l'attributo contentEditable.

Siamo ora in grado di cliccare sul nostro iframe e cominciare a scriverci all'interno.
Da qui in avanti inoltre, la compatibilità tra i due sistemi è pressoché totale. Quindi applicare un effetto
sul testo che stiamo editando all'interno del nostro iframe, si traduce nell'applicare la proprietà execCommand in questo modo:

Mozilla:

document.getElementById("editArea").contentDocument.execCommand(stringadicomando,true|false, stringa);

Internet Explorer:

document.editArea.contentDocument.execCommand(stringadicomando, true|false, stringa)

Che cross browser sia!

A questo punto allora definiamo una piccola funzione Javascript che ci permetta di attivare il nostro iframe in modo corretto a prescindere dal browser dell'utente (vedi l'esempio):

function AttivaFrame(iFrameID){
   if (document.getElementById(iFrameID).contentDocument){
      //Mozilla
      return document.getElementById(iFrameID).contentDocument;
   } else {
      //ie
      return document.frames[iFrameID].document;
   }
}

Come vedete il codice è piuttosto semplice: il controllo è sull'accesso all'iframe usando il metodo valido per Firefox: se il test va a buon fine,
la funzione ritorna l'oggetto instanziato con lo stesso metodo della condizione, altrimenti inizializza lo stesso oggetto con il metodo proprietario di Explorer.
La funzione viene richiamata all'evento onload del body, dalla funzione load() che effettivamente attiva l'area editabile:

function load(){
   AttivaFrame("editArea").designMode = "On";

}

In seguito la stessa funzione verrà richiamata per eseguire i comandi sul testo: in questo modo evitiamo di scrivere ogni volta una lunga stringa per accedere all'iframe su cui stiamo lavorando.

Ed ora tocca ai bottoni

Una volta preparato il nostro foglio virtuale, siamo pronti a inserire i bottoni per le diverse azioni tipiche di un editor visuale.
Per iniziare allora, creiamo un bottone che, una volta cliccato, richiami la funzione che segue e che ci permetterà di applicare l'effetto corsivo sul
testo dentro il nostro iframe (vedi esempio):

function applicaCorsivo(){
   AttivaFrame('editArea').execCommand('italic',false, null);
   if (document.getElementById("corsivo").value == 'corsivo') {
      document.getElementById("corsivo").value="togli il corsivo";
   } else {
      document.getElementById("corsivo").value="corsivo";
   }
}

Anche in questo caso la funzione è semplice ed ovviamente la parte importante è nella prima riga, dove viene richiamato il metodo execCommand, per il quale occorre specificare, sempre,
tre parametri:

  • il comando che si vuole applicare al testo;
  • un valore booleano che va sempre posto a false;
  • una stringa che indica un valore che per alcuni comandi è richiesto (ad esempio per specificare alcune caratteristiche del testo oppure il background dell'area che stiamo editando);

La lista dei comandi che possono essere usati varia leggermente tra i due browser (ecco quella per Internet Explorer e quella per Firefox/Mozilla),
ma quelli di base sono fortunatamente gli stessi. Possiamo allora pensare di generalizzare la funzione (esempio):

function applicaComando(cmdStr,valCmdStr){
   AttivaFrame('editArea').execCommand(cmdStr,false,valCmdStr);
}

dove cmdStr è il nome del comando che vogliamo attivare mentre valCmdStr è il valore da associare ad alcuni dei comandi (vedi esempio).

Che cosa abbiamo prodotto?

L'applicazione di stili ed effetti al nostro testo, è in realtà una realizzazione di una semplice applicazione di tag HTML al testo all'interno del nostro iframe. Ma cosa abbiamo effettivamente inserito?
Scriviamo allora una piccola funzione che mostri in un div della nostra pagina, il codice HTML prodotto durante l'editing (vedi esempio):

function vediCodice(){
   var contenuto = AttivaFrame("editArea").body.innerHTML;
   document.getElementById("codice").innerHTML = contenuto.replace(/</g,"<");
}

Se provate quest'ultima funzionalità, prima con Explorer e poi con Firefox, noterete le diverse modalità con cui i due browser gestiscono il contenuto
dell'iframe: il browser Microsoft sostituirà gli effetti applicati con tag HTML (anche obsoleti e deprecati come nel caso di FONT), mentre Firefox tradurrà le nostre
istruzioni con stili applicati ad un tag span. Opera si comporta invece come Firefox mentre Safari ha ancora una diversa gestione.
Nella tabella che segue, ho riportato il codice HTML che si produce a seconda del browser, dopo aver applicato alla parola test, l'effetto grassetto ed aver
cambiato il colore della scritta da quello di default, in rosso:

Internet Explorer <STRONG><FONT color=red>test</FONT></STRONG>
Firefox (e Camino) <span style="font-weight: bold; color: red;">test</span>
Safari <SPAN class="Apple-style-span">
<SPAN class="Apple-style-span" style="font-weight: bold;">
<SPAN class="Apple-style-span" style="color: red;">test</SPAN>
</SPAN>
</SPAN>
Opera <STRONG><FONT color="#ff0000">test</FONT></STRONG>

Ogni output ha i suoi pro e i suoi contro (escludendo quello di Safari che in realtà presenta pochi punti a favore): ovviamente il risultato che si ottiene con Mozilla è quello più "moderno", che permette cioè una gestione
del contenuto piu in sintonia con le tecniche di sviluppo attuali, anche se la sintassi ne risulta appesantita (si pensi al caso di un grassetto tradotto con <span style="font-weight: bold"> piuttosto che con un semplice <strong>)
e comunque anch'essa deprecata (gli stili in linea non dovrebbero essere usati). Cosa significa questo? Che nel caso si voglia ottenere un codice "pulito"
a prova di W3C, occorrerà effettuare delle operazioni di verifica (e appunto pulizia) del codice HTML ottenuto.

Considerazioni finali

Questo è tutto, o meglio l'inizio: la lista dei comandi che possono essere implementati è ampia ed i problemi a cui ho accennato aggiungono
carne al fuoco, ma le basi ora sono a vostra disposizione: il resto sarà semplice ricerca e tempo da dedicare all'argomento. Continueremo a parlarne in altri appuntamenti.

Se volete farvi un'idea di cosa c'è già in giro vi consiglio di visitare il sito htmlarea.com dove vengono catalogati tutti i web editor WYSIWYG disponibili.
Inoltre molto interessante, è questo articolo
(con annessa serie di commenti anche su edit)
che compara diversi editor: vi potra' dare ulteriori spunti su le problematiche piu comuni a questo tipo di applicazioni.

Nella prima parte abbiamo terminato lasciando aperte le questioni relative alla qualità del codice prodotto dal nostro
editor.

Come esempio iniziale dunque, procediamo sistemando il codice dai tag deprecati, che vengono generati quando scegliamo il colore del testo. A tale proposito,
ci semplificheremo un po' il lavoro sostituendo, nel dropdown per la scelta dei colori, i valori associati ad ogni colore, con il relativo codice esadecimale piuttosto che
con il nome del colore. Questo passaggio ci aiuterà nella creazione delle regular expression che useremo in questo esempio: in questo modo sarà piu semplice la creazione
e in generale, potremmo applicare la stessa regular expression ad ogni colore che vogliamo aggiungere al nostro editor.

Meno male che ci sono le regular expression!

Per brevità ci focalizzeremo su Internet Explorer ed Opera, considerando Safari un esempio particolare di questi due casi e Firefox passabile
dal punto di vista dei tag generati. La funzione da aggiungere al nostro editor per la pulizia del codice è la seguente:

function pulisciCodice(){
  var contenuto = AttivaFrame("editArea").body.innerHTML;
  if (document.frames["editArea"].document) {
    document.getElementById("codicePulito").innerHTML = contenuto.replace(/</g,"<");
    contenuto = contenuto.replace(/</font>/gi,"</span>");
    contenuto = contenuto.replace(/(<font )(color)(=)(#?([A-Fa-f0-9]){3}(([A-Fa-f0-9]){3})?)/gi,"<span style="$2:$4;""); //internet explorer
    contenuto = contenuto.replace(/(<font )(color)(=")(#?([A-Fa-f0-9]){3}(([A-Fa-f0-9]){3})?)(")/gi,"<span style="$2:$4;""); //opera
  }
  document.getElementById("codicePulito").innerHTML = contenuto.replace(/</g,"<");
}

Diamo un occhiata piu in dettaglio. Una volta "attivato" il frame dell'editor, distinguiamo il browser che stiamo usando. Come ricorderete Opera ed Explorer usano lo stesso modo per attivare
le funzioni di WYSIWYG quindi con un semplice controllo riusciamo a capire se dover applicare o meno l'operazione di bonifica del codice. Ma è proprio necessario questo controllo?
In realtà no: le regular expression infatti cercheranno determinati pattern (modelli) di stringhe che noi ovviamente abbiamo "cucito" sull'output che abbiamo studiato
nell'articolo precedente: in questo modo le stringhe generate da Firefox (diverse sia da quelle di Explorer che di Opera, ma anche di Safari) non verranno modificate.
Ovviamente questo vale per il caso particolare preso in esame, in generale quindi, potrebbe tornare utile determinare quale browser l'utente sta utilizzando.

A questo punto allora guardiamole piu da vicino queste regular expressions: la prima sostituisce tutte le occorrenze del tag di chiusura </FONT> in </span>; la seconda e la
terza invece cercano (a seconda del browser) tutte le occorrenze del tipo <FONT color="numHex" e le trasformano in <span style="color:numHex;".
Aggiungiamo poi un bottone al nostro editor, che attiverà la funzione PulisciCodice. L'output sarà il codice pulito e se volete lo potete confrontare con ciò che è stato generato dall'editor (cliccando sul vecchio bottone che attiva la funzione VediCodice().
(esempio 7)

Come avete visto un po' di lavoro di preparazione e un paio di regular expression, permettono di manipolare in modo piuttosto agevole il codice che un utente può creare con il nostro
editor. Di seguito vi riporto alcune delle regular expressions che comunemente si applicano in questo tipo di applicazione: ovviamente non sono complete ed è probabile che
non siano neanche troppo ottimizzate, ma come spunto iniziale credo sia interessanti per i vostri esperimenti:

Togliere i tags vuoti:
   .replace(/<strong></strong>/gi,'').replace(/<i></i>/gi,'').replace(/<P[^>]*></P>/gi,'');
Copia e incolla da word? Qualche caso
   .replace(/<?xml:[^>]*>/g, '')
   .replace(/</?st1:[^>]*>/g,'')
Attributi indesiderati
   .replace(/ class=[^s|>]*/gi,'') //qui potremmo anche ripensarci!
   .replace(/

]*TEXT-ALIGN: justify[^>]*>/gi,'

')
   .replace(/ align=[^s|>]*/gi,'');

Cos'altro manca? a questo punto il nostro editor è pronto per essere usato, ma certo non è bellissimo! Nella terza parte lo sistemeremo graficamente, per renderlo davvero simile
ad un editor tipo Word e vedremo anche in che modo manipolare lato server, il contenuto che l'utente genererà.

Anche per questa parte è disponibile uno zip con l'esempio studiato.

Ulteriori raffinamenti

Siamo arrivati all'ultima parte di questa serie di articoli, dedicati alla realizzazione di un semplice editor visuale, da usare per le vostre pagine web.
Come abbiamo accennato, in quest'ultima fase daremo un'aggiustata all'aspetto della nostra applicazione e cercheremo di rendere il codice prodotto il più
possibile compatibile con le direttive del W3C oltre a fare in modo che esso sia lo stesso a prescindere dal browser usato.

A tale proposito, ritorniamo brevemente alle nostre regular expression: prendendo come pietra di paragone Firefox (ma solamente perché, tra i browser più
popolari è quello che dà come risultato il codice piu conforme alle direttive W3C), possiamo facilmente notare, con l'applicazione che abbiamo costruito finora,
che, quando vogliamo applicare al nostro testo una formattazione di tipo allineamento del testo, Internet Explorer, Opera e Safari si comportano in maniera diversa sia tra di loro,
che rispetto a Firefox:

Firefox <div style="text-align: right;">test</div>
Internet Explorer <P align=right>test</P>
Opera <DIV align="right">test</DIV>

Risolvere il problema richiede ovviamente la semplice applicazione di una regular expression e in particolare della seguente:

per Internet Explorer:

replace(/(<p )(align)(=)([A-Za-z]*)(>)(.*)(</p>)/gi,"<div style="text-align: $4;">$6</div>");

per Opera:

replace(/(<div )(align)(=")([A-Za-z]*)(")(>)(.*)(</div>)/gi,"<div style="text-align:$4;">$7</div>");

Come vedete in questo caso (esempio), facciamo largo uso dei patterns, che vengono associati ai gruppi di caratteri che identifichiamo dalla
regular expression che deve "combaciare" con il nostro testo. Analogamente, e con l'idea di fare in modo che il codice prodotto sia lo stesso a prescindere dal browser che usiamo,
è tempo di fare una scelta. Se infatti torniamo brevemente sull'output che viene prodotto da Midas e da MSHTML quando applichiamo una formattazione al testo, ci accorgiamo i risultati sono
molto differenti (nella tabella abbiamo applicato l'effetto corsivo sulla parola test):

Firefox <span style="font-style: italic;">test</span>
Internet Explorer <EM>test</EM>
Opera <I>test</I>

Qui la decisione differente è giustificata da un passo che definirei direi ambiguo, delle specifiche del W3C. Tradotto letteralmente
tra le altre cose si afferma che:

EM e STRONG sono usati per indicare enfasi... Come questi elementi vengono rappresentati dipende dal tipo di applicazione. In genere (quindi non sempre, n.d.r.)
applicazioni di tipo visuale presentano il testo racchiuso tra tag EM come corsivo e il testo racchiuso tra tags STRONG in grassetto.

Alla luce di quanto leggiamo, entrambe le soluzioni sono valide, quella di Firefox perché usando gli stili in linea mantiene la linea usata per rendere gli altri tag. Quella di Explorer allo stesso
modo è corretta in quanto si suppone che se un utente vuole che parte del suo contenuto sia in grasseto (o in corsivo) vuole in qualche modo enfatizzare quella parte di testo. Per la cronaca, completamente
errata la soluzione proposta da Opera in quanto il tag <I> è deprecato dal W3C.

Per quel che ci riguarda, visto che finora abbiamo usato Firefox come petra di paragone, continuiamo ad usarlo e trasformiamo i tag STRONG, EM ed I in tag SPAN con stili in linea.

per Internet Explorer:

replace(/(<em>)(.*)(</em>)/gi,"<span style="font-style: italic;">$2</span>");

per Opera:

replace(/(<i>)(.*)(</i>)/gi,"<span style="font-style: italic;">$2</span>");

E questo è piu o meno tutto ciò che riguarda le regular expressions e il nostro tool: nella versione finale ne troverete altre ma ovviamente, sono simili a quelle che abbiamo già analizzato.
Sembra ostico ma non è cosi: vi consiglio la lettura di questo articolo e di scaricarvi il foglio riassuntivo sulle
regular expressions qui che risulterà molto utile come prima referenza da consultare durante i vostri esperimenti.

Anche l'occhio vuole la sua parte

Finalmente la parte un po' più creativa di tutto il progetto: introduciamo al posto dei bottoni tipici dei form delle semplici icone che rappresentino le azioni più comuni
da far compiere ad un editor WYSIWYG. In questo caso ho creato da me una quindicina di icone per le funzioni che ho implementato, ovviamente è estremamente semplice crearne
delle altre o modificare quelle che ho creato io!

Procediamo allora sostituendo ai tag button, le nostre icone e ad esse associamo un semplice file CSS per l'effetto rollover. Va inoltre modificato il codice HTML che
innesca la nostra funzione applicaComando() in modo che sia compatibile con i tutti i browser. Modifichiamo ancora la stessa funzione dato che, per applicare i tag headers la sintassi varia tra
Internet Explorer e il resto dei browser.

function applicaComando(cmdStr,valCmdStr){
   if (!document.getElementById("editArea").contentDocument){
     switch(valCmdStr){
       case "h1":
        valCmdStr = "heading 1";
        break;
       case "h2":
        valCmdStr = "heading 2";
        break;
       case "h3":
        valCmdStr = "heading 3";
        break;
       case "p":
        valCmdStr = "paragraph";
        break;
   }
  }
 AttivaFrame("editArea").execCommand(cmdStr,false,valCmdStr);
}

Abbiamo quindi terminato: il nostro semplice editor è pronto per essere usato (vedi esempio).

Come recupero il codice? Una semplice soluzione

Avere un editor fine a se stesso è certamente poco utile. Come recuperare il contenuto per poi farci quel che meglio crediamo? Ci sono due cose da "recuperare": il testo così com'è e quello
ripulito. Ovviamente è consigliato utilizzare la versione ripulita, non foss'altro che per essere sicuri (con minimo margine di errore, strettamente legato all'efficacia delle regular expressions) di
manipolare un codice "well formed".

Veniamo quindi al sodo: prima di tutto occorre racchiudere il nostro editor da un form che punti alla pagina che poi farà il lavoro lato server. Inoltre aggiungiamo un campo hidden che
nomineremo contenutoPulito e che useremo per spedire il testo pulito. Quello originale, invece, sarà recuperato grazie al nome del form (che sarà uguale al suo id, ovvero editArea). Eliminiamo infine i
due bottoni per visualizzare il codice prodotto e quello pulito e inseriamo un bottone, al submit del form assoceremo le azioni di pulizia del codice e faremo in modo che al campo hidden
venga assegnato il valore ottenuto dalla pulizia del codice, tutto questo modificando leggermente la nostra funziona PulisceCodice (vedi esempio).

È tutto: tengo a sottilineare che il codice, soprattutto per ciò che riguarda il controllo dell'HTML prodotto, può essere migliorato ed arricchito, ma gli spunti non si limitano a quello: ad esempio la
gestione delle immagini, dei link e delle tabelle non sono stati trattati, ma ormai avete in mano tutti gli strumenti per farlo da soli. Anche per questa terza parte è a vostra disposizione uno zip con i file HTML e le immagini utilizzate. Alla prossima!

Ti consigliamo anche