Is it possible to add a suffix to a jQuery mask? - javascript

I'm using Igor Escobar's jQuery mask plugin (https://github.com/igorescobar/jQuery-Mask-Plugin) and I'm trying to add a suffix to a mask to show a % sign at the end of the input value.
The application I'm building is for Brazilian users, then I have to use this number format: 000.000,00. Where , (comma) is the decimal separator and . (dot) is the thousand separator.
I wrote the following code to apply the mask, not yet appending the % sign at the end of it.
$('.mask-decimal').each(function () {
var $self = $(this);
var precision = parseInt($self.data('precision'), 10) || 2;
var mask = '#.##0,' + (new Array(precision + 1).join('0'));
$self.mask(mask, {
reverse : true,
maxlength : false
});
$self.on('keyup', function () {
var val = this.value;
if (val) {
if (val.length <= precision) {
while (val.length < precision) {
val = '0' + val;
}
val = '0,' + val;
} else {
var parts = val.split(',');
parts[0] = parts[0].replace(/^0+/, '');
if (parts[0].length === 0) {
parts[0] = '0';
}
val = parts.join(',');
}
this.value = val;
}
});
});
This mask works perfectly.
What I tried to do was add the percent symbol at the end of the mask, then remove it just in the beginning of my keyup handler, and finally append it again before return the value. I basically changed these lines:
var mask = '#.##0,' + (new Array(precision + 1).join('0')) + '%';
var val = this.value.replace('%', '');
this.value = val + '%';
After change that, I can input a value in the field, but I can't clear/change it using backspace. If I'm fast enough I can select the entire content and delete it, but it's an awful solution for regular users. It happens this way because the cursor stays always at the end of the input and the keyup event is triggered very quickly.
With that in mind, there is a way to move the cursor to a specific position inside the input? Does somebody can see another way to change my function so the user can use the backspace to clear the input contents?

If this is for an input field and the % sign is just for decoration, you could position a span containing the symbol above the input. Because it's purely a display item, right? Or you could have the sign after the input.
There's absolutely no need to have it in the input if the user's not supposed to be able to edit it. It's counterintuitive both for the user and for your logic.
Here I put the sign inside a label just so that clicking it will activate the input field.
label, input {
font-size:18px;
font-family:sans-serif;
}
label {
margin-left:-24px;
}
input {
text-align:right;
padding-right:24px;
}
<input type="text" id="number" value="123">
<label for="number">%</label>

Related

Javascript credit card field - Add space every four chars - Backspace not working properly

I have a credit card field that I want to handle while the user inputs its credit card number.
Assumptions are that the user can enter digits and alphabetic characters, and a space must be added every four characters.
The input part works fine, but I have problems with backspace. Deleting with the backspace key works if I the cursor is on a digits, but it does not work fine when the cursor is on a space: in this case the user must hold backspace to properly delete some input.
An additional requirement is to let clipboard actions (copy, cut, paste) work properly on that field.
I cannot use any plugin for the solution (like the JQuery Mask Plugin), and I won't use keyCode directly, if possible.
Updated
JS Fiddle: https://jsfiddle.net/ot2t9zr4/10/
Snippet
$('#credit-card').on('keypress change blur', function () {
$(this).val(function (index, value) {
return value.replace(/[^a-z0-9]+/gi, '').replace(/(.{4})/g, '$1 ');
});
});
$('#credit-card').on('copy cut paste', function () {
setTimeout(function () {
$('#credit-card').trigger("change");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container">
<form class="" action="" method="post">
<fieldset>
<legend>Payment</legend>
<div class="beautiful-field field-group credit-cart">
<label class="label" for="credit-card">Credit card</label>
<input class="field" id="credit-card" value="" autocomplete="off" type="text" />
</div>
</fieldset>
</form>
</div>
Bind keypress event only and see.
$('#credit-card').on('keypress change', function () {
$(this).val(function (index, value) {
return value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ');
});
});
Check here.
Steve Davies already pointed it out, but if you only reformat the whole value with replace(), the caret position will always go at the end of the input value which can be annoying if the user edits what he previously entered. It will lead to a bad user experience if the caret position is elsewhere or a selection has been made in order to replace it with a new digit.
That being said, a good way to get rid of that behavior is to create a custom replace function with a for loop that goes through each character, then you will be able to know if the space inserted is before the current caret position and update the position if it's the case.
Pure javascript solution: https://jsfiddle.net/pmrotule/217u7fru/.
EDIT: I added support for the American Express format (15 digits instead of 16).
input_credit_card = function(jQinp)
{
var format_and_pos = function(input, char, backspace)
{
var start = 0;
var end = 0;
var pos = 0;
var value = input.value;
if (char !== false)
{
start = input.selectionStart;
end = input.selectionEnd;
if (backspace && start > 0) // handle backspace onkeydown
{
start--;
if (value[start] == " ")
{ start--; }
}
// To be able to replace the selection if there is one
value = value.substring(0, start) + char + value.substring(end);
pos = start + char.length; // caret position
}
var d = 0; // digit count
var dd = 0; // total
var gi = 0; // group index
var newV = "";
var groups = /^\D*3[47]/.test(value) ? // check for American Express
[4, 6, 5] : [4, 4, 4, 4];
for (var i = 0; i < value.length; i++)
{
if (/\D/.test(value[i]))
{
if (start > i)
{ pos--; }
}
else
{
if (d === groups[gi])
{
newV += " ";
d = 0;
gi++;
if (start >= i)
{ pos++; }
}
newV += value[i];
d++;
dd++;
}
if (d === groups[gi] && groups.length === gi + 1) // max length
{ break; }
}
input.value = newV;
if (char !== false)
{ input.setSelectionRange(pos, pos); }
};
jQinp.keypress(function(e)
{
var code = e.charCode || e.keyCode || e.which;
// Check for tab and arrow keys (needed in Firefox)
if (code !== 9 && (code < 37 || code > 40) &&
// and CTRL+C / CTRL+V
!(e.ctrlKey && (code === 99 || code === 118)))
{
e.preventDefault();
var char = String.fromCharCode(code);
// if the character is non-digit
// -> return false (the character is not inserted)
if (/\D/.test(char))
{ return false; }
format_and_pos(this, char);
}
}).
keydown(function(e) // backspace doesn't fire the keypress event
{
if (e.keyCode === 8 || e.keyCode === 46) // backspace or delete
{
e.preventDefault();
format_and_pos(this, '', this.selectionStart === this.selectionEnd);
}
}).
on('paste', function()
{
// A timeout is needed to get the new value pasted
setTimeout(function()
{ format_and_pos(jQinp[0], ''); }, 50);
}).
blur(function() // reformat onblur just in case (optional)
{
format_and_pos(this, false);
});
};
input_credit_card($('#credit-card'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container">
<form class="" action="" method="post">
<fieldset>
<legend>Payment</legend>
<div class="beautiful-field field-group credit-cart">
<label class="label" for="credit-card">Credit card</label>
<input class="field" id="credit-card" value="" autocomplete="off" type="text" />
</div>
</fieldset>
</form>
</div>
Since I cannot just reply to Developer107's comment; If you only want digits (with regex and don't want to specify it on the field. You can do it like this:
$('#credit-card').on('keypress change', function () {
$(this).val(function (index, value) {
return value.replace(/[^0-9]/g, "").replace(/\W/gi, '').replace(/(.{4})/g, '$1 ');
});
});
https://jsfiddle.net/ot2t9zr4/4/
I wanted to share my solution, in case someone is still struggling to achieve the desired affect.
My code is a refined version of #tonybrasunas's answer. It will add spaces every 4 characters, filter out non-numerical characters, fix character position, backspace, and only move the cursor forward if the character is valid, but still allow pushing with valid characters.
// FORMAT CC FIELD
//
$('#credit-card').on('input', function () {
$(this).val(function (index, value) {
// Store cursor position
let cursor = $(this).get(0).selectionStart;
// Filter characters and shorten CC (expanded for later use)
const filterSpace = value.replace(/\s+/g, '');
const filtered = filterSpace.replace(/[^0-9]/g, '');
const cardNum = filtered.substr(0, 16);
// Handle alternate segment length for American Express
const partitions = cardNum.startsWith('34') || cardNum.startsWith('37') ? [4,6,5] : [4,4,4,4];
// Loop through the validated partition, pushing each segment into cardNumUpdated
const cardNumUpdated = [];
let position = 0;
partitions.forEach(expandCard => {
const segment = cardNum.substr(position, expandCard);
if (segment) cardNumUpdated.push(segment);
position += expandCard;
});
// Combine segment array with spaces
const cardNumFormatted = cardNumUpdated.join(' ');
// Handle cursor position if user edits the number later
if (cursor < cardNumFormatted.length - 1) {
// Determine if the new value entered was valid, and set cursor progression
cursor = filterSpace !== filtered ? cursor - 1 : cursor;
setTimeout(() => {
$(this).get(0).setSelectionRange(cursor, cursor, 'none');
});
}
return cardNumFormatted;
})
});
//
// END OF FORMAT CC FIELD
An Answer for 2021: Handling Backspace, Cursor Position, and American Express correctly
To handle Backspace and cursor arrows, we have to store the original cursor position and restore it with a setTimeout() when editing a spot anywhere other than the end of the string.
For American Express, we set up partitions to handle the 4-6-5 spacing format for Amex and the 4-4-4-4 spacing for all other cards. And we loop through them to add spaces.
$('#credit-card').on('keyup', function () {
$(this).val(function (index, value) {
const selectionStart = $(this).get(0).selectionStart;
let trimmedCardNum = value.replace(/\s+/g, '');
if (trimmedCardNum.length > 16) {
trimmedCardNum = trimmedCardNum.substr(0, 16);
}
/* Handle American Express 4-6-5 spacing format */
const partitions = trimmedCardNum.startsWith('34') || trimmedCardNum.startsWith('37')
? [4,6,5]
: [4,4,4,4];
const numbers = [];
let position = 0;
partitions.forEach(partition => {
const part = trimmedCardNum.substr(position, partition);
if (part) numbers.push(part);
position += partition;
});
const formattedCardNum = numbers.join(' ');
/* Handle caret position if user edits the number later */
if (selectionStart < formattedCardNum.length - 1) {
setTimeout(() => {
$(this).get(0).setSelectionRange(selectionStart, selectionStart, 'none');
});
};
return formattedCardNum;
})
});
If you have a routine of your own to detect American Express numbers, use it. This simply examines the first two digits and compares to PAN/IIN standards.
I also posted an answer on how to do this in an Angular application.
I solved this in Vue JS by creating a custom on-change handler. Rather than show it here, I will provide a link to that solution: Javascript: Set cursor position when changing the value of input
Based on my research, it is required to manage the position of the cursor yourself if you wish to fully-support editing with good UX.
pmrotule's vanilla JavaScript solution is great, but mine is drastically simpler, so it could be worthwhile to examine.
function cc_format(value) {
var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
var matches = v.match(/\d{4,16}/g);
var match = matches && matches[0] || ''
var parts = []
for (i=0, len=match.length; i<len; i+=4) {
parts.push(match.substring(i, i+4))
}
if (parts.length) {
return parts.join(' ')
} else {
return value
}
}
Use
$('#input-cc-number').on('keyup',function() {
var cc_number = cc_format($(this).val());
$('#input-cc-number').val(cc_number);
});
Your issue at its core is that when an input field's value is updated using JavaScript, the cursor/selection position is set to the end of the string.
When user-input is appending to the end, this is fine, but if deleting, or inserting digits in the middle, this becomes quite annoying as you have observed. One way to deal with this would be to save and restore the cursor position within the field before and after each edit.
Crudely done:
$('#credit-card').on('keyup keypress change', function () {
var s = this.selectionStart, e = this.selectionEnd;
var oldleft = $(this).val().substr(0,s).replace(/[^ ]/g, '').length;
$(this).val(function (index, value) {
return value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ');
});
var newleft = $(this).val().substr(0,s).replace(/[^ ]/g, '').length;
s += newleft - oldleft;
e += newleft - oldleft;
this.setSelectionRange(s, e);
});
This is not a full solution as the s and e positions will need updating if your code inserts/removes characters that result in these locations being moved.
You could also significantly optimise this by not setting val() if no update is required.
$('.credit-card').keyup(function (e) {
if (e.keyCode != 8) {
if ($(this).val().length == 4) {
$(this).val($(this).val() + " ");
} else if ($(this).val().length == 9) {
$(this).val($(this).val() + " ");
} else if ($(this).val().length == 14) {
$(this).val($(this).val() + " ");
}
}
});
This should work fine its just for card with format 4444 5555 2222 1111 and back space works correctly
To me #pmrotule answer was the best answer so far in this post. Good credit card spacing apparently helps conversion rate in shopping carts according to extensive research produced by Stripe.
Additionally however, setting the "type" attribute of the html input field to "tel" is also important, as well as inputmode="numeric" as well as setting the auto complete type cc-number, all also help improve checkout conversion rate. Especially on mobile when the user gets a numeric keyboard instead of qwerty.
Unfortunately this breaks #pmrotule's code. On another SO post I found out about Cleave.js which is a well tested library for this purpose & plays nicely with the tel input type: https://github.com/nosir/cleave.js
Putting it all together:
<input class="field" id="number" type="tel" inputmode="numeric" autocomplete="cc-number" />
with JS:
var cleave = new Cleave('#number', {
creditCard: true,
onCreditCardTypeChanged: function (type) {
console.log('The detected card type is: '+type);
}
});

Limit number of characters in input fields without editing HTML

I'm building a database of people on a WordPress website using this plugin.
I want to limit the number of characters the user can enter in certain fields (textarea and input type="text") in the signup form. Since I'm using a plugin, I can't edit the HTML of the page, so I can't just use the maxlength attribute.
How can I limit the number of characters the user can enter - and preferably also show a remaining characters count - without access to HTML? Can you tell me which files to edit, what code to use and where to put it?
You could use jQuery to set the max length as well as append: a remaining character count
$("input").each(function() {
var $this = $(this);
var maxLength = 50;
$this.attr('maxlength', maxLength);
var el = $("<span class=\"character-count\">" + maxLength + "</span>");
el.insertAfter($this);
$this.bind('keyup', function() {
var cc = $this.val().length;
el.text(maxLength - cc);
});
});
http://jsfiddle.net/0vk3c245/
You would just need to specify which input you want to apply the function to by altering "input" on the first line as well as changing the maxLength variable based on your max characters allowed for each. You could modify this function to take in parameters for multiple fields as well.
Here is a modified version that lets you apply this to multiple inputs:
http://jsfiddle.net/0vk3c245/1/
This is really easy to do with MagJS.
Here is a full working example:
HTML :
<div id="limit">
<h2>Characters left: <length/></h2>
<input>
<textarea></textarea>
</div>
JS:
var demo = {}
demo.view = function(state, props) {
state.textarea = state.input = {
_oninput: function() {
state.length = props.limit - this.value.length;
if (this.value.length > props.limit) {
alert('max length reached:' + props.limit)
this.value = this.value.substr(0, props.limit)
}
}
}
}
var props = {
limit: 10,
}
mag.module("limit", demo, props)
Here is link to working example: http://jsbin.com/sabefizuxi/edit?html,js,output
Hope that helps!

js clicking adds color

I have a question. So basically i'm trying to create a js script, when user clicks on a link, it colors the link in different color and then adds the link to an input box, and when the user clicks again on the same link, it changes the color back to original and deletes the text from the input box. The purpose whould be as a search filter, where you can add to search box, predefined keywoards.
$('.tagsSelect a').click(function() {
var clickColor = this.style.color;
if(clickColor = "#F5EBD5"){
var value = $(this).text();
var input = $('#popGirlMenu');
input.val(input.val() + value + ', ');
this.style.color="#f5d47f";
return false;
}
if(clickColor = "#f5d47f") {
var value = $(this).text();
var input = $('#popGirlMenu');
input.val(input.val() - value - ', ');
this.style.color="#F5EBD5";
return false;
}
});
This is my code, it works, bet when the user clicks again on the link, it doesn't change the color back to original and it doesn't remove the text from input box.
p.s sorry for my bad english
Use CSS for your layout, and check the class instead of a color value:
CSS
a { color: blue; }
a.selected { color: red; }
HTML
link a
link b
<input id="txt" type="text" />
JavaScript
$("a").click(function() {
var $this = $(this);
$this.toggleClass("selected");
if ($this.hasClass("selected"))
$("#txt").val($("#txt").val() + $this.text() + ", ");
else
$("#txt").val($("#txt").val().replace($this.text() + ", ", ""));
});
See this jsFiddle
Note to remove a part from a string, you need to replace that part with an empty string. - doesn't work on strings.
Use == or === (comparison) instead of = (setting a variable)

javascript not changing the text color

I have a function that I want to change the font color of the user entered string if it is equal to a certain word located in an array.. So far when I step through it it says that it changes the font color but it actually never updates it to the screen and I don't know why. Here is what I have so far
function getLastWord() {
var input = document.getElementById("my_text");
//var input = document.getElementById(textArea.value);
//var lineIn = document.getElementById(my_text).innerHTML;
var inputValue = input.value;
var lastWordTyped
var changeColorOfWord;
if (input == null) {
input == " ";
}
//lastWordTyped = input.substr(input.trim().lastIndexOf(" ") + 1);
lastWordTyped = inputValue.substr(inputValue.trim().lastIndexOf(" ") + 1);
if (input != null) {
for (var i = 0; i < reservedKeyWords.length; i++) {
if (reservedKeyWords[i] === lastWordTyped) {
lastWordTyped = lastWordTyped.fontcolor("blue");
my_text.replace(inputValue, lastWordTyped);
} else {
}
}
}
}
I see two issues with the code thus far.
You are using 'fontcolor("blue")' parameter on the lastWordTyped. The proper syntax to change color is element.style.color="#CCC".
You will need to wrap the last typed word in a span so you can target it and apply the color to just that word.
string.fontcolor is legacy, and should not be used even though I could see it as a viable option in this case
Essentially, what you are doing is adding font tags around the word:
var txt = 'hello world';
txt = txt.fontcolor('blue');
//txt = '<font color="blue">hello world</font>';
You do not show what you do with the result, but if you actually put it in an HTML element it should work, even though instead of using fontcolor, I'd rather use element.style.color. This would require slightly more work though:
var ele = document.querySelector('#my_text');
ele.style.color = 'blue';
ele.innerHTML = lastWordTyped;
If you still want to go with the .fontcolor method, you could just keep what you have in the question and add
input.innerHTML = my_text;

How to count and limit the text from 2 text fields in a form?

i tried to count and limit the user inputs from two text fields. that means max char is 20, then user can enter only 20 char in both text fields.I tried like this
$(document).ready( function() {
jQuery.fn.getLength = function(){
var count = 0;
var max=$("#max").val();
this.each(function(){
count += jQuery(this).val().length;
});
var rem=max-count;
return rem;
};
var $inputs= jQuery('#left,#right');
$inputs.bind('keyup',function(){
var remain=$inputs.getLength();
jQuery('#count').html($inputs.getLength());
$("#left").keyup(function(){
if($("#left").val().length > remain){
$("#left").val($("#left").val().substr(0, remain));
}
});
$("#right").keyup(function(){
if($("#right").val().length > remain){
$("#right").val($("#right").val().substr(0, remain));
}
});
});
});
but it only works for single text box, doesn't take values from 2 fields. any help please..
All you need is this code, it detects the keypress in either #left or #right, if the count of the two is more than 20, it removes the last character typed
$("#left,#right").keyup(function () {
var charCount = $('#left').val().length + $('#right').val().length;
if (charCount > 20) {
difference = -Math.abs(charCount - 20);
$(this).val($(this).val().slice(0, difference));
}
});
Demo http://jsfiddle.net/k8nMY/
My first solution worked on keydown and used return false to stop further entry, however this had the effect of disabling backspace and other keys.
This solution, which executes on keyup waits until after the key is pressed then counts characters. If the number is over 20 it will remove the last character typed. This way, the user can still press backspace and make changes as they wish, but not go over 20 chars.
I have also modified the script further, what it does is detect ANY change, e.g. a paste of a long string. It removes the 'difference' above 20 characters.
This should be a complete solution to the problem.
DEMO: http://jsfiddle.net/EEbuJ/2/
JQuery
$('#left, #right').keyup(function(e) {
if (maxLen() <= 20)
{
// Save the value in a custom data attribute
$(this).data('val', $(this).val());
} else {
// over-ride value with saved data
$(this).val($(this).data('val'));
}
});
function maxLen() {
var x = 0;
$('#left, #right').each(function() {
x += $(this).val().length;
});
return x;
};
This will save the typed in value for your inputs to a custom data attribute, if the total number of characters in the specified inputs is no more than 20.
When the maximum number of characters is reached then we stop saving the typed in value and revert the value back to our previous save (i.e. less than the maxiumum) effectively undo-ing the value.
Well it should be easy one: http://jsfiddle.net/4DETE/1/
var textLength =$inputs.eq(0).val().length+$inputs.eq(1).val().length;
if(textLength>=20)
return false
just count length of values, if you'll have more elements to limit, use jquery.each to iterate inputs
I doubt you can do $inputs.getLength() as $inputs is an array and thus will return the arraylength: 2
You will have to add up the overall sign length in both inputs:
$('#left,#right').keydown(function(){
var leftlength = $('#left').val().length;
var rightlength = $('#right').val().length;
if(leftlength + rightlength > 20)
{
return false;
}
});
or to make it shorter
if($('#left').val().length+$('#right').val().length >20){return false;}

Categories