peter flanagan
peter flanagan

Reputation: 9790

REACT - Preventing input after decimal place after 2 decimal places has been reached

I have an input box in a component. I want to prevent the user from being able to add any input if the value of the input box contains more than 2 decimal places.

E.g. if a user inputs 10.95 I dont want to allow them write anything else after this value. They could still update it to 101.95 but it should prevent any input being added after the final decimal place.

The code I have so far is below.

class inputBox extends Component {

    countDecimals(value) {
        if(Math.floor(value) === value) return 0;
        return value.toString().split(".")[1].length || 0;
    }

    updateValue(e) {
        if(this.countDecimals(e.target.value) > 2) {
            //prevent user from inputting into box after the decimal place...
        }
    }

    render() {
       return(
          <input type='text' onChange={this.updateValue} />
        )
   }
}

Upvotes: 2

Views: 8274

Answers (4)

Jacob.S
Jacob.S

Reputation: 41

You could use React controlled component and bind a state to the input's value atrribute. Then your onChange event handler will look like.

    updateValue(e) {
        this.setState({ value: e.target.value.toString().split(".").map((el,i)=>i?el.split("").slice(0,2).join(""):el).join(".")})
    }

Worked for me.

Upvotes: 4

RC NL
RC NL

Reputation: 88

As far as my knowledge in Javascript and HTML goes there is no 'easy solution' for this. Working with both 'raw' JS and ExtJs forms has learned me that there are multiple ways to focus and manipulate a field. Which makes it hard to manipulate the inner value at the right time.

So allow me to split your issue in to multiple problems. Which I will attempt to tackle:

Triggering

You want your logic to run at all the times something happens to the field. The following link provides you with the options: http://www.w3schools.com/TAGS/ref_eventattributes.asp

When you use onchange it will trigger when someone changes the values after you blur the field (you click or tab away from the field). So that's no good.

You could try the key (up, down, press) events. But that excludes when you paste a value.

Long story short, you could in theory try to implement a function on every event you could think of to make sure you catch the users input and do what you want with it.

My solution is, start a timer when you focus a field and validate the value and do further logic. And finalize everything you wanted to do on blur.

Determining the correctness of the value

You could write some nifty regex or a single line statement that tells you if the value is correct. It's all the same in the end, it should fit your needs.

So something like:

var inputVar = element.value;
var inputFloat = parseFloat(inputVar);
var normalizeInput = Math.round(inputFloat * 100) / 100;
if(inputFloat > 0 && normalizeInput == inputFloat){
    #correct value
}else{
   #incorrect value
}

Handling correct/incorrect input

Now you want to handle the user input and do something. Things like setting the field to disabled or read only would prevent further input and changes, but would not let your users do anything to your field.

As what I read is you want the field to not change in function, you want to be able to edit it.

So that leaves you with 2 options:

Editing the field content directly by overriding the element.value with the desired value.

Manipulating key inputs / selection to try and keep the cursor at same position the user left it while correcting the false input.

I would opt for the former as it is a lot less of a hassle, although it might mess with the cursor position (browser dependant).

Final implementation

So what I propose combining all the above:

On focus you start running a setTimeout or setInterval http://www.w3schools.com/jsref/met_win_settimeout.asp http://www.w3schools.com/jsref/met_win_setinterval.asp

In the function run then, you check if there is a previous value set.

The first time it is NOT so: You have to hold this previous value somewhere, you could hold it in a variable within javascript or put it in to the field in the DOM.

http://www.w3schools.com/jsref/met_element_setattribute.asp

var inputElement.setAttribute('old_value', oldValue);

Now you check if this value is correct before saving it, else just default it back to blank (or attempt to normalise the value to something that validates, you could keep cutting away characters at the right for example).

Now on each run you check if the value is correct. If the value is correct, you hold the new value as the 'new' previous value (and calling setTimeout again if you use that method).

If it is not correct you write back the old value or attempt to normalise the input value and if that fails use the last correct value.

On the blur event you clear the setTimeout or setInterval running in the background: http://www.w3schools.com/jsref/met_win_cleartimeout.asp http://www.w3schools.com/jsref/met_win_clearinterval.asp

(Alternatively you could check if the document.activeElement is the same as the one that is run on this 'loop' so it knows when to stop).

On blur you check the value one last time and do the same logic to prevent false input.

PLAN B

Use the HTML5 number input field:

HTML 5 input type="number" element for floating point numbers on Chrome

Try <input type="number" step="0.01" /> if you are targeting 2 decimal
places :-).

edited Apr 27 '15 at 18:10
Andre Figueiredo

Which only works on browsers that support it.

Upvotes: 0

LellisMoon
LellisMoon

Reputation: 5020

I think you need to save the oldvalue, that should work.

var input=document.getElementById('input'),
    countDecimals=function(value) {
    if(Math.floor(value) === value) return 0;
    if(value.toString().split(".")[1])
    return value.toString().split(".")[1].length || 0;
},
    updateValue=function(val) {
    if(countDecimals(val) > 2) {
      input.value=input.getAttribute('oldValue');
    }
};

input.oninput=function(){
  updateValue(this.value);
  input.setAttribute('oldValue',this.value);
}
<input id="input" type="text"/>

Upvotes: 0

Jonas Wilms
Jonas Wilms

Reputation: 138247

document.getElementById("yourinput").oninput=function(){
  this.value=this.value.toString().split(".").map((el,i)=>i?el.split("").slice(0,2).join(""):el).join(".");
};

Replace the value with a new value, that is shortened to two chars after each dot. http://jsbin.com/soretokuse/1/edit

Upvotes: 1

Related Questions