Forum Moderators: open
The types I've got planned so far are:
Free text
Letters (including space but no symbols)
Numbers
Both (like an address line)
Phone number
An email address
Dates (various formats)
Custom field (like a reference number, you'll be able to specify a character sequence)
Any other common things I've missed?
Other features will include, case transformation, phone number formatting, date validity, popup calendar, default text, force required fields, active/disable by checkbox/select....and finally, parent CSS class changing.
I'd also like any ideas and feedback you guys can think of to throw into this, of course it will be freely available for use and scrutinization when it's workable!
[edited by: Dabrowski at 2:02 pm (utc) on Sep. 21, 2008]
For Numbers, maybe split it into multiple types: Integer, Decimal (need to support . or , to mark the beginning of the decimal portion), etc.
Both (Letters + Numbers) = Alphanumeric, correct?
What format of phone numbers? US? International? UK? etc.
And sometimes there are situations where no prerolled validation pattern will do, so I have a basic class for validation with Regex, and for inheriting more than one validation rule. For instance, "password must be greater than 6 characters long with no spaces, and contain at least one numeric character and one punctuation character".
A universal system should also be capable of "callbacks" - using AJAX to hit up a database, with a DAL that returns true/false. Example: "that username has been taken, please choose another"
optional parameter: whether the value gets a trim() before measuring length. default = true.
A length filter is useful for checking emptiness of a field. You just set minimum to 1 instead of 0 (the default). If maximum is null or undefined, that means no maximum, i.e. infinity
What is a "Free text" field? What would be considered invalid free text?
For Numbers, maybe split it into multiple types: Integer, Decimal
Both (Letters + Numbers) = Alphanumeric, correct
What format of phone numbers? US? International? UK?
where no prerolled validation pattern will do, so I have a basic class for validation with Regex
A universal system should also be capable of "callbacks" - using AJAX to hit up a database, with a DAL that returns true/false. Example: "that username has been taken, please choose another"
whether the value gets a trim()
A length filter is useful for checking emptiness of a field
Some more info on the way I intend the custom field to work, specify a string, which is code for letters, numbers, or literal characters. Kinda like a very basic regex.
e.g.
* = a letter
# = a number
@ = either
anything else = itself
*@## *** would be a UK car reg number.
Ooo I also missed times off my list, so, added the following types:
Decimal number
Price
Time
Password
var screen=newArray('Email','home_ph',work_ph');
So you could iterate through these and build an alert someone would understand, except this . . . .
for (n=0;n<screen.length;n++) {
if document.getElementById(screen[n].value == '') {
alert('The ' + screen[n] + ' field is required.');
break;
}
}
Would produce
The Email field is required. (good enough . . .)
The home_ph field is required. (ACK!)
The work_ph is required. (ACK x 2!)
Everyone will know what home_ph means, right? Nope . .... so you could attempt to use spaces in id's (nope) or names that are closer,
home_phone
But these are hardly "perfect" and require a compromise that would be more difficult than changing the script (modifying your page id methods or just settling for "good enough.") Currently I use two arrays, but as Fotiman pointed out, it's much better to use an associative array:
var screen=newArray('Email','home_ph',work_ph');
var eng=newArray('email address','home phone','work phone');
........
alert('The ' + eng[n] + ' field is required.');
So now you're just down to modifying the arrays for each project.
Food for thought.
It's probably better just to paste this into a test page rather than read through it.
Basically, on the middle input section here, I've appended "-error" onto the CSS class, ideally my script will append that onto the INPUT tags' parent tag. Hence adding a coloured background, and revealing the error message. You could even stick a little icon in there with a popup explaining the input type. Really it passes the buck on error reporting, this way is much prettier, and as you say there's no one-size-fits-all solution really.
<style>
LABEL {
margin: 0;padding: 0;
display: inline-block;
width: 150px;
}
.input-group {
padding: 5px 0;
width: 498px;
background: lightgrey;
border: 1px solid black;
}
.input-group .text {
margin: 0;padding: 0;
width: 130px;
margin-right: 10px;
border: 1px solid black;
}
.input-group .note {
font-size: 9px;
text-transform: uppercase;
color: gray;
}
.input-field {
padding: 0 5px;
}
.input-field INPUT .ok {border: 1px solid green;}
.input-field INPUT .not-ok{border: 1px solid red;}
.input-field INPUT .partial {border: 1px solid yellow;}
.input-field .error {
display: none;
}
.input-field-error {
padding: 5px;
background: #FF8844;
}
.input-field-error .error {
display: block;
font-weight: bold;
color: white;
}
</style>
<div class='input-group'>
<div class='input-field'>
<label>Any text here</label>
<input class='text' name='freetext' type='text'>
</div>
<div class='input-field-error'>
<label>Any text here</label>
<input class='text' name='freetext-required' type='text'>
<span class='note'>Required</span>
<span class='error'>You must enter something!</span>
</div>
<div class='input-field'>
<label>Any text here</label>
<input class='text' name='freetext-default' type='text'>
<span class='note'>Default text not allowed</span>
<span class='error'>You must enter something!</span>
</div>
</div>
that a user with no CSS enabled
I hadn't thought of that. Although you could argue that that device probably wouldn't support JS either, so you wouldn't build the page in this way.
The idea was to give the control of the errors and styles to the page rather than the script for better integration.
I guess as well as changing the parents' class I could inject the <span class='error'> and give the option to set the text?
Foti, Rocky, What do you think?
* = a letter
# = a number
@ = either
anything else = itself
@ = a letter
# = a number
* = either
anything else = itself
But what if the "anything else" contains one of the tokens characters? For example, what if I wanted to match a string like:
"Fotiman@example.com"
I would suggest that if you're going to go the route or creating your own regex language, you'd need to specify an escape character as well. For example:
@ = a letter
# = a number
* = either
anything else = itself
\@ = a literal "@"
\# = a literal "#"
\* = a literal "*"
\\ = a literal "\"
good point. It's very uncommon for someone to be JS enabled, and CSS disabled.
Yet in the platform I work on, that's one of the test cases we support. Everything in the DOM must be semantically correct as well as functionally robust. It does make things quite a lot more complex, but we haven't discovered a browser/agent/device yet that doesn't look and perform perfectly with our system.
If it were me, I'd probably do both. Create a simple language with a very minimal set of tokens (not forgetting something to escape those tokens) and let your program use either a string containing your simple-language pattern or a RegEx object.
2 cents.
I would lean towards injecting the error message
ok, then I'll look for an element with a class of something like 'error-message' and if there isn't one I'll stick a span in, in the style of my code posted above.
Fotiman@example.com
Use an email address type! :-D
Why would you want to create your own Regex-like pattern matching language
Trust me, I wouldn't even attempt to attempt that! Nothing that advanced.
What I had in mind really was for simple things like reference numbers, there are so many I couldn't possibly code all of them individually, but a simple way of creating a customised field (using Foti's slight change), examples:
car reg no UK.......X123 WYZ or AB08 CDE......@*## @@@
bank sort code......12-34-56..................##-##-##
BT account ref......AB12349876................@@########
HMRC PAYE...........123PP09876543.............###PP########
NI number...........XX 12 34 56 Y.............@@ ## ## ## @
You see what I mean?
An escape character might be handy, I guess some ref numbers could have a # or something in them.
assumption is that you wanted something simple that anyone could use
Correct. Although I will add regex, that way an experienced coder could still use the script but hopefully customise it to his liking.
I've now got a very large test form, and some working code, and I've started to put the basics together.
I've been thinking about how to actually tell the script which boxes are which. I've come up with 2 possibilities so far, one of which will make you all shout at me a lot I think.
Idea 1 - hidden form fields:
<input type='hidden' name='formconfig' value='yourname; required; type, letters, allow, spaces; transform, capitalize'>
...
<input type='text' name='yourname'>
So basically the value string contains:
yourname - name of input box
required - will error on submit if not filled in
type, letters, allow, spaces - tells it to accept letters but allow spaces
transform, capitalize - capitalize the name, e.g. user types "john doe", changes to "John Doe".
With ; and , being major and minor command separators (which I've already added a way to change ;) ).
Idea 2 ... bring the fire ... an invalid property:
<input type='text' name='yourname' validate='required; type, letters, allow, spaces; transform, capitalize'>
I realise that's probably not 100% compatible, but I'm sure I've used that method before for something and it worked ok. I believe the W3C does say somewhere that any non standard properties should be added to the element?
<script type="text/javascript">
validate({
items: [
{id:'yourname', req: true, type: 'letters', allow: 'spaces', transform: 'capitalize'},
{id:'yourname2', type: 'letters'}
]
});
</script>
In other words, pass in an object to your validator. This object has an 'items' property which contains an array of the fields to validate, as well as the rules to validate against.
This would be a much cleaner approach. It's unobtrusive and keeps all of your validation logic out of the markup.
{el: 'yourname'} // If el is a string, getElementById
{el: document.getElementById('yourname')} // if el is an HTML Element, work on that
{el: {form:{name:'yourform',id:''},field:'yourname'}
{el: {form: document.getElementById('yourform'), field:'yourname'}
In other words, add some logic to allow for greater configurability so that users can specify which fields to validate with a variety of methods.
Just a thought.
The only problem I see with the first method, is that will attempt to execute and validate as soon as it hits the script tag, which means the form may not be properly formed yet.
The advantage my way is that the validator uses onload to run and looks for the formconfig tags once the page is done. Also the input tags will be inside the relevant form. The script will be able to handle multiple forms on the page.
I'm really only interested in IE6, IE7 and FF, that's 94% according to:
[thecounter.com...]
And if the other few don't work the page will still be ok.
(Edit: Not sure if that URL is allowed here, in case not you need to go to Yahoo Groups and find the "Validation" group.)
you will have to join the Validation group to be allowed to get to the FILES menu :(, from there you need "Cross Browser" and then the "validation 3.1.5" ZIP file. In that is readme.htm. This has a description of all the validation types and methods supported.
It may give you some good ideas for your project.
In essence you include the relevant JS script on your page, and then add some "hints" to the Form fields - either directly in the INPUT tag, or using Javascript to "apply" them to the fields.
Example:
<input name="MyDate" type="text" display-name="Start Date" date="MM/YYYY" required="on" />
this will force the user to enter something in this field ("Required") and make sure that the Month/Year is valid.
There are methods for adding user-friendly display names to field, and suitable messages to be displayed, and so on.
There are also Pre/Post processing function hooks which can be used to customise the validation further - such as onbeforevalidate / onaftervalidate / onautosubmit
[edited by: Krispy2 at 10:42 am (utc) on Oct. 6, 2008]
I've decided against that. I don't believe it can be 100% reliable as it's not standard code. The HTML validation gurus would have a field day with it.
I'm sticking with my hidden form fields idea. It's valid, and the most simple for other people to use.
The pre/post processing sounds like a good idea though, have you ever found it useful?
This example demonstrates:
1. Your markup remains clean.
2. A class 'DaValidator', which can take the configuration data in the constructor or in an init method call. Public methods defined in the prototype object for memory efficiency.
3. Multiple instances of the validator. One demonstrates passing the config in the constructor, the other demonstrates passing it in the init method.
4. A callback method when the validator fails (useful for capturing the fields that errored and showing that to the user).
Hope this is helpful. At the very least, it was quick and fun. :)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<title>Sample DaValidator</title>
</head>
<body>
<form action="" id="userForm">
<div>
First Name: <input id="firstname" name="firstname">
Price: <input id="price" name="price">
<input type="submit" value="submit">
</div>
</form>
<form action="" id="contactForm">
<div>
Phone: <input id="phonenum" name="phonenum">
Email: <input id="email" name="email">
<input type="submit" value="submit">
</div>
</form>
<!-- Scripts -->
<script type="text/javascript">
function DaValidator(oCfg) {
this.cfg = oCfg ¦¦ {};
}
DaValidator.prototype = {
init: function (oCfg) {
// Valid config object properties:
// items {Array} ...
// ...
this.cfg = oCfg;
},
validate: function (fn) {
var i, o, result = true;
var el, aStatus = [];
if (!this.cfg ¦¦ !this.cfg.items) {
// No items to validate
return result;
}
o = this.cfg.items;
for (i = 0; i < o.length; i++) {
el = document.getElementById(o[i].id);
// Check each of the possible validation properties
if (o[i].req) {
if (el.value.length == 0) {
result = false;
aStatus[aStatus.length] = {id:o[i].id, msg:'Missing required value on ' + o[i].id};
}
}
// if (o[i].type) {...}
// if (o[i].allow) {...}
// etc.
}
// fire the callback method
if (fn) {
fn.call(this,aStatus);
}
return result;
}
};
</script>
<script type="text/javascript">
window.onload = function () {
var V = new DaValidator();
V.init({
items: [
{id:'firstname', req:true, type:'letters', allow:'spaces'},
{id:'price', req:true, type:'email'}
]
});
var V2 = new DaValidator({
items: [
{id:'phonenum', req:true},
{id:'email', req:true}
]
});
function validatorCallBack (a) {
var i, str = '';
// We have the scope of the DaValidator instance, so we can
// access properties of the DaValidator instance using 'this'
if (this.cfg && this.cfg.items) {
alert('Number of items checked: ' + this.cfg.items.length);
}
for (i = 0; i < a.length; i++) {
str += a[i].msg + '\n';
}
alert(str);
}
document.getElementById('userForm').onsubmit = function () {
return V.validate(validatorCallBack);
};
document.getElementById('contactForm').onsubmit = function () {
return V2.validate(validatorCallBack);
};
};
</script>
</body>
</html>
I think you really should consider building this to be driven server side, data in a database, with generated Javascript code for the fields. You are going to have to do the server side validation anyway - and it'll be mostly the same thing. Nobody likes to do things twice!
One very important point: DO NOT check the input ONLY via Javascript. You MUST check it again server-side (using PHP, Perl, ASP or whatever other language you use for your scripts). Don't forget that people can disable JS, and they can of course use other tools to send data to your server (curl, libwww, etc.), for good (automation) or bad (malicious) reasons.
Jacques.