Thomas’ Developer Blog

February 21, 2008

Ultimate javascript date script

Filed under: HTML, javascript — Tags: , , , , , , , — sanzon @ 6:14 am

Every had to create 3 text boxes with a month, day, and year?  It’s fairly simple, but as a user you have to notice a lot of sites being lazy and having no javascript involved to help autotab, which is a huge pain in the but at times. 

 On the otherside we have used these controls before and had go so annoyed at the auto tab we wanted to write a nasty email to the developers telling them to fix their damn script!  Well in this example… semi long example… I’ll show you have to create a really need script to help not only profect your javascript scripts, but as well go the extra mile to make sure it only benifits the users and does not take away from functionality.

 Now is it really ultimate?  No, because I did manage to leave out one function which I find not really all that needed.  Which is the ability to copy and paste dates.  It’s only 3 boxes of 2-4 characters each.  Users typically don’t bother copy and pasting dates in.  If you choose to really get into it, then feel free to look up the clipboarddata element online.  I’m sure you’ll find a lot of interesting articles. 

 Well let’s begin!

 First let’s go ahead and create some basic text inputs in HTML:

<input type="text" id="Month" maxlength="2" />
<input type="text" id="Day" maxlength="2" />
<input type="text" id="Year" maxlength="4" />

Simple right?  Of course I’m going to leave all the CSS to you guys!

Ok now let’s start be disabling those pesky letters and non-numeric values.  The first script then obviously is…

Num Only:

function num_only(e) {
var key;
if(window.event) {
key = window.event.keyCode;
}
else {
key = e.which;
}
if (key !=37 && key !=8 && key !=39) {
if (key < 48 || key > 57) {
return false;
}
}
}

The script above is fairly simple.  You first catch the event which is passed through the declaration in HTML such as:

<input type="text" onkeypress="return num_only(event)" />

You’ll notice the event declaration doesn’t work cross browser.  To fix this we simply use a statement to see if there is a window.event element.  If there is we set the keycode from there, otherwise it is set to e, which is the event attribute sent from the HTML code.

Alright, the next part is simple.  We check the which keycode’s are pressed and if it is 37,8, or 39.  These are just thins like enter, tab, backspace etc.  You can customize it how you want.  After that the next section checks to make sure the key code falls between 48-57 which are your numbers!  If they are not a number, then the if statement is true since it is outside the range and it returns false, meaning the event is cancled and nothing goes through.

Easy enough right?  Well 2 more functions to go! 

Next we’ll move onto autotabbing through the boxes.  I like to leave a lot of open options in my code in case I need them later.  So to do that we need to gather some needed information.  The event once again, the source object, where to tab to when the field is maxed out, where to go when there is nothing in the field and the user presses backspace.

Let’s start with the HTML this time.

<input type="text" id="Day" onkeyup="auto_tab(event,this,'Month','Year');" />

Ok, the first is the event, the second is the source, and then the third is where to go if the field is empty and backspace is pressed, and finally where to go when the field is maxed. 

Now let’s move onto the javascript:

 function auto_tab(e,srcObj,DOMDest,DOMPrior) {var key;
if(window.event) {
key = window.event.keyCode;
}
else {
key = e.which;
}

if (srcObj.value.length == 0 && (key == 8 || key == 37) && DOMPrior != "none") {
document.getElementById(DOMPrior).focus();
document.getElementById(DOMPrior).select();
}

if (srcObj.getAttribute && DOMDest != "none" && key != 39 && key !=37 && key !=8 && key !=9 && key !=16 && srcObj.value.length == srcObj.getAttribute("maxlength")) {
document.getElementById(DOMDest).focus();
}
}

 Well we understand how the key code event works, so let’s move onto the next part.  First we check the length of the source object.  If it is 0 and either 8 or 27 (backspace, or left arrow) we will move to the prior field.  I like using left arrow because users need the ability to just hit left or right to move around, just like they were never seperate boxes to begin with.  As well you’ll notice the part where DOMPrior cannot equal “none”.  This is so that we can simply put none if there is not previous field.  Of course this would normally be filled with either the prior date input or the prior input in general.  The select argument is there to highlight the text once it has been moved to the previous box.  Incase they want to delete it all they can just hold delete down.

