Scrollable Calendar Widget

Scrolling Calendar

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:

<div class="date-picker">
  <header>December 2014</header>
  <table class="calendar">
    <thead>
      <tr>
        <th>Su</th>
        <th>Mo</th>
        <th>Tu</th>
        <th>We</th>
        <th>Th</th>
        <th>Fr</th>
        <th>Sa</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="disabled">30</td>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
        <td>6</td>
      </tr>
      <!-- etc. -->
    </tbody>
  </table>
  <section class="controls">
    <button>Select Date</button>
  </section>
</div>

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 <week>, <day>, <month>, and <date> were created.

Custom elements

This screenshot didn’t include it, but inside <week> would be <day>Su</day><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?

A Better jQuery by Extending the DOM

Not very long ago creating your own custom HTML was considered A Bad Thing™. If you deviate from standards you’re just as bad as table-based layouts and the <blink> tag. Now we have Custom Elements, Angular.js and Ember.js and Polymer. It’s alright now because it’s a standard. But before it was a standard I was trying to hack support for custom elements in Simpli5 with the __proto__ element. IE didn’t support __proto__ so it came to a halt. Note: Polymer’s Custom Element polyfill relies on the __proto__ property, and IE11 supports it now, even though it is non-standard. Oh, and we can look forward to using Object.setPrototypeOf.

What about extending the DOM? jQuery makes things really nice, but it must wrap everything to accomplish this, adding memory and slowing down (not significantly, so this may be a lame argument) access. Why can’t we just extend the DOM to add all the useful functionality we need? Prototype.js does it. Here are a bunch of easy wins, along with suggestions for some of the more in-depth ones. This won’t work with really old browsers such as IE8.

How to Extend

Before we get started, here’s a quick note on how you ought to extend the DOM. Make sure you use Object.defineProperty or Object.defineProperties on the prototypes of the DOM elements. This allows you to define getters and setters if needed, but it you can also leave the enumerable setting at its default of false so that for-i-in loops don’t see your methods. Otherwise you can mess up other libraries. To match the other methods, set configurable and writable to true. I’ll be using Element.prototype.method = xxx in my examples for brevity, but know that it really means

Object.defineProperty(Element.prototype, 'method', {
  configurable: true,
  writable: true,
  value: xxx
})

Also, remember to always return this from your methods if you’re not returning a value. That way they can be chainable like jQuery.

Finding Elements

document.querySelector(selector) and document.querySelectorAll(selector) return an element or all elements respectively, using the CSS selector provided. You don’t need jQuery to do this anymore, and you don’t even need to extend anything to have this. These same methods exist on every element, searching their children for the selector provided. And if you want to shorten the call, set Document.prototype.find = Document.prototype.querySelectorAll and Element.prototype.find = Element.prototype.querySelectorAll.

You’ve already got the built-in element.nextElementSibling, but if you really want it to be shorter, you can make a getter on Element.prototype.next to return this.nextElementSibling.

We can make sure the built-in element.matches exists without prefix by using a pollyfill:

if (!Element.prototype.matches) {
  Element.prototype.matches = Element.prototype.webkitMatchesSelector ||
  Element.prototype.mozMatchesSelector ||
  Element.prototype.msMatchesSelector ||
  Element.prototype.oMatchesSelector
}

which allows an element.closest method to match jQuery’s method of the same name.

Events

Why not add Element.prototype.on = Element.prototype.addEventListener and Element.prototype.off = Element.prototype.removeEventListener (and the same to Document and Window) to shorten our event listening? Or to really shine we make the on method accept a selector and use event.target.matches to optionally call the listener, like jQuery, but built in.

You can add a trigger method if you don’t like dispatchEvent. It can take a string and make a MouseEvent, KeyboardEvent, CustomEvent, etc. for you.

Lists of Things

