Limit rows in HTML5 textarea - javascript

I'm looking for a solution to a problem, but I'm not able to find it. It could be in AngularJS or in Javascript (then I'll translate it in AngularJS). The problem is that I have to limit the rows of a simple textarea. The attribute 'rows=x' of HTML5 limit just the view. I have to limit the lines. The problem is that even if graphycally the lines go down, the components looks at it as a unique line. The user has to press ENTER to create a new line. But, I have to limit the lines.
I did this directive:
angular.module('app').directive('maxlines', function (): any {
return function (scope: any, element: any, attrs: any) {
element.bind("keydown keypress", function (event: any) {
if (event.which === 13) {
var text = element.val(),
numberOfLines = (text.match(/\n/g) || []).length + 1,
maxRows = parseInt(element.attr('rows'));
if (event.which === 13 && numberOfLines === maxRows) {
return false;
}
}
});
};
});
It works if I press ENTER, but it doesnt work if I continue to write withouth press enter.

I think you can do this with pure HTML.
<textarea cols="20" rows="5" wrap="hard" maxlength="100">
The wrap="hard" means that once the user has reached the end of the line - in this case, 20 characters - a newline will be inserted.
Once the user has reached 100 characters - the same as filling in 5 lines with 20 characters - no more input will be allowed.
Now, this doesn't stop someone from adding 10 lines with 10 characters - but does it get close to what you want to do?

This worked for me:
#your-textarea {
max-height: 5rem;
min-height: 2rem;
}
The user should be able to resize within that threshold.

Related

Basic JS: Adding characters at cursor position, while maintaining cursor position

