See the Demo.
Update: I built and tested this on Chrome using my macbook’s trackpad. It scrolls very smoothly, however I’ve heard reports that a scroll wheel jumps from month to month, but can cut off dates half-way. Needs to be updated for scroll wheels. Also touch events for mobile.
I haven’t been on Dribbble in awhile, but last night I was poking around and found a bit of inspiration. A little calendar widget design by Dribbble user Cat Noone caught my eye, but what really sparked my interest was the fisrt comment below it:
Really love this widget! Quick question how do you change the month? Or is the intention that you can only pick a day during a specific month?
I started thinking how the calendar might work if you used scrolling to change the month. From Cat’s design it seems there is a window of delivery, so she may not need to change months. But most apps I’ve used do.
Several hours later I ended up with my own Scrolling Calendar widget. It is just a prototype and could use work to make it more robust, but it will give you a feel for the scrolling interaction.
Building the Scrolling Calendar
I started building the calendar by setting up the HTML I thought I would use and styling it (borrowing from Cat’s color scheme). The initial HTML looked something like:
<!-- etc. -->
Many calendar components use tables to display, with each week being a row in the table. The dates which are not part of the current month but show because they are on the current week, they get a “disabled” look.
I wanted my scrolling calendar to show the current month’s dates in the highlighted blue color, and the other dates with the disabled look, but I didn’t feel like iterating over each date cell to add and remove classes was my favorite method. Couldn’t I use CSS to toggle whole months at a time instead? (It seems like Cat’s company, Liberio, actually has specific date ranges, in which case doing it by day makes the most sense.)
So I threw out the table. That always gives me a good feeling when I get rid of a table—I don’t know why. And instead of using spans and divs, I thought I’d get all HTML5ey and use custom elements. So
<date> were created.
This screenshot didn’t include it, but inside
<week> would be
<day>Mo</day>, etc. The day and date elements became inline-blocks with a set width and height, and their containers got a set width so they would automatically wrap every 7th item.
Once I had the HTML and styles I wanted, I started to code it. I pulled in the trusted moment.js library to populate the dates, and since I was using a date library I auto-generated the weekdays with Moment as well (that’s why the screenshot above is missing the weekdays). The calendar will support multiple locales now!
When I was using the table-based HTML my thought was as the user scrolled up or down, rows would be added at the top or bottom of the table as needed. Now I would be creating months at a time. So the initial population of the calendar contains 3+ months. The current month, the previous month, and the next month. The “+” comes in because I need the “filler” dates from the month before the previous (e.g. January 2015 would show 28, 29, 30, and 31 from December). I can’t just leave these out because I am no longer using a row-based layout, so they are needed to make sure the dates are in the correct place.
At first I tried the “scroll” event, but it was the “wheel” event that I needed. You can’t get the wheel event’s delta change, and you can prevent it’s default action. When the user scrolls over the calendar, I use the delta to know where the screen should be, add a month to the front or back of the calendar if needed, and set the current month if needed. Scrolling up and down will change months very quickly. Try it out.
I played around with the “break point” in the middle of the calendar to determine when to switch the active month. It turned out that directly in the middle wasn’t quite right. It always looked like the break point was too low, because of the amount of visible text above and below. Also, the breakpoint couldn’t be a line, but a margin, otherwise you could get jitter when scrolling slowly between two months.
The final touch was the date selection. Handling the click on a date wasn’t difficult, but I ran into a usability issue when clicking on one of those “disabled” dates. I wanted to allow it so you didn’t have to scroll all the way when you saw the date you needed, but I also didn’t want the top of the calendar showing “January 2015” when you just clicked on the December 28, 2014 date.
To fix this, I selected the month for the date when you click on it. This highlights those dates in blue and shows the correct month in the title bar. You can still scroll after that, but when you click you get confirmation of the date you selected.
Because I’m using moment.js with locales the calendar should work correctly for you, with Sunday being the first day of the week in the US and Monday in many other countries.
Things to Improve
This isn’t ready for prime time as it is just a proof of concept. If you wanted to go the distance you would want to add < and > buttons on the month line. Just because a user can scroll through the months, doesn’t mean that they will know they can. You’ll have to give them the training wheels and let them discover the scroll feature for themselves.
Currently I only create one month in front or back when scrolling past the break point. Using my track pad it is pretty smooth until I try to flick it very fast. It halts the flow. In my prototype you can still get years down the road very quickly, so I didn’t fix this, but to make it a smooth experience you would want to use the delta to add/remove several months when needed instead of just the one at a time.
You could add other features, such as selecting a range of dates, adding time selectors like Cat’s original design, providing maximum and minimum dates available, giving some sort of visual indication that scrolling is possible (without an ugly scrollbar hopefully), and packing it up for reuse of course.
What do you think, would this be useful and intuitive?