Input Masking in Javascript - javascript

I have an input where users are going to type identification numbers and I would like to mask that input so that it always has this format : XX-XXXXXXXX-X
The X's can only be numbers and the dashes need to be always in those positions.
Here is what I got so far:
import React from "react";
import { Inertia } from "#inertiajs/inertia";
import {useForm, usePage} from "#inertiajs/inertia-react";
import ErrorForm from "./ErrorForm"
function Login() {
const{data , setData , processing ,reset} = useForm({
cuit: '',
password: ''
})
const errors = usePage().props.errors
function submit(e){
e.preventDefault()
Inertia.post(route('login'),data,{
onError:() => reset('password')
})
}
function handleChange(e){
if(e.target.value.length === 11){
e.target.value = [e.target.value.slice(0,11),'-'].join('')
}else if(e.target.value.length >= 2){
if(!e.target.value.includes('-')){
e.target.value = [e.target.value.slice(0,2),'-',e.target.value.slice(2)].join('')
}
}
setData('cuit',e.target.value)
}
function handleKeyDown(e){
if(e.key === "0" || e.key === "1" || e.key === "2" || e.key === "3" || e.key === "4" || e.key === "5" || e.key === "6" || e.key === "7" || e.key === "8" || e.key === "9"){
handleChange(e)
}
}
return(
<div className="ContenedorLogin">
<form onSubmit={submit}>
<input
name="cuit"
type="text"
placeholder="C.U.I.T."
className="input"
onKeyDown={handleKeyDown}
maxLength="13"
/>
{errors.cuit &&
<ErrorForm
content={errors.cuit}
/>
}
<input
name="password"
type="Password"
placeholder="Contraseña"
className="input"
value={data.password}
onChange={e => setData('password',e.target.value)}
/>
<button className="btn-consejo" type="submit" disabled={processing}>INGRESAR</button>
</form>
</div>
)
}
export default Login
Basically I'm capturing a keyDown event, then changing the input only if the user typed a number and finally in the handleChange function I try to mask and set the value.
It kind of work but not for all cases, for example if I'm in the middle of typing and I already have the first dash and I add a number before the first dash its going to allow it leaving me with something like this : XXXX-XXXXX
I imagine I can achieve the result using regular expressions or something like that but I'm not familiar at all with them
Thanks in advance!

It would work if you bind event on whole input value instead of each key press.
Here, instead of using event "onKeyDown", use "onChange". Also You can use html pattern for accepting number only.
<input
name="cuit"
type="number"
pattern="[0-9\/]*"
placeholder="C.U.I.T."
className="input"
onChange={handleChange} // calling handleChange on input change directly
maxLength="13"
/>
You already have working handleChange() function, which will work perfectly.

