Search code examples
javascriptjqueryregexvalidationcurrency

RegEx (replace all non numeric characters and enfore 2 decimal place numbers)


I currently working on some regex logic to enforce the input of field to ONLY accept (replace/display) a number (no alpha characters) and enforce a 2 decimal place limit...

I"m not sure how I can make this more efficient, and also add in the (max) 2 decimal place limit/restriction?

Here is my current on keyup() function

$("#amount").on("keyup", function(){

    var valid = /^\d{0,9}(\.\d{0,2})?$/.test(this.value);
    val = this.value;

    if(!valid){
        console.log('bad character found');

        //strip out commas
        this.value = val.replace(/,/g , "");

        //strip out all non-numeric characters (leaving decimal)
        //this.value = val.replace(/[^\d.-]/g, "");
        this.value = val.replace(/[^0-9.]/g, "");

        //enforce only (max) 2 decimal places
    }   
});

I was originally using this, but it is FLAWED (if you go back and add in a comma into the current/existing number, it removes the last digit/character in the field.. (even though that is not the offending character)

var valid = /^\d{0,9}(\.\d{0,2})?$/.test(this.value);
val = this.value;

if(!valid){
    //console.log("Invalid input!");
    this.value = val.substring(0, val.length - 1);
}

To be clear...

The value DOES NOT have to have a decimal, and forced 2 digits after said decimal point.. but if there IS one.. I need to enforce a 2 character limit after decimal point..

  • no commas (at all)
  • only allow 1 decimal/period
  • while a decimal it not -required-.. if present, ensure there is only 2 decimal places after the period

update 1:

ok.. so I have things 'close' (although one line of REGEX would be nice!)

My last 'to-do' item.. is to somehow enforce (if there is a 'dot'... that it only has 2 decimal places after it..... although a dot/decimal is -not- required)

$("#amount").on("keyup", function(){
    var valid = /^\d{0,9}(\.\d{0,2})?$/.test(this.value);
    val = this.value;
    console.log("ORIGINAL VAL: " + val);

    if(!valid){
        console.log('bad character found');

        var dotCheck = val.indexOf("..");
        if(dotCheck >= 0){
            console.log('DOT CHECK: ' + dotCheck);
            console.log("PRE VAL: " + val);
            val = val.replace("..", "?");
            console.log("POST VAL: " + val);
        }       

        //strip out commas                      
        val = val.replace(/,/g , "");                           
        console.log("AFTER COMMA VAL: " + val);

        //strip out all non-numeric characters (leaving decimal)
        val = val.replace(/[^0-9.]/g, "");
        console.log("AFTER CHAR VAL: " + val);

        //output to field
        this.value = val;

    }

});

update final:
(final working solution)... still checking out the regex solution posted below...

  • no commas
  • no characters outside of digits/numbers
  • no double '..' (dots)
  • no more than 2 digits after decimal point

    $("#amount").on("keyup", function(){                            
        var valid = /^\d{0,9}(\.\d{0,2})?$/.test(this.value);
    val = this.value;
    
    if(!valid){
        var dotCheck = val.indexOf("..");
        if(dotCheck >= 0){                              
            val = val.replace("..", ".");
        }
    
        //strip out commas                                          
        val = val.replace(/,/g , "");                           
    
        //strip out all non-numeric characters (leaving decimal)                            
        val = val.replace(/[^0-9.]/g, "");
    
        //enforce 2 decimal places (max)                    
        var totalLength = val.length;
        var only2DecimalsCount = val.indexOf(".");
    
        if(only2DecimalsCount >= 0 && totalLength > (only2DecimalsCount + 2)){                          
            val = val.substring(0, (only2DecimalsCount + 3));
        }                           
    
        //output to field
        this.value = val;                           
    }
    
    
    });
    

EDIT: I found that this does NOT handle something likeL

1.9.9 (bummer)


Solution

  • Here is a modified version of your final edit... I added a line to check for the situation: 1.2.3 and replace it with a pattern to remove the second dot. Did it without a look behind because it may not be supported. Here is the line: val = val.replace(/(\.)(.)(\.)/g, ".$2"); The ".$2" will replace the .#. with a dot and the pattern group that is the wildcard (.) in this case. Your other check at the bottom will catch a double dot ".." situation.

    $("#amount").on("keyup", function () {
        var valid = /^\d{0,9}(\.\d{0,2})?$/.test(this.value);
        val = this.value;
        console.log("ORIGINAL VAL: " + val);
    
        if (!valid) {
            var dotCheck = val.indexOf("..");
            if (dotCheck >= 0) {
                val = val.replace("..", ".");
            }
    
            val = val.replace(/(\.)(.)(\.)/g, ".$2");
    
            //strip out commas                                          
            val = val.replace(/,/g, "");
    
            //strip out all non-numeric characters (leaving decimal)                            
            val = val.replace(/[^0-9.]/g, "");
    
            //enforce 2 decimal places (max)                    
            var totalLength = val.length;
            var only2DecimalsCount = val.indexOf(".");
    
            if (only2DecimalsCount >= 0 && totalLength > (only2DecimalsCount + 2)) {
                val = val.substring(0, (only2DecimalsCount + 3));
            }
    
            //output to field
            this.value = val;
        }
    });
    

    EDIT: Fixed new line by adding parenthesis. Explanation: Parenthesis "groups" the pieces of the pattern together (1-based index). So the new linehas 3 groups - (\.)- 1, (.)- 2, (\.)- 3. Replacing with $2 will call group #2 which in this case is the wildcard.