Accessible date picker calendar [beta]

Author: mike foskett updated: 16th December 2005

Based on a JavaScript calendar script by Francisco Charrua.

Created for an intranet meeting room booking system replacing an inaccessible version.

Update: I wanted to declare 31st of March as national no meetings day but apparently the rest of the world disagreed. Consequently the calendar now includes the date.

Download the calendar files [.zip 4KB]

Clicking a link calls the same page (index.php4) passing the date request via the URL.
For example: index.php4?strDate=31/10/2005.
Both the file name and the server variable are easily changed.

The JavaScript takes care of the calendar functionality while the server CMS delivers the content accordingly. This example uses no server-side scripting.

The original script was chosen because there is a PHP and an ASP version available too.

Comment, critiques, amendments etc - please email: mike.foskett@webSemantics.co.uk or post on the accessify forum.

The (X)HTML

<div class="shiftoffscreen"><a href="#skipcalendar">Skip the room booking date selection calendar</a></div> <script type="text/javascript">calendar(date);</script> <noscript> <form id="bookingdate" action="index.php4" method="get"> <!-- Note: Server-side error checking required and the action address equals the basURL variable in the JS --> <fieldset> <legend>Meeting room bookings</legend> <label for="strDate">Date [format: dd/mm/yyyy] <input type="text" id="strDate" name="strDate" /></label> <!-- Note: the input id is used in the JS too in dbSTR --> <input type="submit" value="Display room availability" /> </fieldset> </form> </noscript> <div class="shiftoffscreen"><a name="skipcalendar" id="skipcalendar"></a></div>

I'm currently unsure of the validity of placing a form in a noscript element.

Full testing, cross browser / cross platform, commences this week and will include a Jaws screen reader evaluation.

Top of page

The CSS

#calendar {width:170px; background:#def; color:#336; border:2px outset #fff; font-size:80%; border-collapse:collapse} #calendar * {padding:0; margin:0; text-align:center} #calendar tr.days th {color:#669} #calendar th, #calendar td {background:#dfeafc; border:1px solid #ccc; padding:0px} #calendar th.heading {padding:2px 0} #calendar th.weekend, #calendar td.weekend {background:#cde; color:#999} #calendar td.current {background:#fff; border:1px solid #fff} #calendar td.foot {border:0 solid} #calendar a {color:#00f; display:block; width:100%; height:100%; text-decoration:none} #calendar a:visited {color:#009} #calendar a:hover, #calendar a:focus, #calendar a:active {color:#f00; background:#fff} .shiftoffscreen {margin-left:-9000px; font-size:1px; line-height:1px; width:1px; height:1px; overflow:hidden}

All visual styling is done via the CSS.
Note the "skip link" is moved off screen.

Top of page

The JavaScript

Initialise

var baseURL="index.php4" var dbSTR="?strDate=" var today_date=new Date() var months = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December') var daysofweek = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday') // get date from the url otherwise use the current date var date=new Date() x=window.location.href x=x.replace(/%2F/g,"/") if (x.slice(x.length-19,x.length-10)==dbSTR) date = new Date(x.slice(x.length-4,x.length),x.slice(x.length-7,x.length-5)-1,x.slice(x.length-10,x.length-8))

The date of the calendar is read from the URL otherwise todays date is used. Also the replace function is used to ensure compatibility between form submission (JavaScript off) and calendar submission (JavaScript on).

The small functions

