Styling a native date input into a custom, no-library datepicker
With these in mind I started experimenting and browsing StackOverflow.
Default appearance of
MDN offers an incredibly helpful wiki page around , and it includes screenshots of the datepicker on various browsers. I added a couple of mine to the bottom of the list.
One thing I found out is that in Safari (and in IE, I hear) a date input acts like a regular text input, and has no datepicker.
On Chrome (MacOS):
On Safari (MacOS):
None! No datepicker for Safari.
As can be seen in the above screenshots, the default datepicker is a text input where the date is displayed in a locale-specific format. Either clicking on the input, or on a calendar-icon inside it, opens a datepicked popup. This is similar to how datepicker JS libraries work.
To highlight the different components:
Whereas JS libraries allow for customization of styles and functionality, in the case of you don’t really get that. There’s no toggle to enable week numbers or year dropdowns; the browser will either render such things, or it won’t.
Default functionality of
Another concern was if the input value, when read via JavaScript, would be consistent between browsers.
In the case of Helppo I always wanted it in the same format (YYYY-MM-DD), but obviously it wouldn’t really matter what the format was as long as it could be consistently parsed.
Luckily this is not a problem! As per the MDN page, reading input.value should result in a consistent format:
const dateInput = document.querySelector('.date-input'); console.log(dateInput.value); // "2020-10-26"
This is regardless of the fact that the visible format is locale-based in the input.
To be sure, I tested and verified it in various browsers on MacOS.
Based on this result, we can confidently listen to onchange event on a and get the same date format back no matter the platform.
Customizing the input
First of all, if your application needs a datepicker, I would strongly recommend just using as seen above! There really should be a specific reason for including a datepicker library when the out-of-the-box solution works so well.
As noted in the beginning, however, for Helppo I had the requirement that the datepicker should be an extra button next to a regular text input. In this fashion:
So I needed to transform into a button-like element.
- Some browsers (Firefox, Edge) don’t have a toggle, and just open the popup when you click the input
- Some browsers (Chrome) open the popup when you click a toggle-button
- Some browsers (Safari, IE) don’t open a popup at all
For point #1, we should be able to just make a date input invisible and stretch it on top of the toggle we want to display. That way, when the user clicks the toggle, they actually click the date input which triggers the popup.
For point #2, we should try to do the same but for the toggle-part of the date input. I started searching and landed on this answer in StackOverflow, which does that for webkit browsers.
For point #3, we should not show the toggle at all because clicking on it won’t do anything. Ideally this would be done without user-agent sniffing or any other method of hard-coded browser detection.
Making it happen (the markup)
Here’s how we’ll structure the toggle-button in the HTML:
class="datepicker-toggle"> class="datepicker-toggle-button"> type="date" class="datepicker-input">
Note on accessibility: because we’re using just spans with no text content, the only actionable element in this piece of HTML is the input. The focus flow of the document is the same as if there just was a regular input here. If you use different kind of elements (e.g. an ), make sure to ensure your element stays accessible.
And here are the base styles of the wrapper element and the visual button we want to show:
.datepicker-toggle display: inline-block; position: relative; width: 18px; height: 19px; > .datepicker-toggle-button position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-image: url('data:image/svg+xml;base64. '); >
Note on .datepicker-toggle-button : this is the element you would style entirely based on your application. In this case I’m just using a background-image for simplicity.
At this point we just have the date input visible next to the button-element, as it resides in the DOM:
Here begins the browser-specific CSS for stretching the actionable part of the input over the visible button.
Based on the previously mentioned SO answer:
.datepicker-input position: absolute; left: 0; top: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; box-sizing: border-box; > .datepicker-input::-webkit-calendar-picker-indicator position: absolute; left: 0; top: 0; width: 100%; height: 100%; margin: 0; padding: 0; cursor: pointer; >
Works for Chrome (MacOS), which normally has the toggle:
Works for Firefox (MacOS), which doesn’t have a toggle:
I also asked a friend to verify on Firefox on Windows, and they sent me a similar screenshot to the above.
I wasn’t able to verify it on Edge, but based on the fact that on Edge clicking the input triggers the popup, it should work similarly to Firefox because we stretch the invisible input over the whole button.
Finally, I tested it also on iOS Safari, and the datepicker opened as expected:
Detecting browsers that don’t have a datepicker popup
As mentioned above, some browsers (Safari, IE) don’t have the datepicker functionality built-in. Instead a date input acts like a regular text input. For these browsers we shouldn’t show the button at all, because it would just sit there without doing anything.
I looked into detecting the browser support automatically. Some ideas I researched:
- Detecting the existence of specific shadow DOM. See the below screenshot. It would’ve been great if we could check the existence of datepicker-specific elements (e.g. input.shadowRoot.querySelector(«[pseudo=’-webkit-calendar-picker-indicator’]») ), but unfortunately that’s not possible in the case of an input element ( shadowRoot is null ; read more at MDN). I’m not aware of any workaround for this.
- Reading document.styleSheets to figure out if our pseudo-element CSS selector has been successfully applied. Now, this was a wild idea based on no prior code I’d ever seen, and of course it yielded no results. CSSStyleSheet does not contain any information about how the style was applied or if it was valid.
- Continued on the previous point… how about trying to read the styles of the pseudo element, and noticing differences in the results based on the browser? I added an obscure enough CSS style for the pseudo-element: .datepicker-input::-webkit-calendar-picker-indicator < font-variant-caps: titling-caps; >and then tried to read it: window.getComputedStyle($0, ‘::-webkit-calendar-picker-indicator’).fontVariantCaps (also with variations of and the lack of colons). Unfortunately this would always return the style value of the input element, not the pseudo element. This is probably because, again, we can’t access the shadow DOM. The above works for e.g. :before pseudo-elements, but doesn’t seem to work for our use case.
- Checking if value is automatically sanitized by the browser. This turned out to be the winner. In hindsight I should’ve checked this one first, but it seemed obvious to me that this would not work, because I assumed that a date input would still have formatting/sanitization. Turns out that’s not the case.
So in the end checking for datepicker support is as simple as setting an input value and checking if the browser accepted it:
const input = document.createElement('input'); input.type = 'date'; input.value = 'invalid date value'; const isSupported = input.value !== 'invalid date value';
I verified that this works in Safari.
Listening for onchange event
All that’s left is to update the value of a text input when the user chooses a date from the popup. That is very straight-forward:
const textInput = document.querySelector('.text-input'); const dateInput = document.querySelector('.datepicker-input'); dateInput.addEventListener('change', event => textInput.value = event.target.value; // Reset the value so the picker always // opens in a fresh state regardless of // what was last picked event.target.value = ''; >);
An alternative to resetting the date input value is to keep it in sync with the text input, which gets a bit more complicated but is still quite simple:
dateInput.addEventListener('change', event => textInput.value = event.target.value; >); textInput.addEventListener('input', event => const value = textInput.value.trim(); dateInput.value = value.match(/^\d -\d -\d $/) ? value : ''; >);
GitHub repository
I’ve packaged this solution into a reusable mini-library here:
Also published on npm as native-datepicker :
The repository contains examples of how to use the code in both a React codebase as well as in plain vanilla JavaScript. A glimpse of the API:
const picker = new NativeDatepicker( onChange: value => // . >, >); someElement.appendNode(picker.element);
Included is also a ready-made React-component:
NativeDatepicker className="custom-class" value=value> onChange=value => // . >> />
It is also aware of datetime inputs (i.e. if the text input contains a value like «2020-10-26 23:35:00», the library will replace just the date-portion of it upon change).
It would be cool if you found use for the package. If there’s any issues, please file an issue in the GitHub repository!
Thanks
Thank you also to MDN and the StackOverflow commenters for the building blocks of this exploration.
If you haven’t already, feel free to check out Helppo, for which I originally built this component. Helppo is a CLI-tool (written in NodeJS) which allows you to spin up an admin interface for your database. Just run npx helppo-cli —help to get started.
Custom Input Date | CSS Tutorial
Hey everyone. Welcome to today’s tutorial. In this tutorial, we will learn how to create a custom input date field. To build this custom date input, we need HTML and CSS. We do not make use of any external libraries, images or javascript.
If you are looking for javascript projects to improve your javascript skills, you can check out this playlist here. This playlist consists of 100+ tutorials along with the source. The difficulty of these projects varies from simple to quite complex ones. Hence this playlist is suitable for everyone including javascript beginners to javascript experts.
Video Tutorial:
If you are interested to learn by watching a video tutorial rather than reading this blog post check out the video down below. Also, subscribe to my youtube channel where I post new and exciting tutorials every alternate day.
Project Folder Structure:
Before we start coding let us take a look at the project folder structure. We create a project folder called – ‘Styling Input Date’. Inside this folder, we have two files. The first file is index.html which is the HTML document. And the second file is style.css which is the stylesheet.
HTML:
We begin with the HTML code. First, copy the code below and paste it into your HTML document. Our HTML document consists of just the input tag of type ‘date’.
CSS:
We style this date input with CSS. For this copy, the code provided to you below and paste it into your stylesheet. We make use of the ‘::-webkit-calendar-picker-indicator
‘ pseudo-element to style the input field.
*, *:before, *:after < padding: 0; margin: 0; box-sizing: border-box; >input[type=»date»] < background-color: #0080ff; padding: 15px; position: absolute; transform: translate(-50%,-50%); top: 50%; left: 50%; font-family: "Roboto Mono",monospace; color: #ffffff; font-size: 18px; border: none; outline: none; border-radius: 5px; >::-webkit-calendar-picker-indicator
That’s it for this tutorial. Our input field styling is now ready. You can now go ahead and customize it the way you want by changing font size, font family, colours, border radius and much more.
If you face any issues while creating this project you can download the source code by clicking on the ‘Download Code’ button below. A zip file will download automatically. All you have to do is extract the files inside and you are good to go.
Also, I would love to hear from you guys. So if you have any queries, suggestions or feedback comment below.
Happy Coding!