In the next section we move forward to the next field.  Simply we check a lot more code… again this is to fix that pesky autotab from messing around!  39 and 37 are for left and right arrows!  Again we don’t want to press left and end back at the field once again.  Then we have 8 and 9 backspace and tab!  Don’t want to be moving forward again!  TAB is very very important!  remember people do use tab a lot and shift tab to move back and forth.  It gets annoing to do otherwise.  Also 16 is shift because we use SHIFT+TAB so we don’t want them to let go of shift and be stuck jumping again.  You’ll notice there is not highlight.  This is there because sometimes you have to fix a single text field and you don’t want to accidently erase the one that was right.  You can choose to highlight if you wish, but I prefer not to.

So that was not too difficult.  The final function though.. is one of the most hardest which is the date check.  This is there to help check to make sure the date is valid!

We’ll need all the fields for this function!

HTML:

<input type="text" id="month" onblur="date_check('Month','Day','Year','VerifyMessage');" />

You’ll notice we have a VerifyMessage field as well!  This is to help store a text string to tell if the date is valid.  I use a span to do this:

<span id="VerifyMessage" style="color:red"></span>

Now then time for the javascript function, but since it’s a tab bit long we’ll split it up a bit.

function date_check(MTH,DY,YR,DOMDest) {
var ErrorMsg = "Incorrect:"
var Now = new Date();

First part simply sets the ErrorMsg that will be displayed in the span later on to Incorrect: and then we add text to it as we move on.  Var Now is simply used as a date for checking that the year isn’t in something like 3014 or so.

After we set some basics we’ll move onto seeing long the string is in each field:

//Zero Fill Inputs--------------------------------------------------------------------------------------------
if (document.getElementById(MTH).value.length == 1) {
document.getElementById(MTH).value = "0" + document.getElementById(MTH).value;
}
if (document.getElementById(DY).value.length == 1) {
document.getElementById(DY).value = "0" + document.getElementById(DY).value;
}

if (document.getElementById(YR).value.length < 4) {
document.getElementById(YR).value = "";
}

Generally, if the field has 1 character, we add a 0 in front.  Otherwise we just let it pass.  For year this doesn’t work out quite as well…. so I simply choose to remove the entire thing is they can’t manage to put 4 digits in.  It’s to help ensure data is correct that will be stored for later use in a database or whatever you’re using it for.

 Now for the core of the code, checking the values:

//Check Values------------------------------------------------------------------------------------------------
if (document.getElementById(MTH).value > 12 || (document.getElementById(MTH).value == 0 && document.getElementById(MTH).value != "" )) {
ErrorMsg = ErrorMsg + " Month";
}

In this first part we are checking to make sure that the month is either from 1-12 and nothing higher, and not 0.

if (document.getElementById(MTH).value == "" && document.getElementById(DY).value != "") {
ErrorMsg = ErrorMsg + " Month";
}

Next we check to make sure they didn’t leave the month black and the day filled.

if (document.getElementById(DY).value == 0 && document.getElementById(DY).value != "") {
ErrorMsg = ErrorMsg + " Date";
}

Of course we also check to make sure the date is not zero.  Keep in mind you can’t juse use 0!  Since an empty input also has a value of zero, so we have to check to make sure the value isn’t “” either.

if ((document.getElementById(MTH).value == 1 || document.getElementById(MTH).value == 3 || document.getElementById(MTH).value == 5 || document.getElementById(MTH).value == 7 || document.getElementById(MTH).value == 8 || document.getElementById(MTH).value == 10 || document.getElementById(MTH).value == 12) && (document.getElementById(DY).value > 31)) {
ErrorMsg = ErrorMsg + " Date";
}

Now we check for the months with 31 days.

if ((document.getElementById(MTH).value == 4 || document.getElementById(MTH).value == 6 || document.getElementById(MTH).value == 9 || document.getElementById(MTH).value == 11) && (document.getElementById(DY).value > 30)) {
ErrorMsg = ErrorMsg + " Date";
}

Then we do the same for 30 day months.

if (document.getElementById(MTH).value == 2) {
if (Math.round(document.getElementById(YR).value/4) == document.getElementById(YR).value/4) {
if (Math.round(document.getElementById(YR).value/100) == document.getElementById(YR).value/100) {
if (Math.round(document.getElementById(YR).value/400) == document.getElementById(YR).value/400) {
if (document.getElementById(DY).value > 29) {
ErrorMsg = ErrorMsg + " Date";
}
}
else {
if (document.getElementById(DY).value > 28) {
ErrorMsg = ErrorMsg + " Date";
}
}
}
else {
if (document.getElementById(DY).value > 29) {
ErrorMsg = ErrorMsg + " Date";
}
}
}
else {
if (document.getElementById(DY).value > 28) {
ErrorMsg = ErrorMsg + " Date";
}
}
}

Ok… we have a major problem!  Leap year!  Who came up with this lame idea?… it causes more trouble then it’s worth at times.  So for Feb, we have to take the time to decode if its a leap year or not.  I’m not going to sit here and explain how to calculate leap year.  Instead if you want to read up on a good article you can find it here:

http://support.microsoft.com/kb/214019

I found this extremely useful when I was writting the code.  Plus the best part is it’s just pure math, so you don’t have to decode the language it was written in.  Really helps out.  Despite being written for excel.

  if ((document.getElementById(YR).value < 1900 || document.getElementById(YR).value > (Now.getFullYear() + 2)) && (document.getElementById(YR).value != "")) {
ErrorMsg = ErrorMsg + " Year"
}
if (ErrorMsg != "Incorrect:") {
document.getElementById(DOMDest).innerHTML = ErrorMsg;
}
else {
document.getElementById(DOMDest).innerHTML = "";
}
}

