Angular Material TextArea set limit on number of lines entered - javascript

I don't want to know how to set the visible number of rows.
I want to restrict the number of lines of text that a user can enter into a text area.
For example I want a maximum of 10 lines.
If the user can enter 11 lines it will mess up the UI on other applications that use the data.
By lines I mean when the user enters a carriage return and starts on a new line.
If you cut and paste the text from the text area into notepad on windows there should be no more than 10 lines. e.g. This description is 14 lines long (this does not included wrapped text).
I already know how to adjust visible rows and restrict character length.
EDIT:
I have edited the title and description to try and make it clearer.

There is probably more than one way to do this, but an obvious - and relatively straightforward - one is to listen to input events on the text area and remove any line feed characters added over the limit. This is a generic solution - it isn't specific to Angular or Angular Material (but the code is TypeScript):
<textarea matInput (input)="limitLines($event, 5)"></textarea>
limitLines(event: InputEvent, maxLines: number) {
let text = (event.target as HTMLTextAreaElement).value;
if (text.length > 0) {
const lineCount = 1 + text.replace(/[^\n]/g, '').length;
if (lineCount > maxLines) {
const textArray = text.split('\n');
const newText = textArray.reduce((result, line, lineNum, array) => {
if (lineNum < maxLines) {
return result.concat('\n').concat(line);
}
return result.concat(line);
});
(event.target as HTMLTextAreaElement).value = newText;
}
}
}

you can also check by counting "\n" on the string and if the count is > than max rows then call event.preventDefault();
<textarea
(keydown.enter)="onKeydown($event)"
class="form-control"
></textarea>
onKeydown(event: Event) {
if(textBoxValue.split("\n").length >= maxRows) {
event.preventDefault()
}
}

Related

jQuery update character counter after pasting in more characters than maxlength

I have a form that with a textarea and I'm trying to prevent users from pasting more than 1000 characters into it. I have a div that displays the number of characters left as the user types, which is the problem. I have the pasting part working, but I'm also trying to set the character counter at the same time.
Here's the code:
<textarea name="Note" id="Note" cols="80" rows="5" class="text-input shadow form-control" maxlength="1000"></textarea>
<div id="NoteLength"></div>
<div>characters remaining.</div>
and the jQuery:
$("#NoteLength").text("1000");
//user is typing
$("#Note").keyup(function () {
el = $(this);
if (el.val().length >= 1000) {
el.val(el.val().substr(0, 1000));
} else {
$("#NoteLength").text(1000 - el.val().length);
}
});
//user is pasting
$("#Note").blur(function (event) {
var maxLength = 1000;
var length = this.value.length;
if (length > maxLength) {
$("#NoteLength").text("0");
this.value = this.value.substring(0, maxLength);
}
});
It's truncating the text as expected, but it's not resetting the NoteLength div to if I paste a large chunk of text, say 1500 characters. If I paste blocks of 200 characters, the counter works fine. Works if I backspace over the last character pasted in too, just not when I go way over in one paste which is unfortunately the most likely user scenario.
You can use the input event to capture any change to the value of the input by a user
$("#NoteLength").text("1000");
//user is changing the input value, typing or pasting, all of it
$("#Note").on('input', function () {
if (this.value.length > 1000) {
this.value = this.value.substr(0, 1000);
}
$("#NoteLength").text(1000 - this.value.length);
});

How to use jquery to prevent a editable div reach over our restricting length

Let's said I have the following code:
<div id="editing" contenteditable onclick="document.execCommand('selectAll',false,null)">Put text here...</div>
In this case, I want to restrict my user, for example, can only put 200 characters in it (including space), how can I restrict it? I know I might achieve it with jquery but most example on webs checks it when they click a button, and as I redesign my code with this StackOverflow question as base, it checks it when the div is clicked but with that in mind you can't alert user when the types over 200 words.
So how can I continuously checking words user type in a contenteditable div and alert them when they reach the limit?
The input event is supported with contenteditable, then just stop the event if there is too much text like so:
var el = document.getElementById('editing');
var max = 20;
el.addEventListener('input', function(e) {
if (el.innerHTML.length > max) {
el.innerHTML = el.innerHTML.substr(0, max); // just in case
alert('Not allowed more than ' + max + ' characters');
}
});
<div id="editing" contenteditable onclick="document.execCommand('selectAll',false,null)">Put text here...</div>
However, just stay away from contenteditable events. they are bad
Here is simple pure js solution: if anything is there please comment i will update my answer.. :D
function check()
{
// get the HTML value.
var content = document.getElementById('editing').innerHTML;
//Edit : replace spaces from content (ideally you have to figure out how to remove the posible html tags that user might enter)
content = content.replace(/ /g," ");
console.log(content);
// validate value againts limit say 10 or any constant value.
if(content.length > 20)
{
// alert user.
alert('limit reached');
// flush extra characters from end.
document.getElementById('editing').innerHTML = content.slice(0,content.length-1);
return;
}
}
//Edit : if you want remove placeholder from annoying user you can flush it when user gets the focus on it say
function checkPlaceholder()
{
document.getElementById('editing').innerHTML = "";
}
<div id="editing" contenteditable onInput="check();" onfocus="checkPlaceholder();">Put text here...</div>
<div id="editing" contenteditable onkeypress = "if($(this).text().length >= 200) {return false; }"onclick="alert(document.execCommand('selectAll',false,null)">Put text here...</div>
You return false onkeypress when the character count is greater than or equal to 200
// Detect when you add a character to the div.
$('div#editing').on('keydown',function(e) {
// Check if you already have 200 characters (but allow 'backspace' and 'del' keys).
if (e.keyCode != 8 && e.keyCode != 46 && $(this).text().length >= 200) {
alert('The field is limited to 200 characters');
return false; // This will make the new character to be ignored.
}
});

