Wednesday, May 18, 2011

A Custom Dialog Box in JQuery Tutorial

In the Jquery world, constructing a basic custom dialog box can be quite trivial. Using Jquery UI, creating a dialog box like the above can be done with this little snippet of code:

<div id="dialog" title="Basic dialog">
<p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p>
$( "#dialog" ).dialog();

Web development is easy right? Well in a perfect world yes, but as any developer will tell you there are two worlds: the ideal world, and the real world. JQuery UI is a fine toolkit when one is looking for a standard off the shelf styled widget, but not when something more custom is desired - even with help from the excellent Themeroller.

The recent Twitter UI redesign does everything it can to ensure users will use their browser's back button less. Many of the actions possible do not change the principle view for the user, but merely extend it. One such measure was the compose a tweet dialog box. The JQuery UI dialog widget won't give you a Twitter inspired dialog box - you have to create your own.

Analysis of the Twitter dialog box reveals the following:
  • Non modal - actions possible on the originating view
  • Fixed position - dialog box is fixed in center of window even as window scrolls
  • Dialog box has a simple structure, but the nature of opaque inheritance could cause some problems.

The dialog box really has a very simple structure, and the HTML required reflects this.

<div class="dialog">
<div class="dialog-frame">
<h1>Our first Dialog</h1>
<div class="dialog-content">
<p>A pleasure to open our first dialog with you</p>

The dialog-frame div is a sibling of the dialog-content div rather than being its parent. The reason for this is to accommodate a
semi-transparent dialog frame. Once you reduce the opacity of an element, its children will automatically inherit this opacity. You cannot increase the opacity of an element from the value it has inherited.

Most of the work is in the javascript: you don't know the size of the user's browser window when writing your css, so you need to set dimensions and coordinates of the dialog box dynamically.

var dialog = $('.dialog') // cache the dialog element

// Initial dialog setup
var dialogFrameWidth = '10px'
dialog.css({position: 'fixed', padding:dialogFrameWidth})

// Set dialog coordinates
dialogHeight = dialog.outerHeight()
dialogWidth = dialog.outerWidth()
dialogTop = ($(window).height() - dialogHeight)/2
dialogLeft = ($(window).width() - dialogWidth)/2
dialogFrameWidth = "10px"
dialog.css({top: dialogTop, left: dialogLeft, position: fixed, padding:dialogFrameWidth}})

// Set coordinates and dimensions of dialog frame
dialogFrame = dialog.find('.dialog-frame')
dialogFrame.css({height: dialogHeight, width: dialogWidth, position: 'absolute', top: 0, left: 0, opacity: 0.8, z-index:1000}})

// Set coordinates and dimensions of dialog content
dialogFrameHeaderHeight = dialogFrame.find('header').outerHeight(true)
dialogContent = dialog.find('.dialog-content')
dialogContent.css({'margin-top': dialogFrameHeaderHeight, position: 'relative', top: 0, left: 0, z-index: 3000})

Add some style like background colors, fonts, etc. and your in business!


Monday, May 16, 2011

Simplifying a javascript jump menu

The jump menu is a reasonably common UI design pattern on the web. It is used by big guns such as Facebook and Twitter, and has been dissected and reproduced many times over.

When I set about implementing my own jump menu I found opening and closing the menu when it is clicked on was rather trivial.


The difficulty came in closing the menu when there is a click outside the jump menu. Googling for answers, I found the scenario of closing a menu on clicking outside it to be a well trodden path. Stackoverflow had plenty of examples and possible solutions such as here and here. I even found a plugin created by Ben Alman that extended Jquery to provide a custom clickoutside event. Many of the proposed solutions were fragile, and I was slow to add a new plugin to my web application - as good and all as the implementation appeared. I really wanted to understand the problem before opting to take a proposed solution off the shelf.

The state diagram above illustrates the four possible actions for the two menu states of open and closed. We can see that one of the four actions results in no change of state, while another two result in the same change of state. Clearly this diagram could be simplified, and along with it my perception of the problem.

"Make things as simple as possible, but not simpler" - Albert Einstein

Given this simplified state transition diagram, there was no need for an event register, a global array, comparing with, a handler that would be called for each and every document click, or the jquery plugin. All that I needed was something like the following:

isMenuOpen = function(menu) {
return menu.hasClass('ui-active-state');
openMenu = function(event) {
var documentClicks, menu, self;
menu = $('.jump-menu');
if (isMenuOpen(menu)) {
self = this;
documentClicks = 0;
$(document).bind("click", __bind(function(e) {
if (documentClicks === 2) {
return this;
}, this));
return this;
closeMenu = function(menu) {
menu = $(this.el).find('.jump-menu');

There is always another way to look at a problem.
Happy Coding!