Finally we check that the year is from either 1900 or to Today + 2 years.  Which would have 2010 at the time of this blog.  Finally we check the value of the ErrorMsg and see if anything has been added.  If so we change the innerhtml of the span to show the message!  If not! We clear it! 

So that’s the basic idea of the script!  Just a warning to .Net programers!! DO NOT TRY TO DO THIS IN HTML ONLY!  You’ll need to set the ID’s based on the clientID!  Not the server ID.  So you’ll have to use an add.attributes function or w/e the function is for the lang you use.

Alright for all those who love to just copy and paste code and tear it apart and analyze it like I do, here is a modified version to work in HTML.  Sorry… but it hasn’t been fully tested!  But it should work just fine!

HTML:<input type="text" id="Month" maxlength="2" onkeypress="return num_only(event)" onkeyup="auto_tab(event,this,'Day','none');" onblur="date_check('Month','Day','Year','VerifyMessage');" /> -
<input type="text" id="Day" maxlength="2" onkeypress="return num_only(event)" onkeyup="auto_tab(event,this,'Year','Month');" onblur="date_check('Month','Day','Year','VerifyMessage');" /> -
<input type="text" id="Year" maxlength="4" onkeypress="return num_only(event)" onkeyup="auto_tab(event,this,'none','Day');" onblur="date_check('Month','Day','Year','VerifyMessage');" />
<span id="VerifyMessage" style="color:red"></span>

Javascript:

//Auto Tab---------------------------------------------------------------------------------------------------------------------
function auto_tab(e,srcObj,DOMDest,DOMPrior) {

var key;
if(window.event) {
key = window.event.keyCode;
}
else {
key = e.which;
}

if (srcObj.value.length == 0 && (key == 8 || key == 37) && DOMPrior != "none") {
document.getElementById(DOMPrior).focus();
document.getElementById(DOMPrior).select();
}

if (srcObj.getAttribute && DOMDest != "none" && key != 39 && key !=37 && key !=8 && key !=9 && key !=16 && srcObj.value.length == srcObj.getAttribute("maxlength")) {
document.getElementById(DOMDest).focus();
}
}