How to show end of the text in input after programmatically adding text?

I want to programmatically add words in input and always see the end of the text. But the problem is that when I'm adding word (like input.value += 'word') when length of text is almost the same as length of input, text inside input doesn't move, so I can see only begining of the text and ending is become hidden.
I think if I could put cursor to the end of input it will help, but none of tricks with cursor not working in Chrome, such as
input.value = input.value
or
input.setSelectionRange(input.value.length, input.value.length);
Seems to work well enough:
let input = document.getElementById('auto');
let sentence = 'Courage is the magic that turns dreams into reality.';
let index = 0;
setTimeout(function typing() {
let letter = sentence.charAt(index++);
input.blur();
input.value += letter;
input.focus();
input.setSelectionRange(index, index);
if (index < sentence.length) {
let ms = Math.floor(75 + Math.random() * 150);
setTimeout(typing, ms);
}
}, 1000);
<input id="auto" type="text">
If the input does not have focus, then Chrome will show it "scrolled" all the way to the left, showing the beginning of your input value.
If the element has focus, then you can set the selection range to move the cursor:
window.onload = function(){
var input = document.getElementById('foobar');
input.value += ' bazbat';
input.focus();
input.setSelectionRange( input.value.length - 1 );
}
<input id="foobar" value="foobar" size="6">
But it would be a very confusing and frustrating UX if the cursor is moving around while the user is attempting to type a value.
Other options to consider:
Change the width of your input to match the value. make html text input field grow as I type?
Toggle between an input and a span (edit mode vs read mode). With a span, you'd have a lot more control over how text is displayed. Jquery: Toggle between input and text on click
Use an indicator of some sort to alert the user that one of their input values was modified. This would be useful even if they can see the entire value, as they might not notice that the value was updated.

disable textarea by height not characters

