Any online form that requires a scheduling, appointment or a booking function usually needs some kind of date picker. Traditionally this has been covered by JQuery or another JavaScript framework, but HTML5 features its own native date
input, with good and growing browser support… with the unfortunate exception of Firefox and Safari, at least right now.
The basic structure of the input follows the same accessible pattern I’ve shown in the past:
<label for="bookdate">Booking Date</label>
<input type="date" id="bookdate" placeholder="2014-09-08">
In your browser, the result will look something like this:
Selecting the date that this article was originally published would report a value of 2014-09-09 to JavaScript and/or your backend language of choice. A default value for the input can be set with the value
attribute. Non-supportive browsers will simply render the input as a text field, which is the reason I insert placeholder
text: it provides a guide for entering dates in the correct format in browsers such as Firefox.
Restricting The Date
In most cases, there’s little point in giving users the ability to select dates in the past, but the date input
will allow a complete date range to be selected by default. We can restrict this with min
and max
attributes:
<input type="date" id="bookdate" value="2014-09-09" min="2014-09-09" max="2014-09-18">
The result:
The obvious problem is that unless we’re creating a form for an event with predefined dates, the min
, max
and value
will be constantly changing: in many cases min
will be the current date, with max
set to however far the site owner wants to allow bookings in the future.
It’s possible to set the values using PHP (or another server-side language) or JavaScript.
Using PHP to Restrict The Date Input
We can use PHP to reflect both the current date and the future date within the input, acknowledging that by default the dates will reflect the time on the server, rather than the client’s computer:
<input type="date" id="bookdate" min="<?=date('Y-m-d')>" value="<?=date('Y-m-d')>" max="<?=date('Y-m-d', strtotime("+7 day", time()))>" value="<?=date('Y-m-d')>" >
This will set the max
value to a week in the future from the current date.
Using JavaScript to Restrict The Date Input
We could also use JavaScript to do the same thing:
function convertToISO(timebit) {
timebit.setHours(0, -timebit.getTimezoneOffset(), 0, 0);
// remove GMT offset
var isodate = timebit.toISOString().slice(0,10);
// format convert and take first 10 characters of result
return isodate;
}
var bookdate = document.getElementById('bookdate'),
currentdate = new Date();
bookdate.min = convertToISO(currentdate);
bookdate.placeholder = bookdate.min;
var futuredate = new Date();
futuredate.setDate(futuredate.getDate() + 7);
// go forward 7 days into the future
bookdate.max = convertToISO(futuredate);
Obviously there are more complex variations of both methods: for example, you might want to set min
and
placeholder
to the next day if the local time is after 5pm, forcing the user to book the next day after closing time.
Restricting Date Selection with step()
Dates can be further restricted by using a step
attribute: setting it to a value of 2 for the input would allow the user to only select every other day, while step="7"
would restrict choices to only a certain day each week.
Restricting Date Selection With A Datalist
You can further restrict the available dates by using a <datalist>
with a series of options: the associated date (in yyyy-mm-dd format) becomes the content of each <option>
, while optional label
attributes provide more information:
<label for="vacations">Statutory Canadian Holidays</label>
<input type="date" list="vacation-days" id="vacations">
<datalist id="vacation-days">
<option label="Thanksgiving">2014-10-13</option>
<option label="Remembrance Day">2014-11-11</option>
<option label="Christmas Day">2014-12-25</option>
<option label="Boxing Day">2014-12-26</option>
</datalist>
Which produces:
Restricting Date Selection With The Constraint API
Unfortunately, there’s no way (yet) to restrict (nor, as we will see, to recolor) certain days in the date
input. For example, it would be great if we could always lock off Sundays from the date picker, so that users could never choose them for booking.
While I expect / hope that to be possible in future iterations of the specification, perhaps via a negation list, for right now we have to fall back to using the Constraint API, at least client-side. (Typically, you would add more logic server-side to double-check the entered data).
Taking the example I just used – no bookings on Sunday – we could create the following (with a nod to Tiffany B. Brown for the concept). The markup:
<label for='massage'>Please choose your appointment day (Sundays excepted)</label>
<input type="date" id="massage">
The JavaScript:
var date = document.getElementById('massage'),
errorMessage = date.title;
function noSundays(e){
var day = new Date(e.target.value).getUTCDay();
// Days in JS range from 0-6 where 0 is Sunday and 6 is Saturday
if ( day == 0 ){
date.title = '';
e.target.setCustomValidity(errorMessage);
} else {
e.target.setCustomValidity('');
}}
date.addEventListener('input',noSundays);
This uses the established (but little-known) ability for HTML5 form element title
attributes to contain error messages. Using a similar system, you could test against an array of dates to determine that users don’t place bookings on Christmas Day, etc.
Restrictions On Styling
There are even greater limitations in trying to style the date input, due to the fact that, like the color
input, the date picker UI is pulled from the operating system, rather than being natively rendered in the browser itself. If you think about it, this is a good thing: the date picker in iOS8 looks very different from a desktop browser, and should be: mobile devices have very different UI needs.
Chrome does allow the customization of the numerical date picker field using Shadow DOM pseudo-selectors:
input[type=date] {
border: none;
width: 18rem;
background: black;
color: #fff;
padding: 1rem;
font-size: 1.4rem;
}
::-webkit-datetime-edit {
font-size: 1.4rem;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 1rem;
}
::-webkit-datetime-edit-text {
color: red;
padding: 0 0.5em;
}
::-webkit-datetime-edit-month-field {
color: blue;
}
::-webkit-datetime-edit-day-field {
color: green;
}
::-webkit-datetime-edit-year-field[aria-valuetext=blank] {
color: white;
}
::-webkit-datetime-edit-year-field {
color: purple;
}
::-webkit-inner-spin-button {
display: none;
}
::-webkit-clear-button {
display: none;
-webkit-appearance: none;
}
::-webkit-calendar-picker-indicator {
background: none;
color: red;
font-size: 2rem;
margin-right: 1rem;
transition: .4s;
}
::-webkit-calendar-picker-indicator:hover {
color: yellow;
}
Which creates the result of:
Note that we set up the default style for all browsers on the basic attribute selector so that all browsers take the roughly correct appearance, then specialise for Webkit. ::-webkit-datetime-edit
are the numerical values, with the wrapper immediately around them. Note that you can also set the color of numbers yet to be edited by selecting based on an ARIA attribute.
If you are bound and determined to customize absolutely everything about the appearance of the date picker, I would suggest going back to JQueryUI or another framework, discussed below.
Polyfills
Because of the complexity of the code underneath, crafting a polyfill can be difficult, often requiring a combination of frameworks and plugins. I like Chris Coyier’s approach using Yepnope, Modernizer and JQuery UI; other possibilities include Jon Stipe’s scripts.
Variations
There are many closely-related variants of the date
input, including inputs specialized for month
, week
, and datetime-local
. All have varying degrees of support, which I will cover in future articles.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.