8

Following on from another question I asked, i really didnt seem to be getting anywhere. Due to my ineptitude. I chose the guys answer because , well he answered my question.

I am gathering I didnt ask the right question cos I have no idea what to do ..

So issue is I have input element . Keeping it simple;

<input type="text" maxlength="12" name="price" id="price" class="foo">

I want users to be able to type in numbers only and only one period ( . ) anywhere in that price. so could be 3.00 or 300.00 or 3000

Could someone please help me out, I am going goggle eyed.

The Older question asked was here Quick regex with alert

Community
  • 1
  • 1
422
  • 5,714
  • 23
  • 83
  • 139
  • what about `300.`? Is that a valid input? – Anurag Jan 20 '12 at 02:20
  • @Anurag yes thats fine. we can strip out the . if nothing follows it server side – 422 Jan 20 '12 at 02:25
  • is just a `.` an acceptable input? – Anurag Jan 20 '12 at 02:28
  • @Anurag no. I was thinking, that regex ( not that I understand it or how to implement it ) could be set to expect a number ( min 1 number ) then any combination of either other numbers or one period. – 422 Jan 20 '12 at 02:34
  • Can it be a number like 123.45 or does the part after a decimal (if one exists) have to be all zeros? – j08691 Jan 20 '12 at 02:44
  • can be any combination of numbers with or without a decimal point ( period for the americans ) and only one decimal point , no ther characters. So numbers only and only one period ( other words , period is not required, but if one is typed , no more can be typed ) – 422 Jan 20 '12 at 02:48

3 Answers3

12