Instead of using JS to do this use the in-build HTML form validation.
Add a required attribute and a regex pattern to the input. The form won't submit until the input has been validated. And you'll also get little tool-tips to explain what the issue is if you try to submit and the input isn't validated.
In this case the regex reads:
^ - start of the value
[0-9]{2}- two numbers followed by a dash
[0-9]{8}- eight numbers followed by a dash
[0-9] - one number
$ - end of the value
const { useState } = React;
function Example() {
const [input, setInput] = useState('');
function handleSubmit(e) {
e.preventDefault();
console.log(`Submitted: ${input}`);
}
function handleChange(e) {
setInput(e.target.value);
}
return (
<form onSubmit={handleSubmit}>
<input
onChange={handleChange}
placeholder="dd-dddddddd-d"
pattern="^[0-9]{2}-[0-9]{8}-[0-9]$"
value={input}
required
/>
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
input, button { font-size: 1.2em; }
input { padding: 0.3em; }
input:invalid { border: 1px solid red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Related

How to create a numeric integer input using Vuetify?

Based on Is there specific number input component in Vuetify? I'm trying to create a numeric input.
The input and output value is unknown so it could be undefined or null because one might want to clear the field so it should not respond with 0.
The input component should not have "up"/"down" buttons if possible.
If the user passes in a flag isAcceptingFloatingPointNumbers = false this input should only accept integer values ( it should not be possible to type floats )
Reproduction link
<template>
<v-app>
<v-main>
<v-text-field
type="number"
label="number input"
:clearable="true"
:model-value="num"
#update:modelValue="num = $event"
/>
</v-main>
</v-app>
</template>
<script setup lang="ts">
import { ref, watch, Ref } from 'vue'
const num: Ref<unknown> = ref(undefined)
watch(num, () => console.log(num.value))
</script>
How can I make sure the user can only type integer values if the flag isAcceptingFloatingPointNumbers returns false? The only thing coming to my mind is to append a custom rule like
v => Number.isInteger(v) || 'Must be integer'
but AFAIK this rule would trigger even if the value could be undefined. Is there a way to prevent the user input instead?
Based on yoduh's answer I tried this ( reproduction link )
NumberField.vue
<template>
<v-text-field
type="number"
label="number input"
:clearable="true"
:model-value="num"
#update:modelValue="emit('update:modelValue', $event)"
#keypress="filterInput"
/>
</template>
<script setup lang="ts">
const props = defineProps<{
num: unknown;
isAcceptingFloatingPointNumbers: boolean;
}>();
const emit = defineEmits<{
(e: "update:modelValue", newValue: unknown): void;
}>();
function filterInput(inputEvent) {
if(props.isAcceptingFloatingPointNumbers.value) {
return true;
}
const inputAsString = inputEvent.target.value.toString() + inputEvent.key.toString();
const inputValue = Number(inputAsString);
if(!Number.isInteger(inputValue)) {
inputEvent.preventDefault();
}
return true;
}
</script>
I'm consuming the component like so
<template>
<number-field :num="num" :isAcceptingFloatingPointNumbers="false" #update:model-value="num = $event" />
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import NumberField from "./NumberField.vue";
const num: Ref<unknown> = ref(undefined);
watch(num, () => console.log(num.value));
</script>
The problem is that my filter function is wrong. It's still possible to type "12.4" because the filter ignores "12." and then converts "12.4" to 124.
Does someone got any ideas how to fix this?
Since an integer is made only of digits, you can test only if each pressed key is a digit, no need to check the whole input value.
function filterInput(inputEvent) {
if(props.isAcceptingFloatingPointNumbers.value) {
return true;
}
if(!inputEvent.target.value.length && inputEvent.key === '-'){
return true;
}
if(!Number.isInteger(Number(inputEvent.key))) {
// Of course, you can choose any other method to check if the key
// pressed was a number key, for ex. check if the event.keyCode is
// in range 48-57.
inputEvent.preventDefault();
}
return true;
}
Concerning the arrows, it is not a Vuetify specific element, but elements added by the browser to inputs of type number. You can disable them like this.
As per my understanding you have below requirments :
To prevent the user input based on the isAcceptingFloatingPointNumbers flag value (Only accept integers if flag is false else field should accept the floating numbers).
No up/down arrows in the input field.
Input field should accept the 0 value.
If my above understandings are correct, You can simply achieve this requirement by normal text field and on every keyup event, You can replace the input value with an empty string if it's not matched with passed valid regEx.
Live Demo :
const { ref } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();
let options = {
setup: function () {
let num = ref('');
let isAcceptingFloatingPointNumbers = ref(false);
const validateInput = () => {
const numbersRegEx = !isAcceptingFloatingPointNumbers.value ? /[^-\d]/g : /[^-\d.]/g;
num.value = num.value.replace(numbersRegEx, '');
}
return {
num,
validateInput
};
}
};
let app = Vue
.createApp(options)
.use(vuetify)
.mount('#app');
<script src="https://unpkg.com/vue#next/dist/vue.global.js"></script>
<script src="https://unpkg.com/#vuetify/nightly#3.1.1/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/#vuetify/nightly#3.1.1/dist/vuetify.css"/>
<div id="app">
<v-text-field label="Numper Input" v-model="num" v-on:keyup="validateInput"></v-text-field>
</div>
I think the best way would be to create a custom filter function that runs on keypress. With your own custom filter you can also remove the type="number" since it's no longer necessary and will remove the up/down arrows on the input.
<v-text-field
label="number input"
:clearable="true"
:model-value="num"
#update:modelValue="num = $event"
#keypress="filter(event)"
/>
const filter = (e) => {
e = (e) ? e : window.event;
const input = e.target.value.toString() + e.key.toString();
if (!/^[0-9]*$/.test(input)) {
e.preventDefault();
} else {
return true;
}
}
updated sandbox
As per your comment on #yoduh's answer, if you want to stick with type="number" (good to reduce the step to validate the non-numeric characters), then hide the arrows using following CSS-
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
Logic 1-
On the keyup event, check if isAcceptingFloatingPointNumbers is false and the typed input is not an integer, empty the input field's value. To check if the input value is an integer or not-
You can use a regex pattern, /^-?[0-9]+$/.test(num).
You can use the JS method Number.isInteger(num).
Though, in the second method the input value will always be of type string (why?). To resolve this, use the built-in Vue.js directive v-model.number to recast the input value's type to a number.
Demo-
const { ref } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();
let options = {
setup: function() {
let num = ref(null);
let error = ref('');
let isAcceptingFloatingPointNumbers = ref(false);
const validateInput = () => {
// If floats not allowed and input is not a integer, clean it.
if (
!isAcceptingFloatingPointNumbers.value &&
!Number.isInteger(num.value)
) {
num.value = null;
error.value = "Only integers are allowed."
} else {
error.value = '';
}
};
return {
num,
error,
validateInput,
};
},
};
let app = Vue.createApp(options)
.use(vuetify)
.mount("#app");
.error {
color: red;
}
<script src="https://unpkg.com/vue#next/dist/vue.global.js"></script>
<script src="https://unpkg.com/#vuetify/nightly#3.1.1/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/#vuetify/nightly#3.1.1/dist/vuetify.css"/>
<div id="app">
<v-text-field
type="number"
label="number input"
:clearable="true"
v-model.number="num"
#keyup="validateInput"
>
</v-text-field>
<label class="error">{{ error }}</label>
</div>
The only glitch here is if the user types 123. and stops typing then the dot will be visible because of the type="number" but if you use this value, it will always be decoded as 123.
If you want to restrict the typing of the dot, detect the key on the keypress event and prevent further execution.
EDIT------------------
Logic 2
If a user tries to input the float number, you can return the integer part of that floating-point number by removing the fractional digits using Math.trunc(num) method.
Demo-
const { ref } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();
let options = {
setup: function() {
let num = ref(null);
let error = ref('');
let isAcceptingFloatingPointNumbers = ref(false);
const validateInput = () => {
if (!isAcceptingFloatingPointNumbers.value && !Number.isInteger(num.value)) {
error.value = "Only integer is allowed.";
// Keep only integer part.
num.value = Math.trunc(num.value);
} else {
error.value = ''
}
};
return {
num,
error,
validateInput,
};
},
};
let app = Vue.createApp(options)
.use(vuetify)
.mount("#app");
.error {
color: red;
}
<script src="https://unpkg.com/vue#next/dist/vue.global.js"></script>
<script src="https://unpkg.com/#vuetify/nightly#3.1.1/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/#vuetify/nightly#3.1.1/dist/vuetify.css"/>
<div id="app">
<v-text-field
type="number"
label="number input"
:clearable="true"
v-model.number="num"
#keyup="validateInput"
>
</v-text-field>
<label class="error">{{ error }}</label>
</div>

how to remove white space from first and last in react

I don't want any space before the input and after the input like " text" and "text " does not allow so I replace the white space but when we copy "text " from notepad and paste over the input and want to remove the space it throws error like "can not read property of undefined reading target".so how to do like when user give space front and back its automatically replace whitespace
const handleKeyDown = (e) => {
if (e.key === " ") {
e.preventDefault();
}
};
const handleChangeWhiteSpace = (e) => {
if (e.target.value.includes(" ")) {
e.target.value = e.target.value.replace(/\s/g, "");
}
};
<MyInput
type="text" style={{width:'240px'}}
error={formik.errors.input && formik.touched.input}
value={formik.values.input}
onBlur={formik.handleBlur}
onChange={(e)=>{formik.handleChange(e);handleChangeWhiteSpace()}}
onKeyDown={handleKeyDown}
name="input"
id="input"
autoFocus={false}
autoComplete="off"
/>
using regex the following should work, you can test it at regex101:
e.target.value = e.target.value.replace(/^[ \t]+|[ \t]+$/gm, "");
the cleaner solution would be what sojin suggested
e.target.value = e.target.value.trim()
Replace
const handleChangeWhiteSpace = (e) => {
if (e.target.value.includes(" ")) {
e.target.value = e.target.value.replace(/\s/g, "");
}
};
With this
const handleChangeWhiteSpace = (e) => {
e.target.value = e.clipboardData.getData('Text').trim();
};
To register changes when you paste text inside the text field use the onPaste event
onPaste={handleChangeWhiteSpace}
Final Code
const handleKeyDown = (e) => {
if (e.key === " ") {
e.preventDefault();
}
};
const handleChangeWhiteSpace = (e) => {
e.target.value = e.target.value.trim();
};
<MyInput
type="text" style={{width:'240px'}}
error={formik.errors.input && formik.touched.input}
value={formik.values.input}
onBlur={formik.handleBlur}
onPaste={handleChangeWhiteSpace}
onChange={(e)=>{formik.handleChange(e);
handleChangeWhiteSpace()}}
onKeyDown={handleKeyDown}
name="input"
id="input"
autoFocus={false}
autoComplete="off"
/>
use normalize={(value, prevVal, prevVals) => value.trimStart()} after rules{[]} in form.item
for prevent whitespace before value in antd input
for example check selected code in image ==>
enter image description here
To dont allow to send only spaces you can use normalize prop
<MyInput
type="text" style={{width:'240px'}}
normalize={(value) => value.trimStart()}
error={formik.errors.input && formik.touched.input}
value={formik.values.input}
and then you can remove all of the spaces inside of this function
const onFinish = (values) => {
//values.input1=values.input1.trim() or whatever
}
btw this function you can use to remove all the spaces
const remove = (str) => str.trim().replace(/\s\s+/g, ' ')

Formik instant feedback input box

I'm trying to make a input box component that has instant feedback using Formik. I want the input box to turn green when the user input matches a predefined string (the "answer"), gray if the input matches the prefix of the answer (including the empty string) and red otherwise. This string is stored as a property of the initial values, values.answer. The Formik validate function checks if the input equals values.answer and sets values.correct = true. I then created a css class corresponding to a green input box and set the className of the input conditional on the value of values.correct. The problem is it only seems to update (i.e turn green with a correct input) when I click out of focus of the input box (i.e onBlur). I would like it to work onChange. How would I do this?
Here is the relevant code sandbox: https://codesandbox.io/s/instant-feedback-box-lub0g?file=/src/Frame.js
Cool problem, but you've overcomplicated your code a little bit 😉 Some feedback:
touched is set to true during onBlur by default. You can override this by using setTouched(), but I found it simpler to just use values instead of touched in your form
try to keep values as minimal as possible, it's only meant to access input values so there's no need for hint and answer to be assigned to it
the purpose of the validation function is to return an errors object and not to set values, so remove assignments like values.correct = true
You don't need to store isDisabled in state, you can derive it from formik.submitCount and formik.isSubmitting
const Note = () => {
const [showFrame, setShowFrame] = useState({ 1: true });
const onCorrectSubmission = (frameId) => {
setShowFrame({ ...showFrame, [frameId]: true });
};
const text =
"What is the sum of the first three natural numbers? (give answer as a word, i.e one, two etc.)";
const hint = "The first three natural numbers are 1, 2, and 3";
const answer = "six";
return (
<div>
<h1>Induction</h1>
{showFrame[1] ? (
<Frame
id={1}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
{showFrame[2] ? (
<Frame
id={2}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
</div>
);
};
const Frame = ({
id,
text,
hint,
answer,
values,
onCorrectSubmission,
...props
}) => {
const validate = (values) => {
const errors = {};
if (!answer.startsWith(values.cloze)) {
errors.cloze = hint;
} else if (values.cloze !== answer) {
errors.cloze = true;
}
return errors;
};
const formik = useFormik({
initialValues: {
cloze: ""
},
validate,
onSubmit: (values) => {
onCorrectSubmission(id + 1);
}
});
const isFinished = formik.isSubmitting || formik.submitCount > 0;
return (
<form enablereinitialize={true} onSubmit={formik.handleSubmit}>
<p>{text}</p>
<input
id="cloze"
name="cloze"
type="text"
autoComplete="off"
{...formik.getFieldProps("cloze")}
disabled={isFinished}
className={`input
${!answer.startsWith(formik.values.cloze) ? "invalid-input" : ""}
${formik.values.cloze && !formik.errors.cloze ? "valid-input" : ""}
`}
/>
{formik.values.cloze && formik.errors.cloze ? (
<div>{formik.errors.cloze}</div>
) : null}
<button disabled={!!formik.errors.cloze || isFinished} type="submit">
Submit
</button>
</form>
);
};
export default Frame;
Live Demo

restrict special characters in reactNative TextInput

I'm trying to prevent my TextInput from getting values like $,%,^,&,(,) etc. Basically my TextInput should allow letters only. My approach is as follows. But still i'm able to input these other characters. How can i prevent special characters from the TextInput
restrict(event) {
const regex = new RegExp("^[a-zA-Z]+$");
const key = String.fromCharCode(!event.charCode ? event.which : event.charCode);
if (!regex.test(key)) {
event.preventDefault();
return false;
}
}
<TextInput
underlineColorAndroid='transparent'
allowFontScaling={false}
style={styles.questionText}
onKeyPress={e => this.restrict(e)}
value={firstNameState}
/>
the onKeyPress event on android does not work very well.
That is why I have chosen to use a method that eliminates these characters and then save it wherever you want, just as it might change the state of your field.
restrict = text => text.replace(/[`~0-9!##$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '')
I have to block special characters by this line of code.
var format = /[!##$%^&*()_+-=[]{};':"\|,.<>/?]+/;
if(format.test(string)){ }
You may define your OnChange event handler using your regex, where you will check if the input string matches your regex with /^[^!-\/:-#\[-`{-~]+$/.test(text):
const {useState} = React;
const App = () => {
const [value, setValue] = useState("");
const onChange = e => {
const input = e.currentTarget.value;
if (/^[^!-\/:-#\[-`{-~]+$/.test(input) || input === "") {
setValue(input);
}
};
return (
<div className="App">
<input
value={value}
onChange={onChange}
underlineColorAndroid='transparent'
allowFontScaling={false}
/>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

Filter input text to enter only number and not even decimal

My goal is for the user to only enter number from [0-9] not even decimal is allowed
How to do that?
The code
<b-input expanded
type="number"
v-model="amount"
#input="updateValue()"
placeholder="{{ amount }}">
</b-input>
The <b-input> is from buefy https://buefy.github.io/documentation/input/
From the Beufy doc, I get that <b-input> is essentially an extension of native <input> field so it accepts attribute that the native input would accept.
As of now, it is not possible to completely filter out specific characters using pure HTML attributes like pattern="\d+".
What you can do is to use a "keydown" event listener to filter out these characters using native event.preventDefault() by respective keys. Of course, we could use the native <input type="number"> to help in general.
const app = new Vue({
el: '#app',
methods: {
filterKey(e){
const key = e.key;
// If is '.' key, stop it
if (key === '.')
return e.preventDefault();
// OPTIONAL
// If is 'e' key, stop it
if (key === 'e')
return e.preventDefault();
},
// This can also prevent copy + paste invalid character
filterInput(e){
e.target.value = e.target.value.replace(/[^0-9]+/g, '');
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input
type="number"
step="1"
min="0"
#keydown="filterKey"
// OR
#input="filterInput"
>
</div>
I have just started using vue js so i don't have much knowledge but i think you can add an event listener and use reg ex in your function
<input type="number" #input="checknum">
export default{
methods: {
checknum: function (event) {
this.value = this.value.replace(/[^0-9]/g, '');
}
}
}
You can call a function on keyup event and check all the non-numeric characters and remove from the value.
For Example:
// In template add this line
<input type="text" v-model="inputNumber" #keyup="onlyNumeric" />
// Define inputNumber in data.
// In Methods add onlyNumeric function
onlyNumeric() {
this.inputNumber = this.inputNumber.replace(/[^0-9]/g, '');
}
Template
<input type="number"
v-model="amount"
#input="updateValue"
placeholder="amount" />
Script
<script>
export default {
data() {
return {
amount: null,
}
},
methods: {
updateValue(e) {
e.target.value = e.target.value.replace(/[^0-9]+/g, '')
}
}
}
</script>
I don't know sometime people doesn't understand, what needed is buefy input which type is text, because on default it must empty string, but when input value its only accept number, this is my answer:
Input tag:
<b-input
type="text"
v-model="onlyNumber"
:placeholder="'Input only number example: 345678'"
#keypress.native="isNumber($event)"
/>
script:
data() {
return {
onlyNumber: '',
};
},
methods: {
isNumber(evt) {
evt = (evt) ? evt : window.event;
var charCode = (evt.which) ? evt.which : evt.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57)) {
evt.preventDefault();
}
return true;
},
},
Pros : default is empty string, but only accept number
Const : accepted number will settled as string of number example : "333214234", just convert to number if you have need on number form
Sample code
$('#numberfield').on('input', function(event) {
console.log(parseInt(event.target.value))
event.target.value = parseInt(event.target.value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="numberfield" type="number" />
If you are using Vuetifyjs, you could use rules directive as below:
Template
<v-textfield type="number" v-model="amount"
:rules="theForm.myRules" placeholder="{{amount}}"> </v-textfield>
Script
export default {
data() {
return {
amount: null,
theForm: {
myRules: [
v => /[^0-9]/.test(v) || 'Input Invalid'
]
}
};
}
};

Categories