Wednesday, November 23, 2005

Build Your Own "Personalized Google" Page

google_personalizedOne of my favorite Google tools is the personalized home page. I use it to keep up to date on current events, email, weather, and more. If you haven't seen it, it is worth checking out. Go to google.com, and click the "Personalized Home" link in the upper-right corner. One of the "cool" features for me was the ability to drag-and-drop the various news pods around the page. This allows me to group the information in a way that makes sense to me.

Being a developer, I started thinking about how allowed users to do that with only using JavaScript. I even tried to code something similar, but didn't get too far before real work got in the way. Recently though I was looking at the script.aculo.us JavaScript library, and realized that it was up for the task. In this article I will briefly explain how script.aculo.us makes this a failry simple coding job.

First you will need to download both the script.aculo.us library, as well as the Prototype JavaScript library which it relies on. Once you download both of these, you should create a simple HTML page that looks comething like this below.

<html>
<head>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/effects.js"></script>
<script type="text/javascript" src="js/dragdrop.js"></script>
<script type="text/javascript">

// [JAVASCRIPT]

</script>

<style>

// [CSS]

</style>

</head>
<body>

[CONTENT]

</body>
</html>

In the code above I am assuming that you placed all of the JavaScript files in a subdirectory called "js", just to keep things tidy. Note the three bold sections in the example, this designates the places where we will add our CSS, JavaScript code, and HTML content pieces that are coming up next.

The script.aculo.us library includes a Sortable class, which allows you to create a sortable area. This might seem non-intuitive since we aren't sorting anything, but in a way we are. Our goal is to mimic Google's three column layout. So what we need are three containers that contain a list of pods (or components if you will). We want to drag the pods to new locations in their own column (i.e. change the sort order), as well as be able to move them to another column. The script.aculo.us Sortable class does just this, by allowing us to change the order of elements (i.e. sorting) by dragging them around the page.

So next we need to create three columns, each with their own container element. Since we need three columns, an HTML table seems appropriate, and the container for each column of sortable items will be an HTML <td> tag. The Sortable class will need to know the id's of the container tags, so we will include id attributes in the HTML.

<table width="100%">
<tr>
<td id="container1">[COLUMN 1]</td>
<td id="container2">[COLUMN 2]</td>
<td id="container3">[COLUMN 3]</td>
</tr>
</table>

Next we need to add some content items in the containers so that we have something to move around. In the example below I am using very simple items, just simple div's, with some textual content. The code that we will be writing to make these moveable will require that they are all <div> tags, but note that the div tag may contain any additional content that you want, including images, and nested tables.

<table width="100%">
<tr>
<td id="container1">
<div class="item">Content Item 1</div>
<div class="item">Content Item 2</div>
<div class="item">Content Item 3</div>

</td>
<td id="container2"></td>
<td id="container3"></td>
</tr>
</table>

The next step is to write the JavaScript code to allow us to move the div areas within the container, and between containers. This is done by calling Sortable.create() for each of the three columns. An example of this is below, following which we will examine the arguments that we are passing to the create method. If you are following along, this code should be placed where we placed the [JAVASCRIPT] marker in the starting HTML code.

window.onload = function () {
var params = {
tag: 'div',
ghosting: true,
containment: new Array("container1","container2","container3"),
constraint: '',
dropOnEmpty: true
};

Sortable.create($("container1"), params);
Sortable.create($("container2"), params);
Sortable.create($("container3"), params);
};

We first create a parameter object to include all of the options we need. We could have passed this inline using the {} notation, but since we have three containers it is easier to use a perameter object. The tag setting allows us to specify the content tag type that is inside of our container, in our case it is a div. Setting ghosting to true will add a visual clue to the user of our application by showing them a ghosted version of the content that is being moved. The containment setting lets us specify that we are allowing dragging and dropping between containers as well as within the same container. The constraint setting allows you to specify if content items may be dragged vertically or horizontally, in our case we want both, so we set it to nothing. The last setting we use is the dropOnEmpty setting. By default dropOnEmpty is set to false, which will prohibit dragging items into a container if the container is empty, but this isn't desirable for this application.

Next come the three Sortable.create() statements. The funny looking $() function is part of the Prototype library which is the same as calling document.getElementById(), except that it is a lot shorter. I prefer using $() as it makes the code easier to read.

If you try the code as it is, you should be able to drag and drop between container, and sort items within the same container. So we are pretty much done. The only addition I want to make is to change the cursor to a move pointer when we move the mouse over any of the items, and add some color to the container div's to make them easy to see. You may have noticed that I added an "item" CSS class to the div items in the HTML earlier. Here is where we will use it. The following code gets placed in the [CSS] section of the original HTML.

.item {
cursor: move;
}
#container1 {
background-color: #e0e0e0;
width: 33%;
vertical-align: top;
}
#container2 {
background-color: #e0e0e0;
width: 33%;
vertical-align: top;
}
#container3 {
background-color: #e0e0e0;
width: 33%;
vertical-align: top;
}

Adding that, you should have a (not quite) completed personalized home page. From here you should add some additional CSS, create some real content, and maybe even create a mechanism to save the sorting changes via AJAX calls. You can get more information on other available settings and events on the Sortable page of the script.aculo.us wiki.

7 comments:

Lee Crowhurst said...

Very helpful article!

Have you done any work with the AJAX calls to update a database with the position changes?

If you have, an article on that would be very helpful as I've not have much luck with that (setting up the onChange option)!

Robert Hanson said...

No, I haven't done it yet, but I have wanted to. I just finished up a large project at work, so hopefully I'll find some time to try it out.

Anonymous said...

a nice solution-->
http://tool-man.org/examples/sorting.html
try it~

Robert Hanson said...

That looks very cool, I'll have to take some time to check it out.

Anonymous said...

That is awesome!
As an aside, any idea on how to achieve this outside of a table? I'm trying to make an XHTML 1.1 and WAI-compliant site so I shouldn't be using tables for layout structure.

Robert Hanson said...

MedicineWorker, I am sure that it is possible... but for me tables was just a lot easier to lay out since I didn't need to much with CSS.

Anonymous said...
This comment has been removed by a blog administrator.