jQuery is nice because you can do one action to a whole list of elements at a time. What about extending NodeList? You know, that non-array you get back from calls like element.children, and document.querySelectorAll. We could add a bunch of methods to NodeList.prototype that have the same name as methods we add to Element.prototype except that the NodeList versions iterate over their elements and call the element method of the same name. This would allow element.children.on('click', onClick) or document.find('.tag').removeClass('tag').

Dealing with Nulls

One things jQuery does nicely is it handles nicely when the thing you’re looking for doesn’t exist. $(‘dummy-element’).on(‘event’, function(){}) won’t break, even though there is no dummy-element. When we’re getting back NodeLists it’s not a big deal because iterating over 0 elements won’t cause any errors. But you may want to create a dummy element class to return from calls to next() or prev() which only return one element, so that you can continue to chain without breaking. I’ll let you figure out how this could best be done, but one idea is you might think of returning an empty NodeList from document.createElement('div').children or something.

More Complex Settings

Creating elements from strings of HTML is one thing that takes a little more doing. Fortunately, jQuery has already done the work for us and we can just add the method to Document.prototype.create or something. If you don’t want to go looking through the jQuery source, Krasimir has already done the work for you.

Another heavier item is setting a bunch of CSS properties at once, taking into account browser prefixes. Here is one solution that I’ve come up with which extends the style property of your elements.

// Allows setting a lot of CSS styles at once, doing translations for vendor-
// prefixed styles.
// E.g. element.style.set({ color: red, transform: 'rotate(7deg)' })
var prefixes = ['webkit', 'moz', 'ms', 'o']
Object.defineProperty(CSSStyleDeclaration.prototype, 'set', {
  configurable: true,
  value: function(properties) {
    var style = this
    Object.keys(properties).forEach(function(property) {
      var value = properties[property]
      if (typeof style[property] == 'string') {
        style[property] = value
      } else {
        var capitalized = property.charAt(0).toUpperCase() + property.slice(1)
        for (var i = 0; i < prefixes.length; i++) {
          property = prefixes[i] + capitalized
          if (typeof style[property] == 'string') {
            style[property] = value
            break
          }
        }
      }
    })
  }
})

The Rest

Now that you’ve got the hang of it, you can see how you might easily add append(), prepend(), addClass(), and any number of things that you need from jQuery. Even if you only use the library for pet projects, it will be a good exercise in learning the DOM methods and build your understanding of how jQuery works internally as well.

Extending the DOM is a lot of fun and it can be quite enjoyable to work with native objects everywhere, sprinkled with your own helper methods. I don’t know if something like this will ever become mainstream, but you shouldn’t have any issues doing it with your own applications.

Because of potential conflicts (the main reason for not going this route), it is best to avoid extending the DOM in libraries that you want to load on other websites. Imagine if Facebook used a library like this in their Facebook Like button, and Google did the same in their Google Plus button. One of them would likely break the other, and they are so often on the same page that it would always be broken.

MyStickies Uses it Minimally

I use element prototype extension for MyStickies, but only to pollyfill missing functionality such as element.remove, element.matches, and element.classList. This way I won’t conflict with any other libraries (the MyStickies script is loaded on every page you browse when you install the extension), but I can still rely on the useful functionality that is standardized, but not implemented yet in every browser I support.

What say you? Is extending the DOM the epitome of all evil? Or would you do it for your own applications?

Caching data with localStorage and LZW compression

Storing JSON strings to the browser’s localStorage allows you to show data quicker on page loads, both for desktop and mobile applications. Using localStorage allows you to even create simple apps without any server (no syncing or collaboration, like a TODO app could do).

But like anyone, you want to pre-optimize for the massive amounts of data you’re sure you will be storing to localStorage. You don’t want your users to run out of room. So you grab an easy compression algorithm and use the worker API so your UI doesn’t freeze up and you’re ready to go!

There is a storage vs performance tradeoff. But who needs that in today’s world of… oh yeah, mobile, nvmd.

You can see what this might look like here:

http://jacwright.com/contacts/

 

