user3312508
user3312508

Reputation: 937

Knockout - restrict input to only numbers with one decimal point

I have the code below which does restrict the input to only numbers and a select number of key combinations. But I also want to restrict the input to only accept one decimal point.

number: {
            init: function (element) {
                // Allows only numbers with decimals
                $(element).on("keydown", function (event) {
                    // Allow: backspace, delete, tab, escape, and enter
                    if (event.keyCode === 46 || event.keyCode === 8 || event.keyCode === 9 || event.keyCode === 27 || event.keyCode === 13 ||
                        // Allow: Ctrl+A, Ctrl + C, Ctrl + V, Ctrl + X
                        ((event.keyCode === 65 || event.keyCode === 67 || event.keyCode === 86 || event.keyCode === 88) && (event.ctrlKey === true || event.metaKey === true)) ||
                        // Allow: .
                        (event.keyCode === 190 || event.keyCode === 110) ||
                        // Allow: home, end, left, right
                        (event.keyCode >= 35 && event.keyCode <= 39)) {
                        // let it happen, don't do anything
                        return;
                    } else {
                        // Ensure that it is a number and stop the keypress
                        if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) {
                            event.preventDefault();
                        }
                    }
                });
            }
        }

I tried to add '$(element).val().indexOf('.') !== -1' like below but it does not do anything and I can still type in as many periods I want. How should I actually restrict it?

if (event.shiftKey || ($(element).val().indexOf('.') !== -1 && (event.keyCode === 190 || event.keyCode === 110)) || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) {
                event.preventDefault();
                        }

Upvotes: 0

Views: 1642

Answers (3)

webketje
webketje

Reputation: 10976

So I must say I don't really see the logic in allowing explicitly; instead you should exclude explicitly as by default everything is allowed. You buy a computer after it having passed all your criteria, you don't buy 10 computers, then throw away those that don't fit your criteria. So, no need to touch the Ctrl & other function keys, you're making it needlessly complicated.
If you look carefully at your extremely long if clause, you will notice the following:
(event.keyCode === 190 under which you stated // let it happen, don't do anything. So periods will always be added regardless. Here are the gotchas with your current approach:

  • with the keydown event, all keycodes produce the same value with Caps Lock on or not, because they're the same keys. So you cannot distinguish between period . and semicolon ;.
  • with the keydown event, keycodes for the same number on a numpad and on the keyboard are different, because they are different keys.
  • Disabling the Shift key (like you did), forces AZERTY users to use Caps Lock because AZERTY keyboards produce the non-numerical char when Shift is off.

As a result you are much better off using the keypress event, which returns the character value. Here is a refactored and much more concise function, that achieves exactly the same:

 $('#jquery').on("keypress", function (event) {
   var k = event.keyCode || event.charCode;
   if (k === 46 && this.value.match(/\./g) || (k < 48 && k !== 46 ) || k > 57) {                            
     event.preventDefault();
   }
 });

Test it here: http://jsfiddle.net/kevinvanlierde/5ay3cory/6/

NOTE: Because Mozilla Firefox sucks at making the difference between keydown and keypress, it messes up and disables all control keys too.

Upvotes: 1

user3312508
user3312508

Reputation: 937

I fixed my own code and thought I'd share it here:

// Allows only numbers with decimals
$(element).on("keydown", function(event) {
    // Allow: backspace, delete, tab, escape, and enter
    if (event.keyCode === 46 || event.keyCode === 8 || event.keyCode === 9 || event.keyCode === 27 || event.keyCode === 13 ||
        // Allow: Ctrl+A
        (event.keyCode === 65 && (event.ctrlKey === true || event.metaKey === true)) ||
        // Allow: .
        (event.keyCode === 190 || event.keyCode === 110) ||
        // Allow: home, end, left, right
        (event.keyCode >= 35 && event.keyCode <= 39)) {
        // let it happen, but check for excessive periods
        if ((event.keyCode === 190 || event.keyCode === 110) && $(element).val().indexOf('.') !== -1) {
            event.preventDefault();
        }
        return;
    } else {
        // Ensure that it is a number and stop the keypress
        if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) {
            event.preventDefault();
        }
    }
});

I added the following code where it allows certain keycodes:

if ((event.keyCode === 190 || event.keyCode === 110) && $(element).val().indexOf('.') !== -1) {
    event.preventDefault();
}

Upvotes: 0

Doug Leary
Doug Leary

Reputation: 318

if ((yourString.match(new RegExp(".", "g")) || []).length > 1) {
  // yourString contains more than one "."
}

Upvotes: 0

Related Questions