How to insert space every 4 characters for IBAN registering? - javascript

I'm really new in JavaScript and I would like to add to my input text, space insertion for IBAN account registering.
<input type="text" name="iban" onkeyup="if(this.value.length > 34){this.value=this.value.substr(0, 34);}" />
There is my input field; could someone tell me how I can do this?

The existing answers are relatively long, and they look like over-kill. Plus they don't work completely (for instance, one issue is that you can't edit previous characters).
For those interested, according to Wikipedia:
Permitted IBAN characters are the digits 0 to 9 and the 26 upper-case Latin alphabetic characters A to Z.
Here is a relatively short version that is similar to the existing answers:
document.getElementById('iban').addEventListener('input', function (e) {
e.target.value = e.target.value.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim();
});
<label for="iban">iban</label>
<input id="iban" type="text" name="iban" />
As stated above, the caveat is that you can't go back and edit previous characters. If you want to fix this, you would need to retrieve the caret's current position by initially accessing the selectionEnd property and then setting the caret's position after the regex formatting has been applied.
document.getElementById('iban').addEventListener('input', function (e) {
var target = e.target, position = target.selectionEnd, length = target.value.length;
target.value = target.value.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim();
target.selectionEnd = position += ((target.value.charAt(position - 1) === ' ' && target.value.charAt(length - 1) === ' ' && length !== target.value.length) ? 1 : 0);
});
<label for="iban">iban</label>
<input id="iban" type="text" name="iban" />
You will notice that there is a slight issue when the character after the caret is a space (because the space wasn't accounted for when initially retrieving the caret's position to begin with). To fix this, the position is manually incremented if the succeeding character is a space (assuming a space was actually added - which is determined by comparing the length before and after replacing the characters).

Using plain-JavaScript, I'd suggest:
function space(el, after) {
// defaults to a space after 4 characters:
after = after || 4;
/* removes all characters in the value that aren't a number,
or in the range from A to Z (uppercase): */
var v = el.value.replace(/[^\dA-Z]/g, ''),
/* creating the regular expression, to allow for the 'after' variable
to be used/changed: */
reg = new RegExp(".{" + after + "}","g")
el.value = v.replace(reg, function (a, b, c) {
return a + ' ';
});
}
var el = document.getElementById('iban');
el.addEventListener('keyup', function () {
space(this, 4);
});
JS Fiddle demo.
Somewhat belatedly, my rewrite of the above to handle strings, rather than DOM nodes:
function space(str, after) {
if (!str) {
return false;
}
after = after || 4;
var v = str.replace(/[^\dA-Z]/g, ''),
reg = new RegExp(".{" + after + "}", "g");
return v.replace(reg, function (a) {
return a + ' ';
});
}
var el = document.getElementById('iban');
el.addEventListener('keyup', function () {
this.value = space(this.value, 4);
});
JS Fiddle demo.
References:
addEventListener().
JavaScript regular expressions.

I wrote a simple function extending David's function to handle the last space. Also you can specify the separator.
function spacify(str, after, c) {
if (!str) {
return false;
}
after = after || 4;
c = c || " ";
var v = str.replace(/[^\dA-Z]/g, ''),
reg = new RegExp(".{" + after + "}", "g");
return v.replace(reg, function (a) {
return a + c;
}).replace(/[^0-9]+$/, "");
}
console.log(spacify("123123123131",4," "))
console.log(spacify("12312312313",4,"-"))

The code from Josh Crozie is really nice, but not complete.
Two issues with it;
If the caret is not at the end but e.g. at the before last position and the user starts typing, sometimes the caret doesn't stay at the before last position
Another issue is with Android 7+ devices. Those devices update the caret position slightly later, that means it needs a setTimeout() before reading the caret location
The code below is based on the code of Josh Crozie, now with the two issues mentioned above fixed and a little more verbose for readability purpose:
var isAndroid = navigator.userAgent.indexOf("ndroid") > -1;
var element = document.getElementById('iban');
element.addEventListener('input', function () {
if (isAndroid) {
// For android 7+ the update of the cursor location is a little bit behind, hence the little delay.
setTimeout(reformatInputField);
return;
}
reformatInputField();
});
function reformatInputField() {
function format(value) {
return value.replace(/[^\dA-Z]/gi, '')
.toUpperCase()
.replace(/(.{4})/g, '$1 ')
.trim();
}
function countSpaces(text) {
var spaces = text.match(/(\s+)/g);
return spaces ? spaces.length : 0;
}
var position = element.selectionEnd;
var previousValue = element.value;
element.value = format(element.value);
if (position !== element.value.length) {
var beforeCaret = previousValue.substr(0, position);
var countPrevious = countSpaces(beforeCaret);
var countCurrent = countSpaces(format(beforeCaret));
element.selectionEnd = position + (countCurrent - countPrevious);
}
}
<label for="iban">iban</label>
<input id="iban" type="text" name="iban" size="35" />

You have to capture each group of 4 digits and then put a space between each group.
$('input').blur(function () {
//Replace each group 4 digits with a group plus a space
var reformat = this.value.replace(/(\d{4})/g, function(match){
return match + " ";
});
this.value = reformat;
})
And this one updates the element while typing
//Keys pressed 0 times
var downed = 0;
$('#test').keydown(function (g) {
if(g.code.match("^Digit")){
downed++;
console.log(g)
}
if(downed == 1){
var reformat = this.value.replace(/(\d{4}\s*)/g, function(match){
//Strip spaces
if(match.match(/\s/)){return match;}
return match + " ";
});
console.log(reformat);
this.value = reformat;
//Start recount
downed = 0;
}
});
Check out the fiddle

for thousands on angular 4 in a pipe
integer = integer.replace(/[^\dA-Z]/g, '').replace(/(.{3})/g, '$1.').trim();

I need the same but for BVR/BVR+ swiss payment form.
So what I need is add a space every 5 chars but from the end of the string.
Example : "52 86571 22001 00000 10520 15992" or sometimes shorter like "843 14293 10520 15992".
So, here is the solution by reversing the string before and after adding spaces if rev=1.
function space(str, stp, rev) {
if (!str) {
return false;
}
if (rev == 1) {
str = str.split('').reverse().join('');
}
if(stp > 0) {
var v = str.replace(/[^\dA-Z]/g, ''),
reg = new RegExp(".{" + stp + "}", "g");
str = v.replace(reg, function (a) {
return a + ' ';
});
}
if (rev == 1) {
str = str.split('').reverse().join('');
}
return str;
}
Use :
var refTxt = space(refNum, 5, 1);
EDIT : PHP version added
function space($str=false, $stp=0, $rev= false) {
if(!$str)
return false;
if($rev)
return trim(strrev(chunk_split(strrev($str), $stp, ' ')));
else
return trim(chunk_split($str, $stp, ' '));
}

document.getElementById('iban').addEventListener('input', function (e) {
e.target.value = e.target.value.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim();
});
<label for="iban">iban</label>
<input id="iban" type="text" name="iban" />

This is the shortest version using JQuery on input with type number or tel:
$('input[type=number], input[type=tel]').on('input', function (e) {
e.target.value = e.target.value.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim();
});
You can also change the 4 to any other character limit you want.

onChangeText={number => {
const data =
number.length % 5 !== 4
? number
.replace(/[^\dA-Z]/g, '')
.replace(/(.{4})/g, '$1-')
.trim()
: number;
this.setState({
...this.state,
card: {...this.state.card, number: data},
});
}}
If you are trying to use for text input to adjust with credit card then this method will help you solve the backspace problem too

To Add space after 4 Digits
Useful to validate IBAN Number
document.getElementById('IBAN').addEventListener('input', function (e) {
e.target.value = e.target.value.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim();
});
<label for="IBAN">IBAN</label>
<input id="IBAN" maxlength="14" type="text" name="IBAN" />

Related

How to make .replace script "digit-case-sensitive"?

I have a simple side-project here to help me at work. What I have is a script that will replace a specified number with another.
My problem now though is, I cannot find a way to make it "digit-case-sensitive" (I'm not sure what it's called sorry), meaning, if I want to change the following, they replace only the specified and not anything else. For example:
10 = 80
1 = 75
0 = 65
The problem is, if I replace 10, there's a tendency that it will become 865.
It's changing 10 to 80 first and then the 0 to 65.
Now I really need help as to how do I make the replace script to the specified digit only and not cut the digits or take only half and change them.
Also, how can I make it so that it changes only once for 1 click of button? In this case, it's processing it twice with 1 click of button. It changes 10 to 80 first and then the 0 to 65. What I'd like is run the script only once per click. In this flawed script, it should only be 65 and not process the 0 to 65, since doing so should take 2 clicks.
Here's my sample code, there will be thousands of digits to replace once I move on from this obstacle.
function fixTextarea(textarea) {
textarea.value = textarea.value.replace("", "")
.replaceAll("10", "80")
.replaceAll("1", "75")
.replaceAll("0", "65")
};
function fixtext() {
let textarea = document.getElementById("textarea1");
textarea.select();
fixTextarea(textarea);
}
window.addEventListener('DOMContentLoaded', function(e) {
var area = document.getElementById("textarea1");
var getCount = function(str, search) {
return str.split(search).length - 1;
};
var replace = function(search, replaceWith) {
if (typeof(search) == "object") {
area.value = area.value.replace(search, replaceWith);
return;
}
if (area.value.indexOf(search) >= 0) {
var start = area.selectionStart;
var end = area.selectionEnd;
var textBefore = area.value.substr(0, end);
var lengthDiff = (replaceWith.length - search.length) * getCount(textBefore, search);
area.value = area.value.replace(search, replaceWith);
area.selectionStart = start + lengthDiff;
area.selectionEnd = end + lengthDiff;
}
};
});
<textarea id="textarea1" name="textarea1">10</textarea>
<button onclick="fixtext()">Update</button>
I apologize in advance for not being able to make myself too clear.
You could replace with searching for alternative strings and take the longest first.
For prevent changing the value again on a click, store the orginal value to a data-* attribute.
const
replace = s => s.replace(/10|1|0/g, s => ({ 10: '80', 1: '75', 0: '65' }[s]));
console.log(replace('1010'));
console.log(replace('01'));
You'll have to be a little creative, by changing "10" to something that has special meaning so that the match on "0" won't change it, then change it to your end result:
function fixTextarea(textarea) {
textarea.value = textarea.value.replace("", "")
.replaceAll("10", "xx")
.replaceAll("1", "75")
.replaceAll("0", "65")
.replaceAll("xx", "80")
};
You can do the replace in an if else if else block
function fixTextarea(textarea) {
if(textarea.value === "10"){
textarea.value = textarea.value.replace("", "").replaceAll("10", "80")
}else if(textarea.value === "1"){
textarea.value = textarea.value.replace("", "").replaceAll("1", "75")
}else{
textarea.value = textarea.value.replace("", "").replaceAll("0", "65")
}
};
function fixtext() {
let textarea = document.getElementById("textarea1");
textarea.select();
fixTextarea(textarea);
}
window.addEventListener('DOMContentLoaded', function(e) {
var area = document.getElementById("textarea1");
var getCount = function(str, search) {
return str.split(search).length - 1;
};
var replace = function(search, replaceWith) {
if (typeof(search) == "object") {
area.value = area.value.replace(search, replaceWith);
return;
}
if (area.value.indexOf(search) >= 0) {
var start = area.selectionStart;
var end = area.selectionEnd;
var textBefore = area.value.substr(0, end);
var lengthDiff = (replaceWith.length - search.length) * getCount(textBefore, search);
area.value = area.value.replace(search, replaceWith);
area.selectionStart = start + lengthDiff;
area.selectionEnd = end + lengthDiff;
}
};
});
<textarea id="textarea1" name="textarea1">10</textarea>
<button onclick="fixtext()">Update</button>

How to replace double quotes nested in another double quotes with single quotes