//Num Only---------------------------------------------------------------------------------------------------------------------
function num_only(e) {

var key;
if(window.event) {
key = window.event.keyCode;
}
else {
key = e.which;
}
if (key !=37 && key !=8 && key !=39) {
if (key < 48 || key > 57) {
return false;
}
}
}

//CheckVal---------------------------------------------------------------------------------------------------------------------
function date_check(MTH,DY,YR,DOMDest) {
var ErrorMsg = "Incorrect:"
var Now = new Date();

//Zero Fill Inputs--------------------------------------------------------------------------------------------
if (document.getElementById(MTH).value.length == 1) {
document.getElementById(MTH).value = "0" + document.getElementById(MTH).value;
}

if (document.getElementById(DY).value.length == 1) {
document.getElementById(DY).value = "0" + document.getElementById(DY).value;
}

if (document.getElementById(YR).value.length < 4) {
document.getElementById(YR).value = "";
}

//Check Values------------------------------------------------------------------------------------------------
if (document.getElementById(MTH).value > 12 || (document.getElementById(MTH).value == 0 && document.getElementById(MTH).value != "" )) {
ErrorMsg = ErrorMsg + " Month";
}

if (document.getElementById(MTH).value == "" && document.getElementById(DY).value != "") {
ErrorMsg = ErrorMsg + " Month";
}

if (document.getElementById(DY).value == 0 && document.getElementById(DY).value != "") {
ErrorMsg = ErrorMsg + " Date";
}

if ((document.getElementById(MTH).value == 1 || document.getElementById(MTH).value == 3 || document.getElementById(MTH).value == 5 || document.getElementById(MTH).value == 7 || document.getElementById(MTH).value == 8 || document.getElementById(MTH).value == 10 || document.getElementById(MTH).value == 12) && (document.getElementById(DY).value > 31)) {
ErrorMsg = ErrorMsg + " Date";
}

if ((document.getElementById(MTH).value == 4 || document.getElementById(MTH).value == 6 || document.getElementById(MTH).value == 9 || document.getElementById(MTH).value == 11) && (document.getElementById(DY).value > 30)) {
ErrorMsg = ErrorMsg + " Date";
}

if (document.getElementById(MTH).value == 2) {
if (Math.round(document.getElementById(YR).value/4) == document.getElementById(YR).value/4) {
if (Math.round(document.getElementById(YR).value/100) == document.getElementById(YR).value/100) {
if (Math.round(document.getElementById(YR).value/400) == document.getElementById(YR).value/400) {
if (document.getElementById(DY).value > 29) {
ErrorMsg = ErrorMsg + " Date";
}
}
else {
if (document.getElementById(DY).value > 28) {
ErrorMsg = ErrorMsg + " Date";
}
}
}
else {
if (document.getElementById(DY).value > 29) {
ErrorMsg = ErrorMsg + " Date";
}
}
}
else {
if (document.getElementById(DY).value > 28) {
ErrorMsg = ErrorMsg + " Date";
}
}
}

if ((document.getElementById(YR).value < 1907 || document.getElementById(YR).value > (Now.getFullYear() + 2)) && (document.getElementById(YR).value != "")) {
ErrorMsg = ErrorMsg + " Year"
}

if (ErrorMsg != "Incorrect:") {
document.getElementById(DOMDest).innerHTML = ErrorMsg;
}
else {
document.getElementById(DOMDest).innerHTML = "";
}
}

Well hope that all helps!  Happy coding!

Advertisements

1 Comment »

  1. Thanks useful bit of javascript, can i freely use it on my sites? obviously will credit you.

    Comment by Mr Open Port :) — August 15, 2008 @ 2:31 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: