Input/character validation on an input-element

Posted on

Problem

I took some time to create a function, which can validate which keys you input on an element. So far it has two uses:

  1. You call .validate() on the object, but make sure it has a data-match attribute with the regex in it
  2. You call .validate("REGEX") on the object, but replace REGEX with your actual regular expression

Here is an example:

document.getElementById("input").validate("[0-9A-Fa-f]");

This will only allow characters 0-9, A-F, and a-f. Nothing else.

I want to know, if I can improve this code. So far I have it the way I want it to be, but I would surely love someone else’s feedback on it as well. It is really light-weight and can easily be implemented.

Thanks a lot!

Object.prototype.validate = function(myRegex) {
    var regex = this.getAttribute("data-match") || myRegex;
    var allowed = [8, 37, 38, 39, 40, 46];
    this.addEventListener("keydown", function(e) {
        var c = (e.metaKey ? '⌘-' : '') + String.fromCharCode(e.keyCode);
        if (!e.shiftKey && !e.metaKey) c = c.toLowerCase();
        if ((e.ctrlKey && (e.keyCode == 65 || e.keyCode == 97)) || c == '⌘-A') return true;
        if (c.match(regex) || allowed.indexOf(e.which) >= 0) {
            //Do nothing
        } else {
            e.preventDefault();
        }
    });
}

document.getElementById("input").validate("[0-9A-Fa-f]");
<input type="text" id="input" />

Solution

Whatever you do DON’T MODIFY Object.prototype

Modifying Object.prototype is one of the worst things you can do in JavaScript

Quoting MDN:

Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine. The effects on performance of altering inheritance are subtle and far-flung, and are not limited to simply the time spent in obj.__proto__ = ... statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. If you care about performance you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().


What should I do?

Preferably create a function:

function Validate(element, regex) {
    // code
}

but if you want to extend an element use Element.prototype, preferable with Object.defineProperty:

Object.defineProperty(Element.prototype, 'validate', {
    value: function(regex) {
        // code
    }
});

The bonus of using this is you can instead of getters and setters which are pretty cool too.

Avoid empty ifs

Here you have:

if (c.match(regex) || allowed.indexOf(e.which) >= 0) {
    //Do nothing
} else {
    e.preventDefault();
}

instead, use a NOT (!):

if (!(c.match(regex) || allowed.indexOf(e.which) >= 0))
    e.preventDefault();

the best way to write this specific condition would be:

if(!c.match(regex) || allowed.indexOf(e.which) < 0)

Use clear variable names

As much as I love code-golf (writing short code), this is not it. Make sure your variable names are clear.

I don’t know what the c variable does so I can’t suggest an alternative name, this also makes it more confusing to me on the workings of your code.

Use Regex literals not strings

Your example shows:

"[0-9A-Fa-f]"

That’s a string, not a Regex, use Regex literals (to avoid escaping also):

/[0-9A-Fa-f]/

Change.. all the objects!

No, don’t do that. Downgoat already explains why. This will touch every object. I shouldn’t be able to do this:

var foo = {
    bar: "hello world"
};

foo.validate(...);

Instead, if you want to modify prototypes, you should modify the prototype of HTMLInputElement. This will only touch input elements now, but you should still do some validation that the element is of type text.

HTMLInputElement.prototype.validate = function() {
    ...
}

Built-in pattern

Now, I don’t know if you already know about this, but input tags already have a feature for validating input: pattern.

With pattern, you simply specify a regex and the form cannot be submitted unless that criteria is met. Now, this isn’t exactly what you have: you have it so the user cannot even enter a wrong character. I personally don’t think this makes a difference; after-all, most every input field should exist in some sort of form.

Here’s an example usage:

<input type="text" pattern="([0-9]){3}"/>

Another good reason for using the built-in pattern would be that you won’t have to do any extra meta key validation when the user is typing, in case you aren’t handling all the possible cases. pattern is much more flexible.

Leave a Reply

Your email address will not be published. Required fields are marked *