function full_date(d,m,y){ // m = 1-12 not 0-11. t_date=new Date(y,m-1,d) return (daysofweek[t_date.getDay()]+", "+t_date.getDate()+" "+months[t_date.getMonth()]+" "+t_date.getFullYear()) } // returns date: "Saturday, 3 September 2005" function addlead0(x){return ((x<10)?"0"+x:x)} function backamonth(d,m,y){ // m = 1-12 not 0-11 if ((m==3)&&(d>28)){ d=29 t_date=new Date(y,1,d) if (d!=t_date.getDate()) d-- } if (((m==12)||(m==10)||(m==7)||(m==5))&&(d==31)) d-- if (m==1) y-- m-- return (addlead0(d)+"/"+addlead0((m==0)?12:m)+"/"+y) } // returns date: "03/09/2005" function forwardamonth(d,m,y){ // m = 1-12 not 0-11 if ((m==1)&&(d>28)){ d=29 t_date= new Date(y,1,d) if (d!=t_date.getDate()) d-- } if (((m==3)||(m==5)||(m==8)||(m==10))&&(d==31)) d-- if (m==12) y++ m++ return (addlead0(d)+"/"+addlead0((m==13)?1:m)+"/"+y) } // returns date: "03/09/2005"

The "back a month" and "forward a month" functions are a little complex due to a check to insure the date remains valid.

Top of page

Initialise the calendar function

function calendar(date) { // modified version of calendar.js from http://scripts.franciscocharrua.com/calendar.php day = date.getDate() month = date.getMonth() year = date.getFullYear() this_month = new Date(year, month, 1) next_month = new Date(year, month + 1, 1)

Display the calendar table head

//Find out when this month starts and ends. first_week_day = this_month.getDay() days_in_this_month = Math.floor((next_month.getTime() - this_month.getTime()) / (1000 * 60 * 60 * 24)) if (month==2) days_in_this_month=31 calendar_html ='<table id="calendar" cellpadding="0" align="center" summary="Room booking calendar - Select date to book">' calendar_html+='<thead>' calendar_html+='<tr><td><strong><a href="'+baseURL+dbSTR+backamonth(day,month+1,year)+'" title="Previous month: '+months[(month==0)?11:month-1]+'">&laquo;</a></strong></td><th colspan="5" class="heading">'+months[month]+' '+year+'</th><td><strong><a href="'+baseURL+dbSTR+forwardamonth(day,month+1,year)+'" title="Next month: '+months[(month==11)?0:month+1]+'">&raquo;</a></strong></td></tr>' calendar_html+='<tr class="days"><th scope="col" class="weekend"><abbr title="Sunday">S</abbr></th><th scope="col"><abbr title="Monday">M</abbr></th><th scope="col"><abbr title="Tuesday">T</abbr></th><th scope="col"><abbr title="Wednesday">W</abbr></th><th scope="col"><abbr title="Thursday">T</abbr></th><th scope="col"><abbr title="Friday">F</abbr></th><th scope="col" class="weekend"><abbr title="Saturday">S</abbr></th></tr>' calendar_html+='</thead>' calendar_html+='<tbody>' calendar_html+='<tr>'

Top of page

The month of days loop

// fill leading blank days first for(week_day=0;week_day<first_week_day;week_day++){ calendar_html+=((week_day==0)||(week_day==6))?'<td class="weekend">&nbsp;</td>':'<td>&nbsp;</td>'} week_day=first_week_day mm=addlead0(next_month.getMonth()) mm=(mm==0)?12:mm // The month of days loop for(day_counter=1;day_counter<=days_in_this_month;day_counter++) { week_day%=7 if(week_day==0) calendar_html+='</tr><tr>' if ((week_day==0)||(week_day==6)) calendar_html+='<td class="weekend">'+day_counter+'</td>' else { calendar_html+='<td' if(day==day_counter) calendar_html+=' class="current"' calendar_html+='><a title="'+full_date(day_counter,mm,year)+'" href="'+baseURL+dbSTR+ addlead0(day_counter)+"/"+mm+"/"+year+'">'+day_counter+'</a></td>' } week_day++ } // fill trailing blank days for(week_day=week_day;week_day<7;week_day++){calendar_html+=((week_day==0)||(week_day==6))?'<td class="weekend">&nbsp;</td>':'<td>&nbsp;</td>'}

Display the calendar table foot

calendar_html+='</tr>' calendar_html+='</tbody>' calendar_html+='<tfoot>' calendar_html+='<tr><td colspan="7" class="foot"><a title="Today" href="'+baseURL+'">'+full_date(today_date.getDate(),today_date.getMonth()+1,today_date.getFullYear())+'</a></td></tr>' calendar_html+='</tfoot>' calendar_html+='</table>' document.write(calendar_html) //Display the calendar. }

Improvements

Unobtrusive JavaScript would be nice. Directly replacing the date form. That was beyond the original specification but if time allows...

Comment, critiques, amendments etc - please email: mike.foskett@webSemantics.co.uk or post on the accessify forum.

Top of page