I'm currently using a replace script to auto fix both single quotes and double quotes.
However, I cannot find a solution to change a double quote nested anywhere inside another double quotes
So that:
“Here’s some extra text at the beginning “abc” and at the end”
should be
“Here’s some extra text at the beginning ‘abc’ and at the end”
Currently, I can only auto-fix this type if it's beside the other quotes (e.g. ““abc””) using a simple replace script
.replaceAll("““", "“‘") and .replaceAll("””", "’”")
Is it possible to use regex to target any double quotes nested inside another?
Note: It's important that the quotes are curly (“” and ‘’) and not the straight ones ("" and '').
function fixTextarea(textarea) {
textarea.value = textarea.value.replace(" ,", ",")
.replaceAll(" ;", ";")
.replaceAll(" .", ".")
.replaceAll(" ", " ")
.replaceAll(" ", " ")
.replaceAll("“ ", "“")
.replaceAll(" ”", "”")
.replaceAll("““", "“‘")
.replaceAll("””", "’”")
.replaceAll(/(^|[-\u2014\s(\["])'/g, "$1\u2018")
.replaceAll(/'/g, "\u2019")
.replaceAll(/(^|[-\u2014/\[(\u2018\s])"/g, "$1\u201c")
.replaceAll(/"/g, "\u201d")
};
function fixtext() {
let textarea = document.getElementById("textarea1");
textarea.select();
fixTextarea(textarea);
}
window.addEventListener('DOMContentLoaded', function(e) {
var area = document.getElementById("textarea1");
var getCount = function(str, search) {
return str.split(search).length - 1;
};
var replace = function(search, replaceWith) {
if (typeof(search) == "object") {
area.value = area.value.replace(search, replaceWith);
return;
}
if (area.value.indexOf(search) >= 0) {
var start = area.selectionStart;
var end = area.selectionEnd;
var textBefore = area.value.substr(0, end);
var lengthDiff = (replaceWith.length - search.length) * getCount(textBefore, search);
area.value = area.value.replace(search, replaceWith);
area.selectionStart = start + lengthDiff;
area.selectionEnd = end + lengthDiff;
}
};
});
<textarea class="lined" id="textarea1" name="textarea1" spellcheck="true" placeholder="" onpaste="console.log('onpastefromhtml')"></textarea>
<br><br>
<button onclick="fixtext()"> Fixit</button>
You could keep track of the depth of the nesting of quotes (whether or not double), and replace them in such a way that on even depths they are double quotes and on odd depths they are single quotes. One precaution is to exclude the apostrophe in ’s (There might be a few other exceptional cases where the apostrophe should not be taken as the close of a quote):
let s = "“Here’s some extra text at the beginning “abc” and at the end”";
let depth = 0;
let result = s.replace(/[“”‘]|’(?!s)/g, m =>
"“‘".includes(m) ? "“‘"[depth++] : "”’"[--depth]);
console.log(result);
NB: This will even fix some cases where quotes are wrongly paired.
It could be integrated into your current function like this:
function fixTextarea(textarea) {
let depth = 0; // <-- added
textarea.value = textarea.value.replace(" ,", ",")
.replaceAll(" ;", ";")
.replaceAll(" .", ".")
.replaceAll(" ", " ")
.replaceAll(" ", " ")
.replaceAll("“ ", "“")
.replaceAll(" ”", "”")
// (removed two lines here)
.replaceAll(/(^|[-\u2014\s(\["])'/g, "$1\u2018")
.replaceAll(/'/g, "\u2019")
.replaceAll(/(^|[-\u2014/\[(\u2018\s])"/g, "$1\u201c")
.replaceAll(/"/g, "\u201d")
// added:
.replace(/[“”‘]|’(?!s)/g, m =>
"“‘".includes(m) ? "“‘"[depth++] : "”’"[--depth])
};
I didn't check the other replacements you are doing in detail, but I have some doubts about these two:
.replaceAll(/'/g, "\u2019")
.replaceAll(/"/g, "\u201d")
These calls replace straight quotes by curly closing quotes, which would obviously create results where you don't have matching opening quotes...
I have a similar concern about the two other replacements that are made near those two.
Try this:
console.log("“Here’s some extra text at the beginning “abc” and at the end”"
.replace(/(“.*?)“(.*?)”(.*”)/, "$1‘$2’$3"));
Example at regex101
Ok, my previous answer sucked.
Now just for fun: a nested quote parsing utitlity for straigth and curly quotes without any RegExp.
const {
nestedDoubleQuotes2Single,
nestedSingleQuotes2Double
} = nestedQuotesParser();
const singleCurly2Double = `‘Here’s some extra ‘text’ at the beginning ‘abc’ and at the end ’`;
const doubleCurly2Single = "“Here’s some extra “text” at the beginning “abc” and at the end ”";
const doubleStraight2Single = '"Here\'s some extra "text" at the beginning "abc" and at the end. "';
const singleStraight2Double = "'Here's some extra 'text' at the beginning 'abc' and at the end'";
console.log(nestedDoubleQuotes2Single(doubleCurly2Single, true));
console.log(nestedDoubleQuotes2Single(doubleStraight2Single));
console.log(nestedSingleQuotes2Double(singleStraight2Double));
console.log(nestedSingleQuotes2Double(singleCurly2Double, true));
function nestedQuotesParser() {
let isCurly = false;
const quoting = {
get all() {
return isCurly ? `“”‘’` : `""'`;
},
get single() {
return isCurly ? `‘’` : `''`;
},
get double() {
return isCurly ? `“”` : `""`;
},
};
const reQuotDouble = m => quoting.single[quoting.all.indexOf(m)] || m;
const reQuotSingle = m => quoting.double[quoting.all.indexOf(m) - 2] || m;
const checkNestedSingle = (s, chr, i) =>
~quoting.all.indexOf(chr) && (i < s.length - 2 && (s[i - 1] === " " || s[i + 1] === " "));
return {
nestedDoubleQuotes2Single: (s, curly = false) => {
isCurly = curly;
return s.split("")
.reduce((acc, chr, i) =>
acc + (i > 0 && i < s.length - 1 &&
~quoting.all.indexOf(chr) ? reQuotDouble(chr) : chr), '');
},
nestedSingleQuotes2Double: (s, curly = false) => {
isCurly = curly;
return s.split("").reduce((acc, chr, i) =>
acc + (s.length - i < s.length - 1 && checkNestedSingle(s, chr, i) ?
curly && reQuotSingle(chr) || '"' :
chr), "");
},
};
}
Your code, using this utility:
function fixTextarea(textarea) {
let depth = 0; // <-- added
const value = textarea.value.replace(" ,", ",")
.replaceAll(" ;", ";")
.replaceAll(" .", ".")
.replaceAll(" ", " ")
.replaceAll(" ", " ")
.replaceAll("“ ", "“")
.replaceAll(" ”", "”")
.replaceAll(/(^|[-\u2014\s(\["])'/g, "$1\u2018")
.replaceAll(/'/g, "\u2019")
.replaceAll(/(^|[-\u2014/\[(\u2018\s])"/g, "$1\u201c")
.replaceAll(/"/g, "\u201d");
textarea.value = nestedDoubleQuotes2Single(value, true);
};

How to replace only current wrong character on keyup for regex?

I wish to replace any character not in the passport format(A9999999) from my input text. I have written the following (jsfiddle here):
HTML
Doc Type <input id='docType' value = 'PASS'/> <br>
Doc ID <input id='docId'/>
JS:
$(document).ready(function () {
var docTypeVal = $("#docType").val();
$('#docId').keyup(function() {
if(docTypeVal == "PASS") {
var $th = $(this);
$th.attr("maxlength","8");
if($th.val().length <= 1) {
$th.val().replace(/[^a-zA-Z]/g, function(str) {
alert('You typed " ' + str + ' ".\n\nPlease use correct format.');
return '';
})
}
else if($th.val().length <= 8 && $th.val().length > 1) {
$th.val().replace(/^(?!.*^([a-zA-Z]){1}([0-9]){7}$)/, function(str) {
alert('You typed " ' + str + ' ".\n\nPlease use correct format.');
return '';
})
}
}
});
});
However, firstly, this doesn't replace any characters (wrong/right). Secondly, it gives the alert the moment I enter 2nd character onwards. It should accept the 1st char if it is alphabet (replace otherwise), from 2nd till 8th char it should accept only numbers (replace otherwise).
You can test this with 1 Regular Expression.
/^[a-z]?\d{0,7}$/i
This pattern will look A9 up to A9999999. It will fail on AA or 99.
Example: https://jsfiddle.net/Twisty/awL0onjg/20/
JavaScript
$(function() {
var docTypeVal = $("#docType").val();
$('#docId').keyup(function(e) {
var exc = [
11, // Tab
127, // Del
];
if (exc.indexOf(e.which) > -1) {
return true;
}
var term = $(this).val();
var re = /^[a-z]?\d{0,7}$/i;
console.log("Testing:", term, re.test(term));
if (re.test(term)) {
$(this).removeClass("invalid");
return true;
} else {
$(this).addClass("invalid");
}
});
});
Consider using .keydown() if you want to prevent the User from typing outside of that pattern. See More.
Fiddle Demo
Referred the JS of Twisty's and realized it should be as shown below:
if(docTypeVal == "PASS") {
$(this).attr("maxlength","8");
var term = $(this).val();
var re = /^[a-zA-Z]{1}\d{0,7}$/i;
if (re.test(term)) {
$(this).removeClass("invalid");
return true;
} else {
$(this).addClass("invalid");
$(this).val(term.replace(term.charAt(term.length-1),
function(str) {
alert('You typed " ' + str + ' ".\n\nPlease use correct format.');
return '';
}));
return false;
}
}

Stop users from entering text into an textarea field after a certain amount of characters have been entered

I'm trying to stop users from entering data into a textarea after the character count has reached its limit. And is it possible for the character count to keep track of how many characters have been entered even after the page has been refreshed?
Jquery
$('.character-count').text('4000 characters left');
$('#description').keyup(function(){
var max = 4000;
var len = $(this).val().length;
var ch = max - len;
$('.character-count').text(ch + ' characters left');
});
Try this:
var max = 10;
$('#description')
.keydown(function(event){
if (event.keyCode != 8 && event.keyCode != 46 && $(this).val().length >= max) {
event.preventDefault();
}
})
.keyup(function(){
var $this = $(this),
val = $this.val().slice(0, max),
val_length = val.length,
left = max - val_length;
$('.character-count').text(left + ' characters left');
$this.val(val);
});
After each keyup textarea value is cut off after max symbols.
Keydown event is blocking after value length reached max,but you can still press backspace (8) or delete (46) keys.
use local storage in order to deal with later use
HTML
<textarea name="userText" id="description" cols="30" rows="10"></textarea>
<div class="character-count"></div>
jQuery
$(function(){
var comment = $('#description');
var val = localStorage.getItem( comment.attr('id') ),
max = 4000,
len = val.length,
left = ( max - len ) ;
// if the value exists, set it
if (val) {
comment.val(val);
$('.character-count').text( left + ' Character(s) left');
}
});
$('#description').on('keyup', function (e) {
var $this = $(this),
max = 4000,
val = $this.val(),
val_length = val.length,
remain = ((max - val_length)>=0)?(max - val_length):0;
$('.character-count').text(remain + ' Character(s) left');
if( val_length > max ){
$this.val(val.slice(0,max));
}
// let's check if localStorage is supported
if (window.localStorage) {
localStorage.setItem($(this).attr('id'), $(this).val());
}
});
SEE DEMO
There is a built-in HTML5 attribute maxlength, which will restrict the count of entered symbols. There is no need to reinvent it yourself:
$("input").on('input', function() {
var left = this.maxLength - this.value.length;
$("span").text(left + " characters left");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" maxlength="5"/>
<span>5 characters left</span>
Also, if you use keyup handler, then the user can easily type your text somewhere else and paste as many characters as he wants. Use input.
Talking about "to keep track of how many characters have been entered even after the page has been refreshed", you may use cookies, but it is a doubtful feature. Any user can easily replace or empty any cookies or other local storage. If this limitation is really important, implement it server-side.
You can save the entire text entered by the user in the cookie as suggested by coolguy and on refresh you can just retrieve the cookie data and display in the text area as well as the character count tracker.
<script>
$(function () {
var descText = $.cookie('description');
var descTextArea = $('#description');
var maxLength = descTextArea.data('maxLength');
if (descText) {
descTextArea.text(descText);
$('.character-count').text((maxLength - descText.length) + ' characters left');
}
else {
$('.character-count').text(maxLength + ' characters left');
}
descTextArea.keydown(function (event) {
var max = maxLength;
var len = $(this).val().length;
var ch = max - len;
if (ch < 0 ) {
event.preventDefault();
return false;
}
$('.character-count').text(ch + ' characters left');
$.cookie('description', $(this).val(), {path: '/'});
});
});
</script>
Using keydown after referring answer by legotin.
HTML file is:
<textarea id="description" data-max-length="4000"></textarea>
<span class="character-count"></span>
I am using jquery.cookie plugin.
Use e.preventDefault() to stop handling.
e.preventDefault();
$('.character-count').html('20 characters left');
var len = 0;
var max = 20;
$('#description').keydown(function (e) {
var code = e.keyCode;
if(len == max && code != 8)
{
e.preventDefault();
return false;
}
len = $(this).val().length;
var ch = max - len;
$('.character-count').text(ch + ' characters left');
});
try this
Fiddle

Angularjs ng-bind-html with custom Filter

I am currently working with ng-bind-html. Basically, what I am trying to do is, when I post a blog, the blog contains links and other styling. So when I am trying to show the list of blogs, I am using ng-bing-html like this:
<p ng-bind-html="blog.blogContent"></p>
which works fine.
But in addition, I try to truncate the blog and show only few paragraphs with view more option by passing a custom filter. But when I pass the filter I get the following:
<p ng-bind-html="blog.blogContent | Truncate"></p>
Error: [$sanitize:badparse] The sanitizer was unable to parse the
following block of html: <a href="https:.......
My Filter looks like this:
return function (text, length, end) {
if (text !== undefined) {
if (isNaN(length)) {
length = 450;
}
if (end === undefined) {
end = ".......";
}
if (text.length <= length || text.length - end.length <= length) {
return text;
} else {
return String(text).substring(0, length - end.length) + end;
}
}
You can solve this using custom directives and filters. try this one: https://stackoverflow.com/a/45076560/6816707
I used the solution posted by Minouris in this post (Javascript truncate HTML text) and adapted it into an AngularJS filter. It seems to work pretty well. The filter is
angular.module('plunker').filter('Truncate', function() {
return function(text, length, end) {
if (text !== undefined) {
if (isNaN(length)) {
length = 20;
}
if (end === undefined) {
end = ".......";
}
if (text.length <= length || text.length - end.length <= length) {
return text;
}
var truncated = text.substring(0, length);
// Remove line breaks and surrounding whitespace
truncated = truncated.replace(/(\r\n|\n|\r)/gm,"").trim();
// If the text ends with an incomplete start tag, trim it off
truncated = truncated.replace(/<(\w*)(?:(?:\s\w+(?:={0,1}(["']{0,1})\w*\2{0,1})))*$/g, '');
// If the text ends with a truncated end tag, fix it.
var truncatedEndTagExpr = /<\/((?:\w*))$/g;
var truncatedEndTagMatch = truncatedEndTagExpr.exec(truncated);
if (truncatedEndTagMatch != null) {
var truncatedEndTag = truncatedEndTagMatch[1];
// Check to see if there's an identifiable tag in the end tag
if (truncatedEndTag.length > 0) {
// If so, find the start tag, and close it
var startTagExpr = new RegExp(
"<(" + truncatedEndTag + "\\w?)(?:(?:\\s\\w+(?:=([\"\'])\\w*\\2)))*>");
var testString = truncated;
var startTagMatch = startTagExpr.exec(testString);
var startTag = null;
while (startTagMatch != null) {
startTag = startTagMatch[1];
testString = testString.replace(startTagExpr, '');
startTagMatch = startTagExpr.exec(testString);
}
if (startTag != null) {
truncated = truncated.replace(truncatedEndTagExpr, '</' + startTag + '>');
}
} else {
// Otherwise, cull off the broken end tag
truncated = truncated.replace(truncatedEndTagExpr, '');
}
}
// Now the tricky part. Reverse the text, and look for opening tags. For each opening tag,
// check to see that he closing tag before it is for that tag. If not, append a closing tag.
var testString = reverseHtml(truncated);
var reverseTagOpenExpr = /<(?:(["'])\w*\1=\w+ )*(\w*)>/;
var tagMatch = reverseTagOpenExpr.exec(testString);
while (tagMatch != null) {
var tag = tagMatch[0];
var tagName = tagMatch[2];
var startPos = tagMatch.index;
var endPos = startPos + tag.length;
var fragment = testString.substring(0, endPos);
// Test to see if an end tag is found in the fragment. If not, append one to the end
// of the truncated HTML, thus closing the last unclosed tag
if (!new RegExp("<" + tagName + "\/>").test(fragment)) {
truncated += '</' + reverseHtml(tagName) + '>';
}
// Get rid of the already tested fragment
testString = testString.replace(fragment, '');
// Get another tag to test
tagMatch = reverseTagOpenExpr.exec(testString);
}
return truncated;
}
}
function reverseHtml(str) {
var ph = String.fromCharCode(206);
var result = str.split('').reverse().join('');
while (result.indexOf('<') > -1) {
result = result.replace('<',ph);
}
while (result.indexOf('>') > -1) {
result = result.replace('>', '<');
}
while (result.indexOf(ph) > -1) {
result = result.replace(ph, '>');
}
return result;
}
});
Working plunkr:
http://plnkr.co/edit/oCwmGyBXB26omocT2q9m?p=preview
I havent tested the above solution and you may run into issues with more complicated HTML strings. May I suggest using a Jquery library like https://github.com/pathable/truncate to be safe?

Categories