You could, in the change event of the input, check if the number format is OK. This code will try to get the number and remove anything else: (I'm assuming you use jQuery, if not, please do)

$('#price').change(function() {
    $(this).val($(this).val().match(/\d*\.?\d+/));
});

See it working here.

EDIT: if you don't have jQuery, this code does the same (at least in Chrome):

document.getElementById('price').onchange = function() {
    this.value = this.value.match(/\d*\.?\d+/);
};

EDIT 2: not sure if I follow, but you could add this too to prevent letters and other characters before the change event:

$('#price').keypress(function(event) {
    var code = (event.keyCode ? event.keyCode : event.which);
    if (!(
            (code >= 48 && code <= 57) //numbers
            || (code == 46) //period
        )
        || (code == 46 && $(this).val().indexOf('.') != -1)
       )
        event.preventDefault();
});
cambraca
  • 27,014
  • 16
  • 68
  • 99
  • I do use jquery and your example isnt working for me, I can type absolutely anything. ?? – 422 Jan 20 '12 at 02:24
  • Yes, and it should keep only the number. Look at my fiddle (the link I posted) – cambraca Jan 20 '12 at 02:25
  • and look at my question ( the question i posted ) numbers only and only one period – 422 Jan 20 '12 at 02:26
  • Thanks @cambraca your second edit, results in no ability to type. – 422 Jan 20 '12 at 02:38
  • I'm not fond of doing it on the keypress event, it could be buggy, but it definitely lets you type numbers and the period (at least in my test it did, Chrome in Win7) – cambraca Jan 20 '12 at 02:41
  • @cambraca: it doesn't have to be buggy! The link I posted is cross browser. I use this technique and it makes more sense than resetting the value after it's been changed. The keypress approach deals with it before the value ever changes – Ruan Mendes Jan 20 '12 at 02:46
  • Hi @cambraca yeah nearly there http://jsfiddle.net/ozzy/nSFBk/2/ seemingly i can add more than one period. I guess theres no way round it.. – 422 Jan 20 '12 at 02:46
  • oh and seeminlgy need to allow the backspace button lol ( and no more than two numbers after a period is entered ) god its complex huh – 422 Jan 20 '12 at 02:50
  • @cambraca - You don't need to test for `event.keyCode` or `event.which`. jQuery is already normalizing the event object to be cross-browser, so you can always get the value from `event.keyCode`. – Anurag Jan 20 '12 at 02:51
  • @422 - and while we're at it.. try pasting some junk value into the box. – Anurag Jan 20 '12 at 02:51
  • @Anurag Actually I first did it using only `event.keyCode`, but it didn't work in Firefox. – cambraca Jan 20 '12 at 02:51
  • That's why I don't like using the `keypress` approach. It looks much cleaner to me if you just let the user type anything and filter it using the `change` event, like my first examples. – cambraca Jan 20 '12 at 02:53
  • @cambraca - I agree, it's best to let users type in whatever they want and just warn them if the value they entered was invalid. I also found a link to a [question](http://stackoverflow.com/questions/302122/jquery-event-keypress-which-key-was-pressed) discussing jQuery's lack for proper normalization. – Anurag Jan 20 '12 at 02:55
  • @cambraca yes the first approach is fine, but we are dealing with ppl who are adding an advertisiment and entering in a value for the goods they wish to sell. I just wanted some inline method for restricting and controlling price input. – 422 Jan 20 '12 at 02:56
  • @422 any way you do it (I still recommend using the first option only), remember to validate in the server. These JS validations are very easy to cheat. – cambraca Jan 20 '12 at 03:00
  • 1
    ok thankyou I will do that on client side, it is light weight and only a idiot would type bugger.it lol – 422 Jan 20 '12 at 03:07
1

Here is my solution(It also validates data/values copy&pasted):

function InputValidator(input, validationType, validChars) {
if (input === null || input.nodeType !== 1 || input.type !== 'text' && input.type !== 'number')
    throw ('Please specify a valid input');

if (!(InputValidator.ValidationType.hasOwnProperty(validationType) || validationType))
    throw 'Please specify a valid Validation type';

input.InputValidator = this;

input.InputValidator.ValidCodes = [];

input.InputValidator.ValidCodes.Add = function (item) {
    this[this.length] = item;
};

input.InputValidator.ValidCodes.hasValue = function (value, target) {
    var i;
    for (i = 0; i < this.length; i++) {
        if (typeof (target) === 'undefined') {
            if (this[i] === value)
                return true;
        }
        else {
            if (this[i][target] === value)
                return true;
        }
    }

    return false;
};

var commandKeys = {
    'backspace': 8,
    'tab': 9,
    'enter': 13,
    'shift': 16,
    'ctrl': 17,
    'alt': 18,
    'pause/break': 19,
    'caps lock': 20,
    'escape': 27,
    'page up': 33,
    'page down': 34,
    'end': 35,
    'home': 36,
    'left arrow': 37,
    'up arrow': 38,
    'right arrow': 39,
    'down arrow': 40,
    'insert': 45,
    'delete': 46,
    'left window key': 91,
    'right window key': 92,
    'select key': 93,
    /*creates Confusion in IE */
    //'f1': 112,
    //'f2': 113,
    //'f3': 114,
    //'f4': 115,
    //'f5': 116,
    //'f6': 117,
    //'f7': 118,
    //'f8': 119,
    //'f9': 120,
    //'f10': 121,
    //'f11': 122,
    //'f12': 123,
    'num lock': 144,
    'scroll lock': 145,
};

commandKeys.hasValue = function (value) {
    for (var a in this) {
        if (this[a] === value)
            return true;
    }

    return false;
};

function getCharCodes(arrTarget, chars) {
    for (var i = 0; i < chars.length; i++) {
        arrTarget.Add(chars[i].charCodeAt(0));
    }
}

function triggerEvent(name, element) {
    if (document.createEventObject) {
        // dispatch for IE
        var evt = document.createEventObject();
        return element.fireEvent('on' + name, evt)
    }
    else {
        // dispatch for firefox + others
        var evt = document.createEvent("HTMLEvents");
        evt.initEvent(name, true, true); // event type,bubbling,cancelable
        return !element.dispatchEvent(evt);
    }
}

if (validationType == InputValidator.ValidationType.Custom) {
    if (typeof (validChars) === 'undefined')
        throw 'Please add valid characters';

    getCharCodes(input.InputValidator.ValidCodes, validChars);
}
else if (validationType == InputValidator.ValidationType.Decimal) {
    getCharCodes(input.InputValidator.ValidCodes, '0123456789.');
}
else if (validationType == InputValidator.ValidationType.Numeric) {
    getCharCodes(input.InputValidator.ValidCodes, '0123456789');
}

input.InputValidator.ValidateChar = function (c) {
    return this.ValidCodes.hasValue(c.charCodeAt(0));
}

input.InputValidator.ValidateString = function (s) {
    var arr = s.split('');

    for (var i = 0; i < arr.length; i++) {
        if (!this.ValidateChar(arr[i])) {
            arr[i] = '';
        }
    }

    return arr.join('');
}

function bindEvent(el, eventName, eventHandler) {
    if (el.addEventListener) {
        el.addEventListener(eventName, eventHandler, false);
    } else if (el.attachEvent) {
        el.attachEvent('on' + eventName, eventHandler);
    }
}

function getCaretPosition(i) {
    if (!i) return;

    if ('selectionStart' in i) {
        return i.selectionStart;
    }
    else {
        if (document.selection) {
            var sel = document.selection.createRange();
            var selLen = document.selection.createRange().text.length;
            sel.moveStart('character', -i.value.length);
            return sel.text.length - selLen;
        }
    }
}

function setCursor(node, pos) {
    var node = (typeof (node) === "string" || node instanceof String) ? document.getElementById(node) : node;

    if (!node) {
        return false;
    }
    else if (node.createTextRange) {
        var textRange = node.createTextRange();
        textRange.collapse(true);
        textRange.moveEnd(pos);
        textRange.moveStart(pos);
        textRange.select();
        return true;
    } else if (node.setSelectionRange) {
        node.setSelectionRange(pos, pos);
        return true;
    }

    return false;
}

function validateActive() {
    if (input.isActive) {
        var pos = getCaretPosition(input);

        var arr = input.value.split('');

        for (var i = 0; i < arr.length; i++) {
            if (!this.ValidateChar(arr[i])) {
                arr[i] = '';

                if (pos > i)
                    pos--;
            }
        }
        console.log('before : ' + input.value);

        input.value = arr.join('');
        console.log('after : ' + input.value, input);
        setCursor(input, pos);

        setTimeout(validateActive, 10);
    }
}

bindEvent(input, 'keypress', function (e) {
    var evt = e || window.event;
    var charCode = evt.which || evt.keyCode;

    if (!input.InputValidator.ValidCodes.hasValue(charCode) && !commandKeys.hasValue(charCode)) {
        if (evt.preventDefault) {
            evt.preventDefault();
            evt.stopPropagation();
        }
        return false;
    }
});

bindEvent(input, 'keyup', function (e) {
    var evt = e || window.event;
    var charCode = evt.which || evt.keyCode;

    if (!input.InputValidator.ValidCodes.hasValue(charCode) && !commandKeys.hasValue(charCode)) {
        if (evt.preventDefault) {
            evt.preventDefault();
            evt.stopPropagation();
        }
        return false;
    }
});

bindEvent(input, 'change', function (e) {
    var dt = input.value;

    input.value = input.InputValidator.ValidateString(input.value);

    if (input.value !== dt)
        triggerEvent('change', input);
});

bindEvent(input, 'blur', function (e) {
    var dt = input.value;
    input.value = input.InputValidator.ValidateString(input.value);

    input.isActive = false;

    if (input.value !== dt)
        triggerEvent('blur', input);
});

bindEvent(input, 'paste', function (e) {
    var evt = e || window.event;
    var svt = input.value;

    if (evt && evt.clipboardData && evt.clipboardData.getData) {// Webkit - get data from clipboard, put into editdiv, cleanup, then cancel event
        if (/text\/html/.test(evt.clipboardData.types)) {
            var dt = evt.clipboardData.getData('text/html');

            input.value = input.InputValidator.ValidateString(dt);
            if (input.value !== dt)
                triggerEvent('change', input);
        }
        else if (/text\/plain/.test(e.clipboardData.types)) {
            var dt = evt.clipboardData.getData('text/plain');

            input.value = input.InputValidator.ValidateString(dt);
            if (input.value !== dt)
                triggerEvent('change', input);
        }
        else {
            input.value = '';
        }
        waitforpastedata(input, svt);
        if (e.preventDefault) {
            e.stopPropagation();
            e.preventDefault();
        }
        return false;
    }
    else {// Everything else - empty editdiv and allow browser to paste content into it, then cleanup
        input.value = '';
        waitforpastedata(input, svt);
        return true;
    }
});

bindEvent(input, 'select', function (e) {
    var evt = e || window.event;

    if (evt.preventDefault) {
        evt.preventDefault();
        evt.stopPropagation();
    }
    return false;
});

bindEvent(input, 'selectstart', function (e) {
    var evt = e || window.event;

    if (evt.preventDefault) {
        evt.preventDefault();
        evt.stopPropagation();
    }
    return false;
});

/* no need to validate wile active,
   removing F keys fixed IE compatability*/
//bindEvent(input, 'fucus', function (e) {
//    input.isActive = true;
//    validateActive();
//});

//validate current value of the textbox
{
    var dt = input.value;
    input.value = input.InputValidator.ValidateString(input.value);

    //trigger event to indicate value has changed
    if (input.value !== dt)
        triggerEvent('change', input);
}

function waitforpastedata(elem, savedcontent) {
    if (elem.value !== '') {
        var dt = input.value;
        elem.value = elem.InputValidator.ValidateString(elem.value);

        if (input.value !== dt)
            triggerEvent('change', input);
    }
    else {
        var that = {
            e: elem,
            s: savedcontent
        }
        that.callself = function () {
            waitforpastedata(that.e, that.s)
        }
        setTimeout(that.callself, 10);
    }
}
}

InputValidator.ValidationType = new (function (types) {
    for (var i = 0; i < types.length; i++) {
        this[types[i]] = types[i];
    }
})(['Numeric', 'Custom', 'Decimal']);

To apply it to an input, do the following :

new InputValidator(document.getElementById('txtValidate'), InputValidator.ValidationType.Decimal);/* Numeric or Custom */

If you specify Custom as the validation type you have to specify the valid characters. eg :

new InputValidator(document.getElementById('txtValidate'), InputValidator.ValidationType.Custom,'1234abc');
Frans
  • 66
  • 2
0

Cambraca aproach kind of works, but the best one is the last mentioned approach , you cancel the keypress event filtering the keys before it shows up instead of undoing what was already done. A consequence of changing the value after the fact is that it may affect the position of the caret in the field.

Here's an example of abstracting the idea in a cross-browser way. Somebody should port this to a jQuery plugin http://www.qodo.co.uk/assets/files/javascript-restrict-keyboard-character-input.html

Ok, I guess I'll port it. But I'm not a jQuery guy so this is an untested bare bones jQuery plugin that uses their code http://jsfiddle.net/mendesjuan/VNSU7/3

(function( $ ) {
    $.fn.restrict = function(regExp, additionalRestriction) {
        function restrictCharacters(myfield, e, restrictionType) {
            var code = e.which;
            var character = String.fromCharCode(code);
            // if they pressed esc... remove focus from field...
            if (code==27) { this.blur(); return false; }
            // ignore if they are press other keys
            // strange because code: 39 is the down key AND ' key...
            // and DEL also equals .
            if (!e.originalEvent.ctrlKey && code!=9 && code!=8 && code!=36 && code!=37 && code!=38 && (code!=39 || (code==39 && character=="'")) && code!=40) {
                if (character.match(restrictionType)) {
                    return additionalRestriction(myfield.value, character);
                } else {
                    return false;
                }
            }
        }
        this.keypress(function(e){
            if (!restrictCharacters(this, e, regExp)) {
                e.preventDefault();
            }
        });
    };
})( jQuery );

$('#field').restrict(/[0-9\.]/g, function (currentValue, newChar) {
    return !(currentValue.indexOf('.') != -1 && newChar == ".");    
});
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • the issue still arises, multiple periods. Plus any number of digits after a period. Hovever this approach does allow delete key – 422 Jan 20 '12 at 02:54
  • @422: Take a look again. Now it does have a way to not allow multiple periods and it's implemented. You could modify the extra restriction so that it only allows a number of digits after the period by modifying the second parameter to `restrict` – Ruan Mendes Jan 20 '12 at 03:24