So many times we want to limit how much a user can write, but here I have a special sized box that it has to fit in, so I want to disable adding more characters if it would surpass a specific height. here is what I did:
var over;
$('textarea').keypress(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
var t = $(this).val();
jQuery('<div/>', {
style: "visibility:hidden",
text: t,
id: "test"
}).appendTo('body');
var h = $('#test').height();
if(h >= 100){
over = true;
}
else{
over = false;
}
if(over){
//code goes here
}
$('#test').remove();
});
I got the limiting code (what goes where I have the "code goes here" comment) from here and it ALMOST works.
There is only one problem:
if somebody copies and pastes, it can place multiple characters and therefore still go over the limit.
How can I fix this issue?
jsfiddle
Another somewhat hacky solution could be to check scroll on key up. If scroll exists, delete the last character while scroll exists:
function restrictScroll(e){
if(e.target.clientHeight<e.target.scrollHeight){
while(e.target.clientHeight<e.target.scrollHeight){
e.target.value = e.target.value.substring(0, e.target.value.length-1);
}
}
};
document.getElementById("textareaid").addEventListener('keyup', restrictScroll);
This would work as you type and if you paste blocks of text. Large text blocks may take a little longer to loop through though. In which case you may want to split on "\n" and remove lines first, then characters.
jsfiddle
If you want your function to fire whenever the text in your field changes, you can bind it to the input and propertychange events, as per this answer:
https://stackoverflow.com/a/5494697/20578
Like this:
$('#descrip').on('input propertychange', function(e){
This will make sure your code fires when e.g. the user pastes in content using the mouse.
As for stopping them from entering content that would go over the limit, I think you have to keep track of what content they've entered yourself, and then revert their last edit if it infringed your criteria.
Note that e.g. Twitter doesn't stop the user from entering more characters if they've gone over the limit - they just tell the user they're over the limit, and tell them when they're back under. That might be the most usable design.
You may try this:
$('#descrip').bind('paste',function(e) {
var el = $(this);
setTimeout(function() {
//Get text after pasting
var text = $(el).val();
//wath yu want to do
}, 100);
};
Jsfiddle
The solution is taken from here and here. It works by binding to the paste event. Since paste event is fired before the text is pasted, you need to use a setTimeout to catch the text after pasting. There is still some rough edges (i.e. if you select text and press backspace, it does not update).
Still, Spudley comment has some valid points, that you may want to consider.
Edit:
Note on the jsfiddle: It allow you to go over the limit when pasting, but once over the limits, you cannot paste (or type) more text until you go under the limit again.
Must be taken into account that, since you are limiting the text length by the size it ocuppies after rendering (wich have it own issues as pointed by Spudley), and not a defined lenth, you can know if a text fits or not, but not know how much of the text is inside the limits, and how much is out of them.
You may consider reseting textbox value to its previous value if pasted text makes imput go over the limit, like in this one.
However, for cutting down the text after pasting so as non-fitting text is left out, but the rest of the pasted text is allowed, you need an entirely different approach. You may try iterating over the text until you find how much of the new text is enough.
By the way, line feeds and seems to cause your original script to behave weirdly.
I've been able to get the program working:
var over = false;
var num = 0;
var valid_entry = "";
$('#descrip').keyup(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
var t = $(this).val();
getMaxRow(t,key,this);
});
function getMaxRow(t,key,obj) {
jQuery('<div/>', {
"class": "hide",
text: t,
id: "test"
}).appendTo('body');
var h = $('#test').height();
$('#test').remove();
if(h >= 100){
num += 1;
if(num == 1){
var div = '<div id="message">You have run out of room</div>'
$('body').append(div);
}
over = true;
}
else{
over = false;
valid_entry = t;
if(num >= 1){
$('#message').remove();
num = 0;
}
}
if( over ) {
//Do this for copy and paste event:
while ( over ) {
//Try using a substring here
t = t.substr(0,(t.length-1));
over = getMaxRow(t,key,obj);
}
}
$(obj).val(valid_entry);
return over;
}

Autotab contenteditable divs or adding function to input type: text

I have 3 contenteditable divs. The first and third divs have a function that simulates changing the character associated with a key event, typing out a pre-programmed string as the user types, and the second div is straightforward- what the user types appears in the div. This can be seen here: http://jsfiddle.net/vRXph/4/. I've included the code for the first div here for convenience:
var list1 = "Sing in me, Muse, and through me tell the story".replace(/\s/g,
"\xA0").split("")
function transformTypedCharacter1(charStr) {
var position = $("#verse1").text().length;
if (position >= list1.length) return '';
else return list1[position];
}
function insertTextAtCursor1(text) {
var sel, range, textNode;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0).cloneRange();
range.deleteContents();
textNode = document.createTextNode(text);
range.insertNode(textNode);
// Move caret to the end of the newly inserted text node
range.setStart(textNode, textNode.length);
range.setEnd(textNode, textNode.length);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML(text);
}
}
$("#verse1").keypress(function(evt) {
if (evt.which) {
var charStr = String.fromCharCode(evt.which);
var transformedChar = transformTypedCharacter1(charStr);
if (transformedChar != charStr) {
insertTextAtCursor1(transformedChar);
evt.preventDefault();
return false;
}
}
});
I want to autotab between these divs. In the first div, the autotab function would be called after the final pre-programmed character is typed, and for the second div the autotab would be called after a certain number of characters (lets say 5 characters, just to keep it short as a test).
My first question (of 3): How do I autotab between contenteditable divs?
I have found a way to autotab between input type: text fields here: http://jsfiddle.net/Ukkmu/73/ but I cannot seem to apply this to my divs. I attempted, and failed, to do this here: http://jsfiddle.net/Ukkmu/76/.
function auTab (currentDiv, currentDivSize, currentDivLength, nextDiv) {
if(currentDivSize == currentDivLength){
$('#' + currentDiv).next().focus();
};
};
$('div[id^="verse"]').keyup(function() {
var thisDiv = $(this).attr('id');
var thisDivSize = $(this).attr('size');
var thisDivLength = $(this).val().length;
auTab(thisDiv, thisDivSize, thisDivLength);
});
As you can see, the system just ignores the autotab function. I created a size of "5" on my first div as a test. I don't know if this is possible, but I did it because I saw that the autotab function is dependent on size. My second question (of 3) is: Can I assign a size or maxlength attribute to a contenteditable div? If I can, and if the autotabbing relies on this attribute, then I would simply assign the size of the first div to be the number of characters in my pre-programmed string (and for the second div I would assign 5 characters as test, as I mentioned above).
An alternative would be to change my contenteditable divs to input type: text fields. I did this here: http://jsfiddle.net/Ukkmu/74/, but as you can see my original function that I described in the first paragraph of this question no longer works. I end up with a repeated character (the "S" from my pre-programmed string) before the first field, instead of my pre-programmed string in the first field. For this test I put the size of the field as 3, just as a test. The final version would be the size of the entire pre-programmed string. My 3rd question (of 3), if applicable: How can I apply the function that simulates changing the character associated with a key even to input type= text fields?
My application of this code is that this is an art project. As the user types, the output on the screen alternates between a classic text (in this case, Homer's The Odyssey), and what the user is actually typing.
Sorry if this is a very long post, but I wanted to include as much information as possible.
My solution, thanks to pckill, answers question 3. I used input type: text fields with the following function:
var list1 = "This is a test.".replace(/\s/g, "\xA0").split("")
function transformTypedChar1(charStr) {
var position = $("#verse1").val().length;
if (position >= list1.length) return '';
else return list1[position];
}

Categories

Resources