How to Mimic the iGoogle Interface

Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Nettuts+.

In this tutorial, I'll show you how to create a customizable interface with widgets. The finished product will be a sleek and unobtrusively coded iGoogle-like interface, which has plenty of potential applications!

Finished Project

The Plan

First, let's list exactly what we'll be creating here and what features it'll have:

  • This interface will contain several widgets.
  • Each widget can be collapsed, removed and edited.
  • The widgets can be sorted into the three seperate columns by the user (using a drag and drop technique).
  • The user will be able to edit the color and title of each widget.
  • Each widget can contain any amount of regular HTML content, text, images, flash etc.

Please note that we will only be covering the front-end aspect of the project in this tutorial. Obviously you could integrate this UI with a solid server-side system which could take care of saving preferences and customised widgets.

Since it's all about the user and because the idea was influenced by iGoogle we're going to be calling this project 'iNettuts'.

The layout of iNettuts

The layout is a simple three column one; each column contains widgets:

layout

Each widget has a "handle" which the user can use to move the widget around.

jQuery UI

As well as the jQuery core library we're also going to make use of the jQuery's UI library and specifically the "sortable" and "draggable" modules. This will make it quite simple to add the drag-and-drop functionality that we want. You should get a personalized download of the UI library which has what we need in it. (Tick the 'sortable' box)


Step 1: XHTML markup

Each column will be an unordered list (UL) and each widget within the columns will be a list item (LI):

First column:

The above code represents the first column, on the left and two widgets each within a list item. As shown in the plan, there will be three columns - three unordered lists.


Step 2: CSS

We'll be using two CSS StyleSheets, one of them will contain all the main styles and the second StyleSheet will only contain styles required by the JavaScript enhancements. The reason we seperate them like this is so that people without JavaScript enabled do not waste their bandwidth downloading styles which they're not going to use.

Here is inettuts.css:

There's nothing too complicated in the above StyleSheet. Normally it would be better to use images instead of the CSS3 border-radius property to create rounded corners (for cross-browser benefits) but they're not really an integral part of the layout - adding a border-radius is quick and painless.

Just a note about the colour classes: Ideally, elements should be named according to their semantic meaning or content, not their appearance. The problem is that the widgets can mean/contain many different things so having classes like this really is the best alternative, unless you're willing to add the colour styles inline. Each colour class is prefixed with 'color-'; it'll become clear why I've done this later in the tutorial.

In the above CSS we're also using a min-height hack for each column so that the background images (the dividers) remain intact and so that an empty column can still have widgets dragged back into it:

We'll focus on the second stylesheet later when we've added the JavaScript.

Here's a preview of what we've got so far, just CSS/HTML (and some images):

layout

Step 3: JavaScript

Introduction

As I've said, we'll be using jQuery. It's the library of choice not only because of the UI modules it offers but also because it will help in speeding up the development process while keeping everything cross-browser operable.

The final product will have endless possibilities, some of which have already been explored by the likes of NetVibes and iGoogle. So, we want to make sure our code is easily maintainable, allows for expandability and is reusable; we want it to be future-proof!

We'll begin with a global object called "iNettuts" - this will act as the sole occupied namespace of the project (plus dependencies such as jQuery). Under it we will code up the main functionality of the site which utilises jQuery and its UI library.

inettuts.js:

The init method will be called when the document is ready for manipulation (i.e. when the DOM is loaded and ready). While there are various methods available it has been proven that the quickest way to initialise your code upon this event is to call it from the bottom of your document. It also makes sense to link to all the scripts at the bottom so as not to slow down the loading of the rest of the page:

Settings

As I've said, there will be a settings object which will contain all of the global settings required to make this functional. We'll also have individual widget settings objects, this means it will be possible to create per-widget settings.

settings object (under iNettuts):

