-
Featured Columnists
- Faruk Ateş
- Andy Clarke
-
Kris Hadlock
- Designing with Code: Providing Feedback
- Designing With Code: Creating a Resizable Interface
- Designing With Code: CSS Tips and Tricks to Speed Your Workflow
- Designing with Code: Handling PNG Transparency on the Web
- Designing With Code: Collaboration
- Designing With Code: Improving CraigsList
- Designing With Code: How to Create a Tag Cloud
- Designing with Code: RSS
- Designing With Code: Tumblelogging
- Designing With Code: Create a DropIn JavaScript Component
- Designing with Code: Leveraging Your Existing Content
- Designing With Code: Leveraging RSS
- Designing With Code: Converting Forms to Ajax
- Designing with Code: Converting Forms to Ajax, Part 2
- Designing With Code: Monster Mash
- How to Create Dynamic Script Tags for Ajax Components
- Creating a Winning Proposal for Web Projects
- Creating a Web Design Questionnaire
- Using Stylesheets in Flash CS3
- Animating with XML in Flash CS3
- Creating a Full-Screen Web Site with Flash CS3
- Robert Hoekman, Jr.
- Molly Holzschlag
- Sarah Horton
- Miraz Jordan
- Jonathan and Lisa Price
- Catherine Seda
- Dave Shea
- Dave Taylor
-
Table of Contents
- Welcome
- Web Basics
- Publishing on the Web: Putting Files on the Server
- Web Design Process and Workflow
- Project Management
- Mark My WWWord: HTML and XHTML
- Standards Compliance
- Layouts
- Forms
- Meta Tags and Search
- Usability
- Accessibility
- Enhancing Web Page Interaction
- Web Graphics
- Web Page Optimization
- Multimedia
- Content
- Overview of Servers
- Server Programming Basics
- Careers in Web Design
- Tools
- Tutorials
- Intellectual Property for Web Designers
Designing With Code: Tumblelogging
Last updated Oct 17, 2003.
As Robert Hoekman mentioned in his previous Designing the Obvious Clinic on Tumblr, tumblelogs are more stream-of-conscious than traditional blogs. With this concept in mind, the interactive process for adding items to your tumblelog should be extremely fast, eliminating any distractions and letting your thoughts flow. I love the simplicity of Tumblr, but I think it could be improved to allow posts to be more spur of the moment.
Have you ever had a brilliant idea, only to forget what it was when the time came around to finally implement it? Well, I don't know about you, but I have these moments all too often, so when I create web applications, I try to spare my users a similar fate.
In Tumblr, when choosing an action—such as adding a quote, a post, etc.—the buttons for the rest of the actions disappear. If you decide to change actions, you're forced to hit the back button, which interrupts the experience and causes you to lose focus. Simply keeping the actions consistent across all of the pages would certainly prevent this issue from ever occurring. We could even add a visual marker to identify what action is currently active.
Figure 1. Disappearing navigation
Figure 2. Consistent navigation
I know the navigation adds some height and additional elements to the page, but it's well worth the improved experience. I always try to create a simple visual design in my web apps, but simple interactions are just as important.
Now that our experience is not interrupted by the disappearing navigation, let's take it a step further and eliminate the page reloads. To do this, we'll implement an Ajax engine to allow the application to run in one page with dynamic page states. This will eliminate almost all of the user effort and allow thoughts to flow freely.
Before getting started, you can download the source code for the example that we will create here. Let's start with the HTML from Tumblr's publisher page. We'll add absolute paths to all of the URLs and strip the content and navigation so that we're left with the shell of the web page.
Tumblr, stripped of content and navigation (shell.html)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Tumblr</title>
<link href="http://www.tumblr.net/stylesheets/publisher.css" media="screen" rel="Stylesheet" type="text/css"/>
<script type="text/javascript" language="javascript" src="http://www.tumblr.net/javascript/prototype.js"></script>
<script type="text/javascript" language="javascript" src="http://www.tumblr.net/javascript/effects.js"></script>
</head>
<body id="publisher_index">
<div id="header">
<a href="./"><img src="http://www.tumblr.net/images/publisher_logo.gif" id="logo" alt="Tumblr"/></a>
<p><a href="http://krishadlock.tumblr.com">Take me to krishadlock.tumblr.com</a> «</p>
<p><a href="/settings">Change settings</a> «</p>
<p><a href="/publisher/logout">Log out</a> «</p>
<p>
<span style="background-color:#6aa72d; color:#e9f7b8;">New:</span>
<b><a href="http://blog.davidville.com/2007/03/01/import-feeds/"
target="_blank">Introducing Import Feeds</a></b> »
</p>
</div>
<div id="container">
<img id="top" src="http://www.tumblr.net/images/publisher_top.gif" alt=""/>
<div id="inner_container">
<h1>Add a new...</h1>
<div id="create_buttons">
</div>
<div id="page"></div>
</div>
<img id="bottom" src="http://www.tumblr.net/images/publisher_bottom.gif" alt="" /></div>
</body>
</html>
We will then create an Ajax engine that takes requests and delegates responses to a specified callback method.
Ajax engine (js/Ajax.js)
var Ajax = new Object();
Ajax.isUpdating = true;
Ajax.Request = function(method, url, callback)
{
this.isUpdating = true;
this.callbackMethod = callback;
this.request = (window.XMLHttpRequest)? new XMLHttpRequest(): new ActiveXObject("MSXML2.XMLHTTP");
this.request.onreadystatechange = function() { Ajax.checkReadyState(); };
this.request.open(method, url, true);
this.request.send(url);
}
Ajax.checkReadyState = function(_id)
{
switch(this.request.readyState)
{
case 1: break;
case 2: break;
case 3: break;
case 4:
this.isUpdating = false;
this.callbackMethod(this.request.responseXML.documentElement);
}
}
With the Ajax engine included in the index page we can start making requests. We will create two methods: AddNavigation and AddPage. AddNavigation will parse an XML file that contains the navigation and add it to the page where it used to belong. AddPage will parse XML files that contain the different page content and add it to a div with an id named page.
Tying it all together (index.html)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Tumblr</title>
<link href="http://www.tumblr.net/stylesheets/publisher.css" media="screen" rel="Stylesheet" type="text/css"/>
<script type="text/javascript" language="javascript" src="http://www.tumblr.net/javascript/prototype.js"></script>
<script type="text/javascript" language="javascript" src="http://www.tumblr.net/javascript/effects.js"></script>
<script type="text/javascript" src="js/Ajax.js"></script>
<script type="text/javascript">
function AddNavigation(xml)
{
var navigation = xml.getElementsByTagName('navigation')[0];
document.getElementById('create_buttons').innerHTML = navigation.firstChild.data;
Ajax.Request('GET', 'pages/posts.xml', AddPage);
}
function AddPage(xml)
{
var page = xml.getElementsByTagName('page')[0];
document.getElementById('page').innerHTML = page.firstChild.data;
}
</script>
</head>
<body id="publisher_index" onload="javascript:Ajax.Request('GET', 'navigation.xml', AddNavigation);">
<div id="header">
<a href="javascript:Ajax.Request('GET', 'pages/posts.xml', AddPage);"><img src="http://www.tumblr.net/images/publisher_logo.gif" id="logo" alt="Tumblr"/></a>
<p><a href="http://krishadlock.tumblr.com">Take me to krishadlock.tumblr.com</a> «</p>
<p><a href="/settings">Change settings</a> «</p>
<p><a href="/publisher/logout">Log out</a> «</p>
<p>
<span style="background-color:#6aa72d; color:#e9f7b8;">New:</span>
<b><a href="http://blog.davidville.com/2007/03/01/import-feeds/"
target="_blank">Introducing Import Feeds</a></b> »
</p>
</div>
<div id="container">
<img id="top" src="http://www.tumblr.net/images/publisher_top.gif" alt=""/>
<div id="inner_container">
<h1>Add a new...</h1>
<div id="create_buttons">
</div>
<div id="page"></div>
</div>
<img id="bottom" src="http://www.tumblr.net/images/publisher_bottom.gif" alt="" /></div>
</body>
</html>
Now that all of our requests are being made and the engine is in place, we simply need to take the page content from Tumblr and add it to XML files that can be loaded dynamically. Rather than showing the entire page content from Tumblr, here is an example of the XML structure that each page will use, plus the navigation that we will load when the page is first entered.
XML structure of pages (pages/*.xml)
<?xml version="1.0" encoding="ISO-8859-1" ?>
<tumblr>
<page><![CDATA[]]></page>
</tumblr>
XML structure of navigation (navigation.xml)
<?xml version="1.0" encoding="ISO-8859-1" ?>
<tumblr>
<navigation><![CDATA[
<a href="javascript:Ajax.Request('GET', 'pages/regular.xml', AddPage)"><img src="http://www.tumblr.net/images/add_regular_post.gif" alt="Regular Post"/></a>
<a href="javascript:Ajax.Request('GET', 'pages/photo.xml', AddPage)"><img src="http://www.tumblr.net/images/add_photo.gif" alt="Photo"/></a>
<a href="javascript:Ajax.Request('GET', 'pages/quote.xml', AddPage)"><img src="http://www.tumblr.net/images/add_quote.gif" alt="Quote"/></a>
<a href="javascript:Ajax.Request('GET', 'pages/link.xml', AddPage)"><img src="http://www.tumblr.net/images/add_link.gif" alt="Link"/></a>
<a href="javascript:Ajax.Request('GET', 'pages/conversation.xml', AddPage)"><img src="http://www.tumblr.net/images/add_conversation.gif" alt="Conversation"/></a>
<a href="javascript:Ajax.Request('GET', 'pages/video.xml', AddPage)"><img src="http://www.tumblr.net/images/add_video.gif" alt="Video" style="margin-right:0px;"/></a>
<div style="clear:both; height:1px;"></div>
]]></navigation>
</tumblr>
With this code in place the entire application becomes a single screen and allows for much quicker decisions. Overall, this example is essentially a web site component that can easily be reused for other single-screen applications. A live running sample can be viewed here.
