Bene, ho buttato giù qualcosa. Come dicevo, non è stato troppo facile. Il problema principale, oltre a suddividere la stringa secondo il limite impostato, è stato quello di reimpostare la posizione del cursore perché, di fatto, quando viene modificato da script il contenuto della textarea, la posizione del cursore normalmente si azzera (o si porta alla fine, a seconda del browser) o perde comunque la regolare posizione tenendo anche conto dei nuovi caratteri aggiunti (i ritorno a capo); va quindi ricalcolata. In realtà non sono riuscito ad ottimizzare il calcolo per alcune situazioni, ad esempio quando si utilizza il CANC a fine riga per cui il testo della riga successiva si porta in quella precedente; il cursore in tal caso si sposta in avanti di un carattere per un "errore" di calcolo che non sono riuscito a sistemare. Sono comunque errori minimi.
Per il resto dovrebbe funzionare tutto anche se può essere chiaramente migliorato ed ottimizzato. A dire il vero, ho anche aggiunto un effetto che avvisa quando il testo non può essere modificato perché si è raggiunto il limite, ad esempio se si cerca di digitare qualcosa quando il testo occupa già la lunghezza massima o il numero di righe consentite. In tal caso il testo diventa rosso per un istante, facendo un breve flash. L'effetto è impostato tramite CSS ed eventualmente è possibile personalizzarlo o escluderlo eliminando quel CSS.
Qui un esempio:
codice:
<!DOCTYPE HTML>
<html>
<head>
<title>Esempio</title>
<meta charset="utf-8">
<style type="text/css">
@keyframes errorblink { 0% { color: red; } 100% { color: #000; } }
.errorblink { animation: errorblink 500ms 1 ease; }
</style>
</head>
<body>
<textarea id="areatesto" cols="50" rows="4" ></textarea>
<script type="text/javascript">
function TestoLimitato(textarea, colonne, righe, autoNewLine){
var obj = this;
if (colonne<0 || righe<1) return;
function verificaTesto(){
var str = this.value
, lunghezzaInput = this.value.length - obj.value.length
, nuovaPosizioneCursore = this.selectionStart
, posizioneCursorePrimaDellaModifica = nuovaPosizioneCursore - lunghezzaInput
, newLines = 0
;
if (str && autoNewLine) {
// Inserimento automatico dei ritorno a capo
str = str.replace(RegExp('.{'+(colonne)+'}(?=[^\n])','g'),'$&\n');
newLines = str.split("\n").length - this.value.split('\n').length;
nuovaPosizioneCursore = this.selectionStart + newLines;
this.value = str;
}
// Verifico se la modifica effettuata rispetta i limiti impostati
if (RegExp('^.{0,'+(colonne)+'}(\n.{0,'+(colonne)+'}){0,'+(righe-1)+'}$','g').test(str)) {
// Modifica avvenuta
obj.value = str;
} else {
// Errore, non è possibile mantenere questa modifica, sarà ripristinato il precedente valore
this.value = obj.value;
nuovaPosizioneCursore = posizioneCursorePrimaDellaModifica;
this.classList.remove('errorblink');
setTimeout(function(){textarea.classList.add('errorblink');},1);
}
this.selectionStart = this.selectionEnd = nuovaPosizioneCursore;
}
obj.value = '';
textarea.addEventListener('input', verificaTesto);
verificaTesto.call(textarea);
}
new TestoLimitato(areatesto, 25, 2, true);
</script>
</body>
</html>
Per abilitare il ritorno a capo automatico, ho impostato un quarto parametro booleano da passare alla funzione. Basta quindi specificare true per abilitarlo (vedi esempio).
Testato su Win10 con FF46, CH57, ED20, IE11, O44
Per il momento è tutto.
Buon proseguimento.