Yes, there are quite a lot of settings, but if we want maximum code reusability this is a necessity. Most of the above is self explanatory. As you can see we've setup a widgetDefault object which contains the default settings for each widget; if you want to override these settings then the script will require you to give the widget an id (in the HTML) and then create a new ruleset. We've got two rule-sets (objects) which override their defaults, 'intro' and 'gallery'. So, those rules specified in the "gallery" object will only apply to this widget:

Retrieving the settings

getWidgetSettings object (under iNettuts):

This method will return an object with the settings of any particular widget. If a widget has no id (HTML attribute) then it will just return the default settings, otherwise it will look to see if that widget has settings of its own, if it does then it will return the default settings and that widget's settings merged into a single object (the widget's individual settings have precedence).

Attaching a CSS file using JavaScript

I mentioned earlier that we have an additional stylesheet which the JavaScript enhancements will require.

Here's the StyleSheet (inettuts.js.css):

The elements targetted in the above StyleSheet have yet to be coded, but eventually we will write the JavaScript which dynamically adds these elements to the page, thus making use of the StyleSheet.

The method which attaches this StyleSheet is called 'attachStylesheet':

The above method appends a link to the head of the document. When a new link element is added to the document through the DOM the browser will load it and apply its CSS rules as it would for any regular hard-coded linked StyleSheet. When doing this, remember that the rules of CSS inheritance and specificity still apply.

Making the widgets work

Widgets

The next part of the tutorial is probably the hardest, so take it slowly.

We want to add another method to our global iNettuts object, we'll call it makeSortable:

By the way, 'method' is just a fancy name given to 'functions' which have been assigned to object properties. In this case our object is called 'iNettuts' so 'makeSortable' is a method of 'iNettuts'...

This new method will take the settings we specified in the 'settings' object and make the required element sortable.

First, we want to make sure everything we need is easily accessible within this new method:

*1: There will only be one instance of our global object, but just encase multiple instances need to be made or if we want to rename the global object it's a good idea to set a new variable (in this case 'iNettuts') to the 'this' keyword which references the object that this method is within. Be careful, the 'this' keyword is a bit of a beast and doesn't always reference what you think it does!

*2: At the very top of the iNettuts object we've placed a new property: 'jQuery : $'. In the pursuit of maximum code reusability we don't want our script to conflict with any other libraries that also make use of the dollar-sign symbol (e.g. The Prototype library). So, for example, if you renamed jQuery to JQLIB then you could change the 'jQuery' property to JQLIB and the script would continue to function properly. The 2nd line in the above code isn't necessary at all, - if we didn't want it we could just use this.jQuery().ajQueryFunction() instead of $() within this method.

*3: Again, this isn't really necessary, we're just creating a bit of a shortcut, so instead of having to type out 'this.settings' within this method we only need to type out 'settings'.

The next step is to define a set of sortable items (i.e. the widgets that will be movable). Remember, back in the settings we made it possible to set a property called 'movable' to true or false. If 'movable' is set to false, either by default or on individual widgets we have to cater for that:

Now we've got a set of DOM elements referenced in the jQuery object which is returned from the above functions. We can make immediate use of this:

So, we're looking for what has been defined as the 'handle' within the movable widgets (within sortableItems) and then we're applying a new CSS cursor property of 'move' to each one; this is to make it obvious that each widget is movable.

The mousedown and mouseup functions are needed to work around some issues with dragging and dropping... Since we want this page and all the elements inside it to expand when the browser is resized we haven't set any explicit widths on the widgets (list items). When one of these list items is being sorted it becomes absolutely positioned (while being dragged) which means that it will stretch to its content's composite width. Here's an example:

When dragged the widget stretches to length of page

This is what should be happening:

When dragged the widget has the correct width!

To make this happen we have explicitly set the widget's width to what it was before dragging had begun. The UI 'sortable' module does have a property in which you can put a function that will run when a widget starts being sorted (i.e. when it starts being dragged), unfortunately this is not good enough for us because it runs too late; we need to set the width before the 'sortable' module takes hold - the best way to do this is by running a function on mousedown of the handle (the 'handle', in this case, is the bar at the top of each widget).