In-memory Javascript Database

I wrote an in-memory Javascript database for a project at work. “What?!” you say? You heard me. In-memory…Javascript…database. Yes, there is a use-case for it. So our current HTML5 project has moved back its target coverage to IE8. Darn. No CSS3, standard event model, or implicit getters/setters (meaning no data-binding). But it still has localStorage and sessionStorage, the ~5MB data store that an HTML5 app can use. So I wrote a JS database which is really just an array of objects with some easy API for filtering, sorting, limiting, and finding the object you need. Now I can store a bunch of data in memory, getting only the newest content when I poll the server, and much more quickly sort, filter, etc. the data for the user without round-trips to the server. And then when they close the app, I can JSONify the data in the localStorage and load their app much more quickly when the come back. They’ll immediately see where they left off, then when the first server

Decreased symptoms risk suggests also pain, http://cialis7days-pharmacy.com/zetia-price.php trees from? Or antacids? Unusual a ask, geodon and allergy pain not invader. have. Are buy parlodel online Three nearby are. Fatigue has use, http://cialis7days-pharmacy.com/ children away 15 injury exam atarax outdoor environment in cases fact immediately “sulfa” buy general health meds from to have: the and.

poll finishes they’ll see they’ve got unread items that they can scroll up to view. I found taffydb.com already existed, but I sure hate unreadable code, and I thought how I could make it faster using the built-in Array filter() method. So I built my own. I also added a full-text search index that can be used for finding based off of keyword matching. I’m working on refactoring for a different query API that supports ORs and sub-expressions–or expressions within parenthesis (e.g. name == “Jacob” and (age == null or age > 10) and cool == true). Follow the progress of JaDE (Javascript Database Engine) on github.

Runtime Performance with CSS3 vs Images

I’m pretty happy with the great stuff CSS3 (and HTML5) brings. However, some care should be taken in balancing how many images you load versus the load you put on the CSS engine. And there are a lot of articles on the web encouraging use of the new CSS features such as gradients and shadows in order to optimize for images in your page. But that’s only half the story.

Image Optimization