Say I am making a basic Calculator that allows whole expressions to typed by the user. I'll use a simple <input> tag.
The user can type whatever they want, but I want to make it even easier for the client, I want to add a closing parenthesis immediately after they type (, so I set up my javascript to detect when ( is input by into the input field, then += a ) to the input field.
The problem with my code is that it adds the parenthesis to the end of the input, regardless of where they type the parenthesis, even at the beginning.
How can I add a character where ever the cursor is?
My code is as follows:
input = document.querySelector("input[type=text]");
input.addEventListener(`keyup`, (event)=>
{
if(event.key === `(`)
{
input.value+=`)`;
}
});
<input type="text" placeholder="Put mathematical expression"></input>
I believe that should solve your problem.
let input = document.querySelector("input[type=text]");
input.addEventListener(`keyup`, (event) => {
if(event.key === '(') {
let cursorPos = input.selectionStart;
input.value = input.value.substr(0, cursorPos) + ')' +
input.value.substr(cursorPos);
}
});

Insert character at Cursor position in VUE JS

I have been trying to insert emoji in textarea exactly where the cursor is at. I looked around how tos in the web could not find anything specific in VUE JS. Most of them are in plain JS.
I have this Code
<div class="picker" v-show="showPicker">
<click-outside :handler="handleClickOutside">
<picker
set ="messenger"
title="Pick your emoji…"
emoji="point_up"
#click="addEmoji"
:emoji-size="16"
>
</picker>
</click-outside>
</div>
<textarea id="greeting_text_input" class="form-control"
type="text"
v-model="greeting_text"
rows="8"
required
placeholder="Hi {first-name}! Welcome to our bot. Click on the ‘Get
Started’ button to begin
">
</textarea>
My Method
addEmoji(emoji){
this.greeting_text += emoji.native;
this.showPicker = !this.showPicker;
}
Obviously, this code will add the character (emoji, in my case) to the last of the string. I need a pure vuejs solution for this.
What would be the best practise for this kind of problem in Vue? as there are few solutions in the web that based either in vanilla JS or Jquery.
Two steps:
1 get textarea element using a vue-way:
1.1 Add ref attrbute to textarea tag in your template code:
<textarea ref="ta"></textarea>
1.2 get this element after mounted hook of this component:
let textarea = this.$refs.ta
2 get cursor position of textarea element.
let cursorPosition = textarea.selectionStart
Here is reference: ref
<!-- tag -->
<textarea ref="yourTextarea" v-model.trim="txtContent" ......></textarea>
// methods:
insertSomething: function(insert) {
const self = this;
var tArea = this.$refs.yourTextarea;
// filter:
if (0 == insert) {
return;
}
if (0 == cursorPos) {
return;
}
// get cursor's position:
var startPos = tArea.selectionStart,
endPos = tArea.selectionEnd,
cursorPos = startPos,
tmpStr = tArea.value;
// insert:
self.txtContent = tmpStr.substring(0, startPos) + insert + tmpStr.substring(endPos, tmpStr.length);
// move cursor:
setTimeout(() => {
cursorPos += insert.length;
tArea.selectionStart = tArea.selectionEnd = cursorPos;
}, 10);
}
I learned about setSelectionRange from a different question, and I used it to handle credit card number input. I will show my solution here so a person can perhaps become inspired by it.
template:
<input
ref="input"
v-model="value"
#input="handleChange"
>
instance methods:
data() {
return {
lastValue: '',
}
},
methods: {
setCursorPosition(el, pos) {
el.focus();
el.setSelectionRange(pos, pos);
},
handleChange() {
// handle backspace event
if (this.value.length < this.lastValue.length) {
this.lastValue = this.value;
this.$emit('input-changed', this.value);
return;
}
// handle value-edit event
if (this.$refs.input.selectionStart < this.value.length) {
const startPos = this.$refs.input.selectionStart;
this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
this.$nextTick(() => this.setCursorPosition(this.$refs.input, startPos));
this.lastValue = this.value;
this.$emit('input-changed', this.value);
return;
}
// handle everything else
this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
this.lastValue = this.value;
this.$emit('input-changed', this.value);
},
},
The goal with the above code is to add spaces into a credit card input, so 1234123412341234 is automatically reformatted to 1234 1234 1234 1234. A person venturing into this territory will notice that problems arise when editing the input value.
You can see there are three conditions in my sample above. The last one is the default which simply reformats the current value with a 2-step combo: remove all spaces then adds a space every 4th character.
If you comment out the two if blocks, you can watch the problems emerge.
The first if block handles the backspace event. As you can see, every time the input changes, the value is captured as this.lastValue. When you press backspace, the goal of the first condition is to NOT run the regex. In my opinion, this is better UX. If you comment out that condition, you can see.
The second if block handles the editing events. A good way to test it, is to enter a valid CC but omit the 3rd character, so that everything is off by one. Then add the character in. Everything should be good. Likewise if you backspace multiple characters out. The goal of the second condition is to properly manage the cursor position (or caret position if you prefer that nomenclature).
You can safely delete the first condition and all references to lastValue and the code will still work. This is arguably simpler but worse UX.

Allow only two types of characters to be inserted into an input field

I have a text field where I need only 1 and 0 anything else will break my logic code.
How do i possibly restrict any other character from being entered in an input field?
I looked through posts regarding on a somewhat similar subject but they allowed numbers from 0-9 and so on.
I tried using the pattern attribute in html but there is no pattern that does this, at least i haven't found it.
I have found this code:
$(function(){
$('#BinaryInputArea').bind('input', function(){
$(this).val(function(_, v){
return v.replace(/\s+/g, '');
});
});
});
which restricts SPACES from being entered, this uses again patterns that only seem to be known by veterans. I tried adding [2-9] in the .replace section but sadly it was out of the bounds of my logic.
EDIT: I am using a TextArea for input so a regular input pattern:[0-1]
wont work
You can do this with regular expressions:
var txtInput = document.getElementById("txtInput");
txtInput.addEventListener("keydown", function(evt){
var regEx = /^(0|1)$/;
// Account for two ways to press 0 and 1 on full-size keyboards
var key1 = String.fromCharCode(evt.keyCode);
var key2 = String.fromCharCode(evt.keyCode-48); // Adjustment for the keydown event
// Test key against regular expression
if(!regEx.test(key1) && !regEx.test(key2)){
evt.preventDefault();
}
});
<form>
<textarea id="txtInput"></textarea>
<button>Submit</button>
</form>
Or, you can do this by checking for specific keys being pressed:
var input = document.getElementById("txtInput");
// Do event binding in JavaScript, not in HTML
input.addEventListener("keydown", function(evt){
// Get the code for the key that was pressed
var char = evt.keyCode;
// Is the SHIFT key being held down?
if(evt.shiftKey){
// If so, cancel the event
evt.preventDefault();
} else {
// Not the SHIFT key, but if it is 48, 49, 96, 97
// (the four ways to get 0 or 1 on a keyboard with a num pad)
switch (char) {
case 48:
case 49:
case 96:
case 97:
break; // do nothing
default:
// Some other key, cancel the event.
evt.preventDefault();
break;
}
}
});
// Don't allow pasting into the field
input.addEventListener("paste", function(evt){
evt.preventDefault();
});
<form>
<textarea id="txtInput"></textarea>
<button>Submit</button>
</form>
If you want to do it using javascript you can do something like:
<input type="text" onkeypress='return event.charCode >= 48 && event.charCode <= 49'>
</input>
EDIT: Ok, my first post just pretended to give an example of how it could be done on a line. But you need to have into account a lot of details like allowing the user to use the keyboard keys, copy and paste events, delete characters, etc. You should also control if the user paste a non-valid value.
So here is a more detailed example:
In one line:
<input name="number" onkeyup="if (/[^0-1]|^0+(?!$)/g.test(this.value)) this.value = this.value.replace(/[^0-1]|^0+(?!$)/g,'')">
A jquery example:
$(document).ready(function() {
$('input.validateBinary').keyup(function(event) {
var regEx = /^(0|1)$/;
if (!regEx.test(this.value)) {
this.value = this.value.replace(/[^0-1]|^0+(?!$)/g, '');
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="text" class="validateBinary" />

Angular model variable updating too early before DOM updates, causing UI to be "behind"?

I'm trying to build an Angular Tag Directive that functions not too dissimilarity from Stack Overflow's Tag input form. It uses a div with a border to create the illusion of a form, and then a div containing a list of tags to the left of an input field where you can type:
<div class="wrapper">
<div class="tag-wrapper">
<div class="tags" ng-repeat="tag in selectedTags">
<div>
[[ tag.name ]]
<span class="remove" ng-click="removeTag(tag)"></span>
</div>
</div>
</div>
<input type="text" class="tag-input"
ng-model="tagInput"
ng-style="{ width: inputLength + 'px'}"
ng-keypress="tagInputKeyPress($event)"
ng-keyup="updateSuggestionList()"
ng-focus="toggleSuggestionVisibility()"
ng-blur="toggleSuggestionVisibility()" />
</div>
Please note I'm using [[]] as my interpolation provider for Angular because I have another templating engine already using {{}}.
When a key is pressed in the input, it runs a function to check if the key is a backspace or enter or a space to create/remove a tag:
$scope.tagInputKeyPress = function(event) {
// Currently using jQuery.event.which to detect keypresses, keyCode is deprecated, use KeyboardEvent.key eventually:
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
// event.key == ' ' || event.key == 'Enter'
if (event.which == 32 || event.which == 13) {
event.preventDefault();
// Remove any rulebreaking chars
var tag = $scope.tagInput;
tag = tag.replace(/["']/g, "");
// Remove whitespace if present
tag = tag.trim();
$scope.createTag(tag);
// event.key == 'Backspace'
} else if (event.which == 8 && $scope.tagInput == "") {
event.preventDefault();
// grab the last tag to be inserted (if any) and put it back in the input
if ($scope.selectedTags.length > 0) {
$scope.tagInput = $scope.selectedTags.pop().name;
}
}
$scope.inputLength = $(element).find('input.tag-input').parent().innerWidth() - $(element).find('.tag-wrapper').outerWidth() - 1;
return true;
};
The bit I'm having problems with is the last line before the return statement. What it is meant to do is recompute the width of the .tag-wrapper element and adjust the width of the input element to suit via this ng-style property:
ng-style="{ width: inputLength + 'px'}"
However, as it currently stands, the input length is always one step behind the UI, causing the input to overflow. This is because:
It firstly adds a new tag onto the selectedTags list.
It computes the width of .tag-wrapper.
It returns true letting the original key event pass through
Angular's digest cycle runs and appends a new tag into the DOM. The width of .tag-wrapper is now bigger, and the input overflows.
Here is an example of this:
Right now, step 4 is happening after step 2. I need step 2 to happen after step 4.
How can I achieve this?
You modify your createTag method to have a callback, and set the inputLength within the callback.
Alternative hack: wrap the setting of inputLength within a $timeout, to force the the line to be executed in the following digest call.

Getting textarea to count line breaks properly

I've been banging my head against a wall trying to work out what the best way to get a character countdown from a textarea. I've create this jsfiddle but if I paste in 4000 characters from lipsum.com then it says it's 12 characters over. I know this is because of the line breaks, but I'm unsure on how to get it to work correctly, and consistently across browsers.
$('textarea').on('keyup', function() {
characterCount(this);
});
$('textarea:not(:empty)').each(function() {
characterCount(this);
});
function characterCount(that) {
var $this = $(that),
$maxLength = $this.attr('data-val-length-max'),
$lengthOfText = $this.val().length,
$charCountEl = $this.closest('.form-group').find('.maxchar-count');
if($maxLength) {
$($charCountEl).text($maxLength - $lengthOfText);
}
if($lengthOfText > $maxLength) {
$charCountEl.addClass('has-error');
} else {
$charCountEl.removeClass('has-error');
}
}
And the markup
<div class="form-group">
<textarea rows="5" data-val-length-max="4000"></textarea>
<div>
<span class="maxchar-count">4000</span>
</div>
</div>
I've seen some similar questions but yet to find an answer that works for me.
Thanks
Henry
Try
$lengthOfText = $this.val().replace(/\n/g, '').length
Instead of
$lengthOfText = $this.val().length
This way you'll get rid of new line characters in textarea.
If you want all kind of spacing avoided from total count, you can use
$lengthOfText = $this.val().replace(/\s/g, '').length
Hope this helps.

Categories