I am making a simple website to get familiar with html, css and javascript. I am a beginner programmer. The regular expression will be used in Javascript.
In my website I currently have a simple percentage calculator. It allows users to input a number and click a button to receive their answer.
My goal is to have the input match everything but digits and allows for only one decimal and no negative numbers.
I have a onkeyup
event that deletes any characters that aren't described above.
So if you type 'abc' it automatically gets erased. Here is the snippet for reference
<div class='child flex-child'>
<label for="num2">Enter your second number: </label>
<input type="number" id="num2"
onkeyup="this.value=this.value.replace(/[^\d.]/g, '')" >
</div>
Accepted formats would be as follows: 1.0, 1, 0.1 etc. Rejected formats: -1, 12.12.12, .1 <- more than one decimal place
Currently I am using /[^\d.]/g which works but allows for other decimals.
EDIT This seems to work fine but doesn't allow for and decimals. I want it to allow at least 1 but no more. [^\d] (?=.*)
Here is a small list of expressions that I have used.
- /^\d (.\d )?/gm
- /\d (.\d )/gm
- /[^\d.]/g
- /[^0-9.]/g
- [^\d.]^[^0-9]
- /^\d (.\d )?/gm
- /^\d (.)?\d /gm
- (^0*)(^.*) ^\d (.\d )?
These would be fine if in-versed.
- (^.) (^0) ^\d (.\d )?
- /[^\d.]/g
- ^[0-9]..*[\d]{1,}
They don't fully accomplish what I am looking for. I am able to accomplish matching my goal but I need it in-versed in order for it to meet my needs.
Any guidance would be appreciated. Thank you.
[Edit] I know global and multiline tags aren't necessary as it is only one line. Kept it there from testing.
CodePudding user response:
Try
/[^\d.] |(\.\d). |(\..*)\.|/g
For example
const before = '123.45';
const after = before.replace(/[^\d.] |(\.\d). |(\..*)\.|/g, '$1$2');
console.log(after);
We match and replace either anything which comes after a .
followed by a digit, or a .
if there is already a .
earlier in the string.
$1
refers to whatever is matched by the first capture group (\.\d)
, and $2
refers to whatever is matched by the second capture group (\..*)
.
CodePudding user response:
^([0-9] \.?[0-9]?)$
^ - start at the beginning of the string
(
[0-9] - any number of digits to begin with
\.? - 0 or 1 times a dot
[0-9]? - 0 or 1 times a digit
) - make it a group
$ - after the group, the string must end
The only problem might be it also accepts 1.
as valid input (otherwise you could never type 1.1
as the script removes the dot on input). But you could do a test when submitting the input to filter that one out.
CodePudding user response:
I think this is what you want to achieve.
moreover, why not using <input type="number" min="0" max="100" step="0.1">
which is simple and better
const input = document.querySelector('#num2');
input.addEventListener('keyup', ()=>{
if(!(/^[^-a-z]\d{0,}(([.]{1})?\d?){0,}$/gi.test(input.value)) ){
input.value = '';
}
})
<div class='child flex-child'>
<label for="num2">Enter your second number: </label>
<input type="text" id="num2">
</div>
CodePudding user response:
Event Handling and Form Controls
Using .replace()
on a string from a form control when invoked by an key event handler is tricky. It would be better if you wrap all of your form controls (ie <input>
, <textarea>
, <select>
, etc) in a <form>
and then register the "input"
event to it (see Figure I). "input"
event is like a key event but it's specialized for form controls so it is triggered immediately when the user types into an <input>
or selects an <option>
from a <select>
.
Figure I
const form = document.forms.ID_or_name_of_FORM
// See HTMLFormElement in Reference section
form.oninput = event_handler
//OR
form.addEventListener('input', event_handler);
// See Events in Reference section
Also, inline event handlers are garbage so don't use them.
<input oninput="event_handler(this.value); return false">
Once you have <form>
registered to the "input"
event, you can design the event handler to delegate any "input"
event to any element within <form>
and exclude any element as well. Moreover, this event handling works for dynamically added elements as well. For more details on this programming paradigm refer to the Reference section listing: Event Delegation.
Delegating the "Input" Event
The following examples involve two <input>
s in a <form>
, num1
which accepts only integers and num2
which accepts integers and floats.
All event handlers pass the Event Object by default:
Figure II
/*
The Event Object has many properties that reference every aspect
of the event.
*/
function filterInput(event) {
/*
The .target property always references the element the user is
interacting with.
*/
const data = event.target;
//...
Next, delegate the event to the applicable elements via flow control statements, swithes, tenaries, etc.
Figure III
//...
if (data.matches('input')) {
if (data.id === 'num1') {
// Filter num1 value so it's always an integer
if (data.id === 'num2') {
// Filter num2 value so it's always a float or integer
}
}
/*
By specifying what elements react, all other elements are excluded.
*/
On the Fly Filtering Input Data
In the event handler we will use a RegExp constructor:
Figure IV
const rgx = new RegExp(/\D?([.][\d]*)\D?|\D/, 'g');
When replacing sub-strings within a string value of an <input>
while the user is still typing you'll need to tighten the quantifiers down to ?
which is 0 or 1.
Figure V
Tokens | Description |
---|---|
\D? |
zero to one non-digit character |
([.]\d*) |
literal "." and zero to one digit in a capture group |
\D? |
zero to one non-digit character |
| |
OR |
\D |
one non-digit character |
Note, the RegExp is designed to match each character as it's typed and then remove it. Even the "." and the numbers are removed, but since they are in a capture group (...)
, they can be recalled as the replacement sub-string with an ID ("$1"
means first capture group):
Figure VI
let match = data.value.replace(rgx, '$1');
Since .replace()
actually rips everything out of .value
string then it places the capture group back in, there are times within that timeframe that .value
has nothing (0 length) and then of course it ends up a smaller string (length of capture group "$1"
). So we need to place the caret at the end of the string whenever the "input"
event is triggered:
Figure VII
//...
let end, match
//...
if (event.target.id === 'inputID') {
match = event.target.value.replace(rgx, '$1');
event.target.value = match;
end = match.length;
}
event.target.setSelectionRange(end, end);
event.target.focus();
}
Example
document.forms.calc.oninput = filterInput;
function filterInput(e) {
const data = e.target;
const rgx1 = new RegExp(/\D/, 'g');
const rgx2 = new RegExp(/\D?([.]\d*)\D?|\D/, 'g');
if (data.matches('input')) {
let match, end;
if (data.id === 'num1') {
match = data.value.replace(rgx1, '');
data.value = match;
end = match.length;
}
if (data.id === 'num2') {
match = data.value.replace(rgx2, '$1');
data.value = match;
end = match.length;
}
data.setSelectionRange(end, end);
data.focus();
}
}
<form id='calc'>
<label for="num1">Enter your first number<sup>❉</sup>: </label>
<input id="num1" min='0' placeholder='Positive integers only'> <br>
<label for="num2">Enter your second number<sup>