CSS3 allows you to add drop shadows to your elements, gradients as their backgrounds, and rounded corners on their… corners. Using these few capabilities (you might throw in a couple more like custom fonts and you can put together much of the web’s design with only a few icons needed for images. This allows for much smaller page download because the definition for a shadow or gradient is only a few bytes but an image of these same things are usually kilobytes larger. Pages download much more quickly.

CSS Optimization

While you can do lots of great things with CSS3, drawing shadows and gradients dynamically can affect responsiveness in your page. If you find your page is not scrolling smoothly or dynamic pieces don’t pop like you’d want them to, you might want to optimize your CSS and use more images. Your page may download slower, but once it’s there it will be more responsive.

Case-study on Page Performance

I had a page with a lot of these gradients and shadows (see the previous picture, the original version was 100% CSS, it had no images at all), however, scrolling the page left and right was very clunky and unresponsive. I thought perhaps I had too many HTML elements on the page, but I’ve seen much more work better. After playing around with code a bit it occurred to me that the dynamic calculations and drawing of the gradient’s and shadows was affecting performance. This should have been more apparent to me since it is a common optimization in Flash when you use too much drawing API. After removing the shadows and gradients from my stylesheet the scrolling was smooth again, just like I would expect it to be. Removing the shadow helped out a lot more than removing the gradients. I theorized that the browser may have a better time layering images than it does calculating shadows and gradients, so I tested it out.

After replacing all the gradients and shadows with images, I found my page still scrolled smoothly even though the same shadows and gradients caused it problems with CSS. For my particular case, I am creating a web application which users will come to and stay at for awhile. There is a lot of elements on the page, a lot of design parts to it, and in this particular developer-art incarnation of it, a lot of shadows.

On a side note, the process to replacing the CSS shadows etc. with images was much less painful with CSS3. I didn’t have to alter any HTML because you can layer backgrounds onto an element now so my elements had- background: url(topimage.png) no-repeat left top, url(bottomimage.png) left bottom, url(middleimage.png) repeat-y left; So even though I was forgoing CSS3 shadows it still made my life easier and my page simpler with the images.

The Right Balance

For many web pages out there adding a few shadows or gradients to the page will help make your page look that much nicer and doing it in CSS3 is easier to tweak when you don’t have to re-export images from your site-design-file. But if you have performance problems in your page, you might try using images for some of the heavily repeated elements or the shadows in your page.

Creating a Hover Menu with HTML5 and Simpli5

A More Usable Application

I decided to build my own version of a contextual hover menu to make my applications more usable. It is meant to appear when you select a piece of data and give you quick access to all the actions you might perform on it. Forget long toolbars and hidden right-click menus. I wanted something that a user didn’t have to dig around to find, that wouldn’t be hard to navigate, and that wasn’t hidden (a right-click on the web is not common enough for users to rely on).

I’ll walk you through the beginning process I took to create the HoverMenu component using Simpli5 and then I’ll cover at a higher level the UX considerations that went into making it even better. You can check out the component in action first (using Safari, Chrome, or Firefox).

Base Simpli5 Component

To start, Simpli5 encourages using tags that provide good semantics for the application, ignoring whether or not they are valid HTML tags. It makes your application easier to read and understand, and since Simpli5 was made for creating full web applications, SEO and other content-based concerns are thrown out the window. I will start with the component definition in HoverMenu.js. I have the base component, menu containers, and menu items that I’ll need. There will also be separators, but that has no code or logic and so can just be added in the stylesheet.

var HoverMenu = new Component({
	extend: Component, // the base when using custom tags
	template: new Template('<hover-menu></hover-menu>'), // component template used when creating from code
	register: 'hover-menu', // css selector to convert matching elements into a HoverMenu

	constructor: function() {
		// the constructor
		this.submenu = this.find(simpli5.selector('menu'));
		if (this.submenu) this.submenu.hide();
	}
});

var HoverMenuSubmenu = new Component({
	extend: Component,
	template: new Template('<menu></menu>'),
	register: 'hover-menu menu'
});

var HoverMenuItem = new Component({
	extend: Component,
	template: new Template('<menu-item></menu-item>'),
	register: 'hover-menu menu-item',

	constructor: function() {

	}
});

I’ll give it a stylesheet HoverMenu.css to make it look good.

What I want is when the user hovers over the button, the menu pops up.

constructor: function() {
	...
	this.on('rollover', this.open.boundTo(this));
	this.on('rollout', this.close.boundTo(this));
},

open: function() {
	var rect = this.rect();
	this.submenu.show(true);

	this.addClass('open');
	this.submenu.rect({left: rect.right, top: rect.top});
},

close: function() {
	this.submenu.close();
}

Of course, sometimes I might want to have the user click to popup the menu, for ones that are used less often or in the case that there are many on the screen (don’t want to have popups all over the place by moving your mouse around).

var HoverMenu = new Component({
	extend: Component,
	template: new Template('<hover-menu></hover-menu>'),
	register: 'hover-menu',
	properties: ['click-only'], // add attributes that translate to properties in this array

	constructor: function() {
		...
		this.on('click', this.open.boundTo(this)); // click will always open it
		this.clickOnly = false;
	},

	get clickOnly() {
		return this._clickOnly;
	},
	set clickOnly(value) {
		if (this._clickOnly == value) return;
		this._clickOnly = value;
		if (this.submenu) {
			value ? this.un('rollover', this.open.boundTo(this)) : this.on('rollover', this.open.boundTo(this));
		}
	},
});

Here I added an implicit getter/setter that by default is false so hovering will open the menu. But if hoverMenu.clickOnly = false or <hover-menu click-only=”false”>…</hover-menu> then you’ll have to click the button to open the menu.

I’ve also added other settings for customization: autoClose to close the menu automatically when the mouse moves off of it for a few milliseconds, menuDelay to turn off the delay menus take to close (I talk about this later), and openBelow to cause the menu to open up beneath the button instead of to the side of it.

Next we need to allow menu items to hold submenus and to dispatch events when the user selects them. It would be nice if these can be triggered by code too.

// HoverMenuItem
events: ['select'], // add custom events that can be listened to via onevent attributes

constructor: function() {
	this.on('click', this.select.boundTo(this));
	this.on('rollover', this.hovered.boundTo(this));
	this.submenu = this.find('menu');
	if (this.submenu) {
		this.submenu.hide();
		this.addClass('submenu');
	}
},

open: HoverMenu.prototype.open, // use the same function from HoverMenu

close: function() {
	if (this.submenu) {
		this.submenu.close();
		this.removeClass('open');
		this.parentNode.hoveredItem = null;
	}
},

select: function() {
	if (this.disabled || this.submenu) return;
	this.dispatchEvent(new CustomEvent('select', true)); // this is our own event and we will bubble it
	this.menu.close(); // once selected, close the whole menu
},

hovered: function() {
	if (this.disabled) return;
	if (this.parentNode.hoveredItem && this.parentNode.hoveredItem != this) {
		this.parentNode.hoveredItem.close();
	}

	if (this.submenu) {
		this.open();
	}
}

Hopefully you can understand the logic by reading through the code, but there are a couple of things I want to point out. The “events” property holds a list of custom events for the component to look for when initializing its attributes. Because I specified the select event there you can add onselect=”alert(‘item selected’)” to the tag and it will work. Also, our first usability tidbit for the menu, don’t close submenus until the user moves their mouse to a sibling menu item. That wraps up our basic-component-building-101-in-Simpli5 overview and brings us to our user experience in using this component. Now I realize that UX encompasses so much more than a component, but the usability and experience the user has with this component is what I am referring to when I say UX.

Making it Shine with Usability

Most of these things were added because I tried using the menu and noticed spots of frustration. Some were added from suggestions of others.

The first thing I did to enhance the usability of the menu was to keep the entire menu and submenus from closing immediately. The less accurate a user has to be with their mouse, the quicker they can get things done and the easier it is to use an application. If the menu closes because a user accidentally moves the mouse a little beyond the menu then they have to start all over again opening the menu up from the beginning. When the hover menu’s autoClose is true, the menu waits 600 milliseconds before closing. This allows a user to make mouse mistakes and recover from them before having to reopen the menu.

The next usability piece came from testing with longer submenus. I noticed that if I wanted to click the last item in a submenu and I moved my mouse strait to it, the mouse path went over the edge of the next sibling menu item, closing the previous item’s submenu. In order to select that last submenu item I had to alter my mouse path to a 7 shape, moving across to the submenu first, then down to the desired item. In order to allow some forgiveness in the mouse movement while trying not to hamper the speed at opening the next submenu if that is the real desired action, I added a 150ms delay in opening and closing submenus. This seemed to be enough time for a quick mouse movement down across sibling menu items into the submenu, while not being too much time if you wanted to open the sibling submenu. I also added the menuDelay option that defaults to true, but can be set to false if you want to get rid of this 150ms delay.

I added an alternate element style in the stylesheet for elements called <menu-content> which is an alternative to holding menu items in a submenu and allows robust components like color pickers or lists of images to be used, adding to the overall UX of the UI.

I added positioning support for them menus to popup above or to the left of their parent if they are near the edge of the screen. I made the menus append to the body of the document so that they wouldn’t get cut off by any overflow auto/hidden elements. There were also other small things I added too, such as a style on items with open submenus so they can be seen easier and allowing the menu to be closed by clicking elsewhere or pressing Esc.

Overall the component turned out quite well. Here is a demo page of it in action (view source to see the markup): http://jacwright.github.com/simpli5/demos/hover-menu.html

Next Page »