If we leave it like this then when you drop the widget in a certain place and res

the browser the widget will not change in size. In order to prevent this we need to write a function to be tied to the mouseup event of the handle:

The 'dragging' class is added on that 'start' property of the sortable module which we talked about earlier. (we'll write that code later)

This is what our makeSortable method looks like so far:

Next, still within 'makeSortable' we need to initialise the 'sortable' module:

The above options setup the behaviour we want for our sortable widgets. There are plenty more available options for this module but those above will be sufficient for now.

Editing, removing and collapsing widgets

The next step is to make it possible for the user to collapse widgets, close (remove) widgets and edit certain elements within each widget.

We're going to put this all within one method, we'll call it 'addWidgetControls':

As with 'makeSortable' we want to set the following variables at the start:

We need to loop through every widget on the page and add functionality dependent on the default settings or the settings made for any particular widget.

As you can see from the above code, we're checking the settings before adding any one of the three buttons and each button’s corresponding functionality.

Before we write out exactly what will happen within each of three conditions let's list exactly what each of these buttons will do:

  • CLOSE (remove): This button will remove the widget from the DOM. Instead of just removing it immediately we'll apply an effect which will fade out he widget and then slide up its occupied space.
  • EDIT: This button, when clicked, will bring up an 'edit box' section within the widget. Within this 'edit' section the user can change the title of the widget and its colour. To close the 'edit' section the user must click on the same 'edit' button again - so basically this button toggles the 'edit' section.
  • COLLAPSE: This button switches between an up-arrow and a down-arrow dependent whether the widget is collapsed or not. Collapsing a widget will simply hide its content, so the only viewable of the widget will be the handle (the bar at the top of each widget).

We know what we want now, so we can start writing it: (The snippets below are riddles with comments so make sure you read through the code!)

CLOSE (remove):

EDIT:

COLLAPSE:

What is "Event Bubbling"?

Event bubbling or 'propagation' is when, upon clicking on an element, the event will bubble up through the DOM to the highest level element with an event the same as the event you just triggered on the original element. If we didn't stop propogation in the above snippets (e.stopPropagation();) on the mouseDown event of each added button then the mouseDown event of the handle (parent of the buttons) would also trigger and thus the dragging would would begin just by holding your mouse down over one of the buttons - we don't want this to happen; we only want dragging to begin when the user puts their mouse directly over the handle and pushes down.

Edit-box events/functionality

We've written the code which will inject the edit boxes into the document in the correct places. - We added an input box so users can change the title of a widget and we also added a list of available colours. So, we now need to loop through each new edit-box (hidden from view) and specify how these elements can be interacted with:

The edit-boxes are entirely functional now. All the above code resides in the 'addWidgetControls' method.


Almost finished

Now that we've written most of the JavaScript we can write the initiating method and intialise the script!

Now, to start it all:

Just so we're clear, this is the overall structure of our iNettuts object with each of its methods explained:


 Finished!

Finished Project

We're totally finished, the interface should be totally operable now. I've tested it on my PC (running Windows XP) in the following browsers: Firefox 2, Firefox 3, Opera 9.5, Safari 3, IE6, IE7 & Chrome.

Note: There are a couple of issues in IE. Specifically, it doesn't set the placeholder size correctly plus there are some CSS issues in IE6 (to be expected).

At first glance this interface's potential applications seem limited to those like iGoogle or NetVibes but it can, in fact, be used for many different things.

  • You could, for example use it on your blog by giving the user the option to sort your blog's widgets in the sidebar - you could then save their preference to a cookie so that the widgets would be in the same order when the user returns.
  • If you add a user authentication system and a database then you've got yourself a simple iGoogle.
  • The 'sortable' plugin itself can be used for sorting any elements, they don't have to be widgets.

Regardless of whether you're going to use this in a project or not I hope you've learnt something today!

Tags:

Comments

Related Articles