PDA

View Full Version : [DHTML]Configurable Date Validator



Anton
05-19-2007, 12:09 PM
1) CODE TITLE: DateValidator.js

2) AUTHOR NAME/NOTES: Anton Chertok

3) DESCRIPTION: Automatic Date Validator. Allows the user to enter a date in a variety of dd/mm/yyyy formats, configurable by the "web-master" to:

dd/mm/yyyy or mm/dd/yyyy date formats.
"Correct" dates to be zero padded or not. i.e. 01/01/2007 or 1/1/2007
Choice of default separator. e.g. 1/1/2007 or 1-1-2007 or 01012007
Auto corrects users prefered separator to the default. So if the default separator is "-", 1/1/2007 will be converted to 1-1-2007 on validation.
Allows the user to write their own error handing code, with easy access to the text field object causing the error.
Comes with a default error message, and can be used as is without any extra coding.
Automatically finds input text fields with "date" in their id, and attaches validation to them. e.g. <input type="text" id="myDateField">. Just need to include the .js and it is set up.
Automatically puts in todays date when "t" is keyed.
Uses the onblur event to validate on.

4) URL TO CODE: (this may work, but probably won't)
http://necrotic.ath.cx/proto/DateValidator.html

or, ATTACHED BELOW (see #3 in guidelines below):
Use this code to demo the js. Just put the following in a html file in the same directory as DateValidator.js, and it will all work.


<html>
<head>
<title>Date Validator Demo</title>
<script type="text/javascript" src="DateValidator.js"></script>
</head>
<body>
<h1>DateValidator.js Demo</h1><hr>
Start Date (dd/mm/yyyy): <input type="text" id="sDate" name="Start Date">
End Date (dd/mm/yyyy): <input type="text" id="e_date" name="End Date"><br>
Task: <input type="text" id="task" name="Task"><br>
<input type="button" id="dateInput" value="Some Button"><br><hr>
The two date fields are automatically picked up by the script.<br>
If you key "t" in a field, it will display todays date.<br>
Validation occurs when the field looses focus.
</body>
</html>


Add the following code to the HTML to see how user error handling works.



<script type="text/javascript">
function DateValidatorErrorHandler(oField)
{
var msg = "This is the user error handler.\nThe bad date value is " + oField.value;

alert(msg);
}
</script>



All in all, easy to use, and useful.

mwinter
05-19-2007, 01:56 PM
Allows the user to enter a date in ... dd/mm/yyyy or mm/dd/yyyy date formats.

What about the international date format?



Auto corrects users prefered separator to the default. So if the default separator is "-", 1/1/2007 will be converted to 1-1-2007 on validation.

Why?



Automatically finds input text fields with "date" in their id, and attaches validation to them. e.g. <input type="text" id="myDateField">. Just need to include the .js and it is set up.

Why the id attribute? For a form control to be successful and have its value submitted it must have a name attribute. That would seem to be the more logical option.



Automatically puts in todays date when "t" is keyed.

Automatically modifying user input isn't always such a good idea. It would be better to use a button that inserted the date when activated.



Uses the onblur event to validate on.

The blur event is unfriendly: the user cannot leave the control until the value is considered valid, a usability no-no. Instead, use the change event and validate before submission - only the latter is another option.



(this may work, but probably won't)
http://necrotic.ath.cx/proto/DateValidator.html

Doesn't here.

The code is vastly overcomplicated. The function below (documented in a past thread) will validate DMY, MDY, and YMD using full stops (.), dashes (-), and slashes (/) as separators.



function parseDate(string, preferMDY) {
var match, year, month, date, result;
preferMDY = Boolean(preferMDY);

if ((match = /^(\d{4})([.\/-])(\d{1,2})\2(\d{1,2})$/.exec(string))) {
year = Number(match[1]);
month = Number(match[3]);
date = Number(match[4]);
} else if ((match = /^(\d{1,2})([.\/-])(\d{1,2})\2(\d{4})$/.exec(string))) {
year = Number(match[4]);

if (preferMDY) {
month = Number(match[1]);
date = Number(match[3]);
} else {
month = Number(match[3]);
date = Number(match[1]);
}
if (month > 12) {
var temp = month;

month = date;
date = temp;
}
}
with ((result = new Date(year, --month, date)))
if ((month == getMonth()) && (date == getDate())) return result;
return null;
}

It could also accept omitted or additional separators with a small tweak to both regular expressions.

Now an examination of the code itself.





// CONSTANTS - may need your attention.

/* [List omitted] */


There is no need for these to be global, something which can also be said for the global functions (such as __checkDate). See my tutorial article in the Coding tips and tutorials board (http://www.dynamicdrive.com/forums/forumdisplay.php?f=25) for a way to avoid polluting the global namespace.





function $(id)
{
// a'la Prototype.js, bah, its sooo BIG!
return document.getElementById(id);
}


Identifiers with a leading dollar symbol ($) are explicitly reserved for machine-generated identifiers. Like the Prototype library, which I thoroughly disdain, you flout this and provide no feature detection or provide a fall-back.





DateValidator.prototype.load = function(field)
{
/* ... */
this.fields.push(field);
}


I assume you have no desire to support earlier versions of IE which don't implement the push method?





DateValidator.prototype.onblur = function()
{
/* ... */
// Generic error message
var fName=(this.name)?this.name:this.id;
alert("Bad date in \""+fName+"\" field.");
this.select();
}
}
}


Using the blur method in combination with changing the currently focused control can lead to a fatal situation where focus is continuously changed between invalid controls, forcing the user to close their browser. This is another reason why the change event is preferred.

Expecting the name or id attributes to contain a human-readable value isn't very realistic. Instead, use an object to lookup names:



var fieldNames = {a : 'Foo', b : 'Bar'};
/* ... */
alert('Invalid date in the "' + fieldNames[this.name] '" field.');






// This trigger actually ALLOWS the page to load before looking at
// the stuff on it. Without the setTimeout, the page is
// not "really" loaded, I think the onload event triggers just
// before the page renders, so sticking in the delay of actually
// having to look at the setTimeout function distracts the browser
// so it renders before the javascript is run.


"Sticking in the delay" is nothing but a hack.





// If you use:
// document.onload = init();
// the code will not see anything.


Of course not. The init function will be called immediately - that's what an identifier followed by parentheses (containing an optional argument list) means - and its return value - presumably undefined - will be assigned to the onload property. Moreover, the load event is not dispatched from the document object, rather it is dispatched from the global object.



Verified in IE & FireFox.

Not very well, apparently.





document.onload = setTimeout("__initDateValidator()",0);


This assigns the time-out handle to the onload property.





function __initDateValidator()
{
/* ... */

// check for an error handler...
try {
handler = DateValidatorErrorHandler;
} catch(e) { }


Exception handling? This simple statement would have done quite well:



if (typeof DateValidatorErrorHandler == 'function') handler = DateValidatorErrorHandler;






// Parouse the doc for text input nodes with "date" in id.
var inFields = document.getElementsByTagName("input");


Again, no feature detection.

The function,




function __isNumber(i)
{
// Returns true if i is a number.
var x1 = i * 1;
return (x1 == i);
}


isn't up to the task. As a result, your code will validate a date string such as '1.-1.-1.55' as correct.

Anton
06-05-2007, 01:21 PM
Very cool... May fix it this weekend =)

Lol, i make 1 javascript a year =( if that.

I suppose I should be more conscientious =)

Trinithis
06-05-2007, 06:52 PM
Questions for mwinter (or anyone else i guess...)


Identifiers with a leading dollar symbol ($) are explicitly reserved for machine-generated identifiers.
Not that I'm arguing the point, but other than the RegExp.$1 (etc), what other native JS $ vars are there? Is it "deemed" okay to use $ vars in self-created objects? What about when giving an object a method that has the computer tack on values to a (custom dollar) property?
Such as (as silly as it is):


Array.prototype.$doubleLength;
Array.prototype.setDoubleLength = function() {
this.$doubleLength = this.length*2;
}

If I'm completely missing the point, could someone try explaining it? Perhaps you have written a C++ program that creates your JS for you?


I assume you have no desire to support earlier versions of IE which don't implement the push method?
If one were to want to utilize the push method on old browsers, would this be a good way to do it?


if(!Array.prototype.push) {
Array.prototype.push = function() {
for(var i=0; i<arguments.length; ++i) {
this[this.length] = arguments[i];
}
return this.length;
}
}

mwinter
06-16-2007, 07:50 PM
Not that I'm arguing the point, but other than the RegExp.$1 (etc), what other native JS $ vars are there?

None, that I can think of, and the $n properties are deprecated. However, native variables aren't the same as machine-generated identifiers. :)



Is it "deemed" okay to use $ vars in self-created objects?

If the "self" is an automaton.

The point, as I see it, is that a program, such as server-side process, may need to generate identifiers in code and it is difficult, or may even be impossible, to foretell what identifiers will already be in use. Therefore, to help avoid clashes the automaton can prefix those names with a dollar symbol ($) in the knowledge that nothing else will be using them.



What about when giving an object a method that has the computer tack on values to a (custom dollar) property?

I wouldn't. If I were to create a property that was meant to be unique, I would do it thus:



var propertyName = '_name';
while (typeof object[propertyName] == 'undefined')
propertyName += '_name';




If one were to want to utilize the push method on old browsers, would this be a good way to do it?


if(!Array.prototype.push) {
Array.prototype.push = function() {
for(var i=0; i<arguments.length; ++i) {
this[this.length] = arguments[i];
}
return this.length;
}
}


In most cases, yes. However, the push method is designed so that it can be called against object that aren't necessarily arrays, but may nonetheless have a series of numeric properties. The problem here is that such an object won't have a "magic" length property.

The following code can be called against any object:



if (typeof Array.prototype.push != 'function') Array.prototype.push = function(e) {
var i = this.length >>> 0, j = 0, n = arguments.length;
while (j < n) this[i++] = arguments[j++];
return this.length = i;
};

The use of unsigned right-shift (>>>) emulates step 2 in the algorithm presented in 15.4.4.7, ECMA-262. The unused argument ensures that the method has a length property value of 1.