4

I have this Knockout custom binding to validate a text box to contain only English letters. But, it seems like Javascript's String.fromCharCode returns wrong values.

For example, the Hebrew letter "ש" returns as the English letter "A", and the number "1" from the right numbers pad has returns as "a"...

Here is my Knockout binding:

var arrValidKeys = [8, 16, 17, 20, 35, 36, 37, 39, 46];
    ko.bindingHandlers.validateText = {
        init: function (element, valueAccessor) {
            $(element).on("keydown", function (event) {

                //Regex pattern: allow only (A to Z uppercase, a to z lowercase)
                var englishAlphabet = /[A-Za-z]/g;

                // Retrieving the key from the char code passed in event.which
                var key = String.fromCharCode(event.which);

                // keyCodes list: http://stackoverflow.com/a/3781360/114029
                // check that the key is valid with the above Regex
                valueAccessor()($(this).val());

                return ((jQuery.inArray(event.keyCode, arrValidKeys) != -1) || englishAlphabet.test(key));
            });

            $(element).on("keyup", function (event) {

                //Regex pattern: allow only (A to Z uppercase, a to z lowercase)
                var englishAlphabet = /[A-Za-z]/g;

                // Retrieving the key from the char code passed in event.which
                var key = String.fromCharCode(event.which);

                // keyCodes list: http://stackoverflow.com/a/3781360/114029
                // check that the key is valid with the above Regex

                valueAccessor()($(this).val());

                return ((jQuery.inArray(event.keyCode, arrValidKeys) != -1) || englishAlphabet.test(key));
            });

            $(element).on("paste", function (e) {
                var englishAlphabet = /[A-Za-z]/g;
                if (englishAlphabet.test($(this).val()))
                    valueAccessor()($(this).val());
                else
                    e.preventDefault();
            });
        }
    };
haim770
  • 48,394
  • 7
  • 105
  • 133
Liran Friedman
  • 4,027
  • 13
  • 53
  • 96
  • `event.keyCode` and `event.which` are the same thing in jQuery - best to use one or the other. Using both can be confusing. – Alex McMillan May 11 '15 at 07:17
  • Something that might help us is a jsfiddle :) – Jeanluca Scaljeri May 11 '15 at 07:35
  • I'm working on a jsfiddle for you :) – Liran Friedman May 11 '15 at 11:54
  • A jsfiddle, just for you :) https://jsfiddle.net/2ma9nhn7/ – Liran Friedman May 11 '15 at 12:00
  • Seems to work well for me. I've updated the fiddle a bit, because with `paste` it didn't work: http://jsfiddle.net/2ma9nhn7/2/ What value holds `event.which` if you hit `1` ? – Jeanluca Scaljeri May 11 '15 at 13:04
  • There are 2 sets of numbers on a desktop keyborad:one on the top of the letters and the other one to the right where the "NumLock" button is. And if I press "1" from the right it translates i into "a", and letters in other languages are also enabled and I want only English letters to be enabled – Liran Friedman May 11 '15 at 13:31
  • I don't have those keys :) Your problem does sound however like [this](http://stackoverflow.com/questions/5630918/get-correct-keycode-for-keypadnumpad-keys) post. – Jeanluca Scaljeri May 11 '15 at 14:04
  • I've tried it at the beginning, but the `keypress` event doesn't fire when pressing the backspace (for delete) button... So I ended up using all 3 events fo this: `keypress`, 'keydown' and `keyup` – Liran Friedman May 11 '15 at 14:19

1 Answers1

0

Following @Jeanluca Scaljeri's suggestion from this post: Get Correct keyCode for keypad(numpad) keys

I updated my code to this:

// keyCodes list: https://stackoverflow.com/a/3781360/114029
var arrValidKeys = [8, /*backspace*/
                    16, /*shift*/
                    17, /*ctrl*/
                    20, /*caps lock*/
                    35, /*end*/
                    36, /*home*/
                    37, /*left arrow*/
                    39, /*right arrow*/
                    46, /*delete*/];

function validateChar(keyCode) {
    //Regex pattern: allow only (A to Z uppercase, a to z lowercase)
    var englishAlphabet = /[A-Za-z]/g;

    //Retrieving the key from the char code passed in event.which
    var key = String.fromCharCode(keyCode);

    //Check that the key is valid with the above Regex
    return ((jQuery.inArray(keyCode, arrValidKeys) != -1) ||     englishAlphabet.test(key));
}

ko.bindingHandlers.validateText = {
    init: function (element, valueAccessor) {
        $(element).on("keypress", function (event) {
            valueAccessor()($(this).val());
            return validateChar(event.which);
        });

        $(element).on("keydown", function (event) {
            valueAccessor()($(this).val());
            return validateChar(event.which);
        });

        $(element).on("keyup", function (event) {
            valueAccessor()($(this).val());
            return validateChar(event.which);
        });

        $(element).on("paste", function (e) {
            var englishAlphabet = /^[A-Za-z]+$/g;
            var text = e.originalEvent.clipboardData.getData('Text');
            if (!englishAlphabet.test(text))
                e.preventDefault();
        });
    }
};

The reason I've added all 3 events (keypress, keydown and keyup) is that keypress alone does not fire when pressing the backspace key, and I need it to update a label with knockout observable.

And here is a working jsfiddle link: https://jsfiddle.net/2ma9nhn7/6/

Community
  • 1
  • 1
Liran Friedman
  • 4,027
  • 13
  • 53
  • 96