How to limit number of characters per line in text area to a fixed value
In my text area, I should be able to enter only 72 characters per line. If I use, cols property set to 72, it is allowing more or less number of characters depending on character width.
Can any one help how to do it?
Duplicate of Textarea Limit characters per line Jquery or Javascript
<TEXTAREA NAME="HARD" COLS="72" ROWS="5" WRAP="HARD">
I had the same problem and tried to solve it with JavaScript. Why not just take the HTML-Code suggested by Juan Mendes?
Well, it's quite simple: It doesn't really work cross browser, or at least with Firefox 25 under Ubuntu, the maximum number of characters per line seems to be limited by the textarea width and depending on font size I could enter +-1 letter. But I wanted the number of characters per line limited to a specific value, no matter what the textarea's width is. So I came up with this code:
var maxLength = 3;
$('#mytext').on('input focus keydown keyup', function() {
var text = $(this).val();
var lines = text.split(/(\r\n|\n|\r)/gm);
for (var i = 0; i < lines.length; i++) {
if (lines[i].length > maxLength) {
lines[i] = lines[i].substring(0, maxLength);
}
}
$(this).val(lines.join(''));
});
I have also prepared a jsFiddle. I hope this helps someone :)
And at the end just a short explanation of how this code works:
- The function waits for one of the following events: input, focus, keydown, keyup (it may look a bit unnecessary to use this many events but I tested a lot to find this combination which works crossbrowser and always fires, no matter if only single letters are entered, the key is continually pressed or text is pasted into the textarea)
- it gets the value of the textarea
- then it splits the textarea at every linebreak into a new array element
- the for loop iterates over this array and checks for every line respectively element of the array, if it exceeds the before set maxLength
- if one line exceeds the maxLength, the line is "cut off" after maxLength characters
- at the end, when there is no line left which is longer than maxLength characters, the array elements are joined together in a string again
EDIT: The only constrain I found out now, is that when entering an additional character at the beginning or within the line, the code "cuts off" the string at the end and not where the characters have been added. This won't matter in most cases but just keep it in mind :) Anyway, it should not be too difficult to change this function appropriately, but in most cases it will be waste of resources ;)
A small addition to complete a previous solution.
I also limit the number of lines.
It serves me in old systems where a comment of 4 lines is saved in 4 database entries.
<textarea id="mytext" rows = "4" style="width:300px"></textarea>
$(function() {
var maxLength = 30;
var mawRow = 4;
$('#mytext').on('input focus keydown keyup', function() {
//get Textearea text
var text = $(this).val();
//Split with \n carriage return
var lines = text.split("\n");
for (var i = 0; i < lines.length; i++) {
if (lines[i].length > maxLength) {
lines[i] = lines[i].substring(0, maxLength);
}
}
//On supprime ce qui dépasse... :)
while (lines.length > 4){
lines.pop();
}
//Join with \n.
//Set textarea
$(this).val(lines.join("\n"));
});
});
Here is a way to restrict a textarea in both characters per line and amount of lines. To also make the input interaction feel intuitive to a user it needs to handle (1) the value of the input and (2) the cursor position:
- (a) READ VALUE from the textarea, (b) DETECT IF TEXT PER LINE IS TOO LONG as required by the length restrictions, (c) PUSH OVERFLOWING TEXT from a line to the next line and (d) WRITE VALUE back to the textarea.
- (a) READ THE CURSOR POSITION to store the cursor position, and (b) POSITION THE CURSOR where a user would expect it after WRITE DATA.
Check out the codepen here: https://codepen.io/MattWritingCode/pen/bmexwa
This is the essential javascript code (tested on Safari and Chrome, it also works fine when pasting text into the textarea):
var charactersPerLine=document.getElementById("charactersPerLine").value;
var maxLines=document.getElementById("maxLines").value;
var textOutput="";
var onPaste=false;
function formatTextAsRequired() {
/*
This function handles two aspects:
1. (a) READ VALUE from the textarea, (b) DETECT IF TEXT PER LINE IS TOO LONG as required by the length restrictions, (c) PUSH OVERFLOWING TEXT from a line to the next line and (d) WRITE VALUE back to the textarea.
2. (a) READ THE CURSOR POSITION to store the cursor position, and (b) POSITION THE CURSOR where a user would expect it after WRITE DATA.
*/
var textInput=document.getElementById("flexibleInputField").value;//1a: READ VALUE
var inputAsRows=textInput.split("\n");// create array from input => each element contains one row of the textarea
var inputAsOneLine=textInput.replace(/(\r\n\t|\n|\r\t)/gm,"");//remove all line-breaks
var cursorPositionOnInput=document.getElementById("flexibleInputField").selectionStart;//2a: READ CURSOR POSITION
var cursorOffsetAfterOutput=0;//set default value for cursor offset. cursor offset is needed when re-posiotioning the cursor after WRITE DATA
var totalRows=inputAsRows.length; //don't put inputAsRows.length in the for statement, as the array is growing in the loop which results in an infinite loop
var row;
var lineBreakCount=0;
var characterCount=0;
for (row = 0; row < totalRows; ++row) {
if(inputAsRows[row].length>charactersPerLine){ //1b DETECT IF TEXT PER LINE IS TOO LONG
if (inputAsRows[row+1] === undefined) {
inputAsRows[row+1]="";// the row did not exist
totalRows++;
}
//1c PUSH OVERFLOWING TEXT: move text that is too long for this row to the next row:
inputAsRows[row+1]=inputAsRows[row].substring(charactersPerLine)+inputAsRows[row+1];
inputAsRows[row]=inputAsRows[row].substring(0,charactersPerLine);
//determine, if cursor was at the end of the line that got a line-break:
var newOutput=inputAsRows.join("\n");
if(newOutput.substr(cursorPositionOnInput-1,1)=="\n"){
cursorOffsetAfterOutput=1; }
}
}
if(inputAsRows.length<=maxLines && inputAsOneLine.length<=(maxLines*charactersPerLine)){//data is within max number of rows and max total digits
textOutput=inputAsRows.join("\n");
document.getElementById("flexibleInputField").rows=inputAsRows.length;//resize textarea
document.getElementById("errors").innerHTML="";//remove error message
document.getElementById("count").innerHTML=inputAsOneLine.length+"/"+(maxLines*charactersPerLine);//show digits count
if(onPaste){ cursorOffsetAfterOutput=cursorOffsetOnPaste(textInput,cursorPositionOnInput,totalRows)
}
}
else //data would be too long
{
document.getElementById("errors").innerHTML="This field can only have "+maxLines+" lines with "+charactersPerLine+" characters per line.";//display error message
document.getElementById("count").innerHTML="";//remove digits count
cursorOffsetAfterOutput=-1;
}
document.getElementById("flexibleInputField").value=textOutput;//1d: WRITE VALUE
document.getElementById("flexibleInputField").selectionStart=cursorPositionOnInput+cursorOffsetAfterOutput; //2b: POSITION CURSOR
document.getElementById("flexibleInputField").selectionEnd=cursorPositionOnInput+cursorOffsetAfterOutput; //set a single cursor, not a selection
onPaste=false;
}
function countLineBreaks(string,lengthFromStart){
var left=string.substr(0,lengthFromStart);
var countOfLinebreaks=(left.split("\n")).length;
return countOfLinebreaks;
}
function handlePaste(){
//some improvements when pasting content can still be made (particularly on the cursor position)
onPaste=true;
}
function cursorOffsetOnPaste(textInput,cursorPositionOnInput,totalRows){
//offset the cursor by 1 for each added line break:
var countOld=countLineBreaks(textInput,cursorPositionOnInput);
var countNew=countLineBreaks(textOutput,cursorPositionOnInput+totalRows);
cursorOffsetAfterOutput=countNew-countOld;
return cursorOffsetAfterOutput;
}
This is an old thread but i have just developed a little jQuery plugin solution. Check it out here. Find the readme for further details. My plugin has a bit more to it but the basic are as follows:
$(document).ready(function(){
var linesUsed = $('#linesUsed');
var charsUsed = $('#charsUsed');
var errorreading = $('#errors');
// HANDLES PASTE EVENTS
$('.line_control').on('paste', function (e) {
var $el = $(this);
var lines = $el.attr("lines");
var chars = $el.attr("chars");
var errors = [];
setTimeout(function (e) {
var newLines = $el.val().split("\n");
console.log(newLines);
linesUsed.text(newLines.length);
charsUsed.text(newLines[newLines.length - 1].length + 1);
for (var i = 0, len = newLines.length; i < len; i++) {
if (newLines[i].length >= chars) {
let line = i + 1;
let count = newLines[i].length;
errors.push({
'line': line,
'count': count
})
}
}
if (errors.length > 0) {
var html = '<p>Errors:</p>';
var alertMessage = "Warning!\n\nYour pasted content has exceeded the line limitations. Please review the following:\n\n"
for (var i = 0, len = errors.length; i < len; i++) {
html = html + '<span>Line: ' + errors[i]['line'] + '</span></br><span>Count: ' + errors[i]['count'] + '</span></br>'
alertMessage = alertMessage + 'Line: ' + errors[i]['line'] + ' Over: ' + (errors[i]['count'] - chars) + ' Count: ' + errors[i]['count'] + '\n';
}
alert(alertMessage);
errorreading.html(html);
}
console.log(errors);
if (newLines.length >= lines) {
linesUsed.css('color', 'red');
return false;
} else {
linesUsed.css('color', '');
}
if (newLines[newLines.length - 1].length >= chars) {
charsUsed.css('color', 'red');
return false;
} else {
charsUsed.css('color', '');
}
}, 100);
});
//HANDLES AND KEYDOWN EVENTS
$('.line_control').keydown(function (e) {
var lines = $(this).attr("lines");
var chars = $(this).attr("chars");
newLines = $(this).val().split("\n");
linesUsed.text(newLines.length);
charsUsed.text(newLines[newLines.length - 1].length + 1);
if (newLines.length > lines && e.keyCode !== 8 && e.keyCode !== 46) {
linesUsed.css('color', 'red');
return false;
} else if (e.keyCode !== 13 && e.keyCode !== 8 && e.keyCode !== 46 && newLines[newLines.length - 1].length >= chars) {
charsUsed.css('color', 'red');
return false;
} else {
linesUsed.css('color', '');
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="line_control" lines="2" chars="8" style="resize: none;"></textarea>