Non ci vuole niente a modificarlo alla bisogna (in verde le aggiunte, in rosso le modifiche)…

codice:
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Esempio</title>
<script type="text/javascript">
function normalOnly(oField, oKeyEvent) {
	var	nKey = (oKeyEvent || window.event || { charCode: 0 }).keyCode,

		// qui il numero massimo di caratteri per riga:
		nCols = 30,
		// qui il numero massimo di righe:
		nRows = 3,

		nSelS = oField.selectionStart, nSelE = oField.selectionEnd,
		sVal = oField.value, nLen = sVal.length,
		nBackward = 0, nNewLine = 0, nRowStart = 0,
		aReturns = (sVal.substring(0, nSelS) + sVal.substring(nSelE, sVal.length)).match(/\n/g);

	if (nSelS >= nCols) {
		nBackward = nSelS - nCols;
		nNewLine = sVal.substring(nBackward, nSelS).search(new RegExp("\\n(?!.{0," + String(nCols - 2) + "}\\n)"));
		nRowStart = nBackward + nNewLine + 1;
	}

	var	nAfterLen = nSelE + nRowStart + nCols - nSelS,
		sRow = sVal.substring(nRowStart, nSelS) + sVal.substring(nSelE, nAfterLen > nLen ? nLen : nAfterLen),
		bKeepCols = nKey === 13 || nLen + 1 < nCols || ((nNewLine > -1 || nKey > 0) && (sRow.length < nCols || (nKey > 0 && (nLen === nAfterLen || sVal.charAt(nAfterLen) === "\n")) || /\n/.test(sRow)));

	return (nKey !== 13 || (aReturns ? aReturns.length + 1 : 1) < nRows) && ((nKey > 32 && nKey < 41) || bKeepCols);
}
</script>
</head>

<body>
<form name="myForm">


Textarea con numero fisso di caratteri per riga:

<textarea cols="50" rows="10" name="myInput" onkeypress="return(normalOnly(this, event));" onpaste="return(false);" /></textarea></p>
</form>
</body>
</html>