Development standards for e-learning… a starting point

Understanding that we should be using standards and best practices throughout e-learning development, the question becomes “what standards and best practices should we follow?”

Here’s my attempt at outlining some basics. Please feel free to suggest additional items in the comments section.

Follow established “web” best practices.

Separate presentation from content as much as possible by using Cascading Style Sheets and semantic (X)HTML markup.

  1. Use classes instead of inline styles or font tags.
  2. Avoid tables for layout purposes, except when presenting tabular data.
  3. Keep your markup semantic by properly using available elements: h1, h2 and h3 for headings, p for paragraphs, div for block-level content divisions (similar to fieldset in forms), and span for inline classes. Don’t be afraid to try lesser-known elements such as acronym for acronyms, dl for definition lists, and cite for citations!
    1. Note: although div is heavily used in CSS-based layouts, you should try to avoid overuse of the div element. Bloated markup is unsemantic and ugly.
    2. Avoid direct copying and pasting from Microsoft Office products, as this tends to insert bloated Microsoft-specific HTML.
  4. Avoid deprecated or browser-specific elements such as blink and marquee.

Separate behavior from content as much as possible by using unobtrusive JavaScript techniques.

Even if your course depends on JavaScript being available, this is a good practice to follow.

  1. Don’t use inline scripts. Keep all scripts in the head of your document or in an external .JS file.
  2. Use a lot of error checking throughout your scripts. This helps ensure that if an error occurs, your script should fail gracefully, and you should be able to diagnose the problem more easily.
  3. Avoid using JavaScript where it isn’t needed. CSS is capable of handling many chores once assigned to JavaScript, such as image-based mouseovers and “hover” or “dropdown” menus. CSS has the added bonus of processing the chore much faster than JavaScript, too!
  4. Avoid using the global space. With the proliferation of JavaScript in our “Web 2.0” world, web pages use multiple script libraries and customs functions. Avoiding using the global space by using faux “namespacing” (object literals) can greatly reduce the odds of your script conflicting with another script.

Avoid plugins and browser-specific technologies as much as possible

  1. Don’t use Microsoft’s ActiveX or the Windows-specific FSCommand. Stick to cross-browser and cross-platform solutions.
  2. Avoid Java and any other format that requires a plugin, especially one that requires a large download. Flash Player may be the only exception due to its incredibly high saturation rate, but even then you should plan ahead in case users don’t have Flash Player available.
  3. If a plugin is required, especially an unusual or uncommon plugin, be sure to notify the user of the requirement before they launch the course.

Keep content as accessible as possible by following established accessibility best practices.

  1. Avoid tables for layout purposes, except when presenting tabular data.
  2. Avoid using images for important navigation elements. Your markup will be cleaner (and you’ll be separating your style from content) by sticking to text-based navigation links. Don’t worry — you can still use images by utilizing CSS image replacement techniques! If you decide to use images in your markup, be sure to include useful descriptive text in the alt attribute.
  3. Be aware of tab order; don’t mess with the tab order of page elements unless it’s absolutely necessary.
  4. If your page contains lots of navigation menu items, give the user a ‘skip navigation’ option. This is especially important for people who use screen readers.
  5. Ensure links contain useful text such as “Click here to continue” or even “continue,” not the more obscure “click here.”
  6. Heavy use of onmouseover and onclick in JavaScript can cause problems for people who don’t use a mouse (many people use their keyboard for all navigation purposes). Be aware of the repercussions of your coding techniques.
  7. Try testing your course in a popular screen reader such as JAWS.
  8. Rich media such as Flash videos are fine to use in your course. However, there are some simple steps you can take to ensure your Flash content is as widely accessible as possible:
    1. Use fallback (popularly known as “alternate”) content in your HTML in case Flash is not available. For instance, if your page contains a Flash video, include a text transcription in the HTML. When Flash isn’t available, display this transcription automatically (SWFObject is great for this purpose).
    2. If using video, screencasts, or any other media that contains audio narration, include a closed-captioning option or a link to a text transcription.
    3. Adobe has made strides to make Flash SWFs more accessible. Keep up with the latest trends, and use Flash best practices to ensure maximum accessibility.

Keep your courseware portable.

  1. Avoid server-side dependencies such as Active Server Pages (ASP), PHP, and databases.
  2. Use standardized LMS communication protocols such as SCORM and AICC. Avoid using proprietary course-to-LMS communication methods as they make content migration and reusability a very difficult task.
  3. Avoid using remote content if possible. Some systems may not be able to access the remote content due to security or connectivity issues, which could lead to a broken course.

Ensure your course has a long shelf-life and is easily maintainable.

  1. Avoid uncommon development platforms and/or proprietary file formats whenever possible. This ensures your content will remain editable for the foreseeable future.
  2. Clearly annotate/document your development process and home-brewed scripts. This ensures future team members and/or contractors will be able to work on the project if needed.
  3. If you compile or compress your files, be sure to keep a commented, uncompressed copy of each file with your development files. This applies to both JavaScript and Flash-based projects.

What do you think?

Let’s have a conversation about this. I’m 100% positive I’ve missed a few things, and I’m pretty sure not everyone will agree with my statements. Why not join in and add your two cents? I’ll leave the comments section for this topic open indefinitely.

Rapid Intake: Where are the standards?

Today Rapid Intake announced a new service named Unison.

Not having used the service, I won’t pretend to know whether it’s a worthwhile service or not. It’s certainly an intriguing idea, and with its oft-mentioned low price, it’s guaranteed to get some industry buzz.

Out of curiosity, I perused the Rapid Intake site to read more about Unison. I wasn’t very impressed with the Unison product webpage. Being the geek I am, I decided to take a peek at the Unison page’s source code. UGH! They’re using tables for layout, JavaScript for simple navigation menu mouseovers, are omitting alt tags on many images, are using non-web-safe fonts, and are positively abusing CSS styles by applying a class called “maintext” to almost every paragraph.

Here’s a snippet:


<tr>
<td height="310" valign="top" class="h1">
<p class="h1">Collaborative eLearning Development and
Review for Teams</p>
<p class="h1"> </p>
<p class="mainText">Built on the Rapid Intake eLearning 
Development Platform, Unison is a web-based solution 
[ ... ]</p>
<p class="mainText"> </p>
<p class="mainText">Now all your SMEs, designers, 
and reviewers can work together on e-learning courseware 
[ ... ]</p>
<p class="mainText"> </p>
<p class="mainText">All you need to do is <a href="#">logon and get started.</a> </p>
<p class="mainText"> </p>

This code clearly demonstrates a lack of understanding of long-established web standards and best practices.

I certainly don’t mean to beat up on whoever designed their site, but as a company whose business is publishing web-based documents, this website gives me zero confidence in the quality of their product.

Please understand that I’m not trying to be a jerk here. I wouldn’t rag on an individual person’s site or home-brewed course system (I know my site isn’t perfect, either!). It’s just that this site is a perfect example of how our industry appears to pay little heed to web standards and best practices. Rapid Intake — a company clearly on the rise in our niche market — is in a perfect position to be a role model for ‘doing it right.’

Standards make development work easier, and greatly reduce compatibility issues. I just don’t understand why companies like Rapid Intake don’t see that.

HTML 5: The strong element

I just saw something interesting I thought I’d pass along. In the new HTML 5 proposal, the strong element is being modified to represent “importance rather than strong emphasis.”

The WHATWG gives the following example:


<strong>Warning.</strong> This dungeon is dangerous. 
<strong>Avoid the ducks.</strong> Take any gold you find. 
<strong><strong>Do not take any of the diamonds</strong>, 
they are explosive and <strong>will destroy anything within 
ten meters.</strong></strong> You have been warned.

The b element is supposed to represent “a span of text to be stylistically offset from the normal prose without conveying any extra importance, such as key words in a document abstract, product names in a review, or other spans of text whose typical typographic presentation is boldened.”

The WHATWG gives the following example:

The <b>frobonitor</b> and <b>barbinator</b> components are fried.

To me, this is both exciting and confusing. Knowing we can nest the strong element opens up some new styling possibilities while keeping the markup valid and semantic. But using both the b and the strong elements, with different conditions for their usage, will ultimately be confusing for most people, including me.

Wonder how WYSIWYG editors will handle this.

A cross-browser JavaScript prompt

You might be looking at the title of this entry and say “Well, DUH! JavaScript’s prompt() is already cross-browser!”

While working on a project earlier today, I discovered a nasty little problem… Internet Explorer v7 (IE7) disables prompt() by default! This means you can’t rely on prompt() being available in IE7 when building your online applications.

In most cases, prompt() should be avoided altogether. Maybe Microsoft was right in disabling prompt(), since so many malicious sites take advantage of it. However, I had a legitimate use for it today, and was very irked to find out it won’t work in IE7. (I develop in Firefox and IE6, which might explain to some of you why I’m late to the party with discovering this limitation in IE7.)

After googling for a while — yes, I use google as a verb — it appears quite a few people have developed workarounds for the prompt() limitation in IE7. Most of them were bulky or required a bit too much hacking for my tastes. There were a few nice examples [link no longer available] out there, but in the end, I decided to make my own workaround using Microsoft’s proprietary showModalDialog function.

Microsoft’s showModalDialog allows the developer to load an external HTML file into a generated window and give it focus. The user can’t click back to the original document until they’ve closed the modal window, just like a prompt(), alert(), or confirm(). The generated window behaves much like a pop-up window (it can be sized, show/hide scrollbars, etc.), but to my knowledge, modal windows aren’t blocked by pop-up blockers.

I’ll admit that using an external HTML page feels like a big disadvantage compared to a simple prompt(). However, one potential advantage showModalWindow provides is the ability to style the faux prompt window; in IE, the prompt window has always looked and behaved a little different from any of the other dialog windows (alert, confirm). Now we can have it visually fit in with the rest of the family! Due to this styling issue, and to make my life easier, I decided to use showModalWindow on all versions of IE, not just IE7.

Native IE6 prompt:
Native IE6 prompt

Native Firefox 2 prompt:
Native Firefox 2 prompt

New faux prompt, as seen in IE6:
Faux prompt, IE6

New faux prompt, as seen in IE7 (WinXP):
New faux prompt, as seen in IE7 (WinXP)

Important note: The IE7 modal window will forcibly display the address and status bars if being called by a server (not localhost) which isn’t listed in that user’s list of ‘Trusted Sites.’ This is annoying, but still better than having no prompt() at all. IE6 forcibly displays the status bar, but not the address bar.

Example

Before I dig into the code, here’s a functioning example. There are two files involved in this hack: the main HTML page calling the prompt, and the faux prompt HTML page.

The traditional prompt()

For those of you new to using prompts, here’s an example of a traditional prompt in action:


window.onload = function (){
   var name = prompt("Please enter your name", "");
   if(name){
      alert("You entered '" +name +"'.");
   }
}

Return values

A key feature of prompt() is that it returns the value of the textfield as a string. If nothing was typed into the text field, prompt() returns either null or false, depending on your browser. If the user clicks ‘cancel’, prompt() returns false.

Our faux prompt for IE needs to work the same way. As luck would have it, showModalWindow allows us to specify return values.

Prompt text

Another key feature of prompt() is the ability to display your own text in the prompt, such as “Please enter your name.” To make our lives easier, our faux prompt should work the same way, using the same syntax.

Our IE-specific function: iePrompt()

In all its glory:


function iePrompt(str){
   var settings = "dialogWidth: 290px; dialogHeight: 160px; center: yes; edge: raised; scroll: no; status: no;";
   return window.showModalDialog("iePrompt.html", str, settings);
}

Let’s break it down.


function iePrompt(str){

Since we can’t use the traditional prompt(), we need to create a new, similar function. Just as prompt allows you to specify what text will display by passing a string,

prompt("This text will display in my prompt","")

we want to be able to pass a string in our new function:

iePrompt("This text will display in my prompt")


var settings = "dialogWidth: 290px; dialogHeight: 160px; center: yes; edge: raised; scroll: no; status: no;";

IE’s showModalDialog allows you to style the window much like a pop-up window. The settings are entered as a single string. For convenience and readability, I placed all the settings in a variable named “settings”. You can tweak these settings to suit your own needs. A list of optional parameters can be found here.

Important note: The height of the modal window is set here using dialogHeight; if the display text (in this case “Please enter your name”) wraps to the next line, the window will NOT expand to fit it. If you’re working with long strings, you’ll need to test the height of your modal window, or develop your own sizing routine. Also note that in IE6, dialogHeight referred to the height of the entire dialog window. In IE7, the model was changed, and dialogHeight now refers ONLY to the height of the content. See this IEBlog for more info.

return window.showModalDialog("iePrompt.html", str, settings);

As mentioned earlier, showModalWindow can return a value the same way prompt() returns a value. We want our iePrompt() function to pass the value returned by showModalWindow.

  • iePrompt.html is the name/path of the HTML file containing a form that mimics prompt().
  • str is the text that will be displayed in the prompt.
  • settings is the variable containing the settings string we defined earlier.

The iePrompt.html file

I don’t want to spend too much time explaining the HTML file, as it’s a simple HTML file containing a wee bit of CSS for styling, and a very tiny form. There are some important elements to note, though.

The submit function


function formSubmit(){
   var str = document.getElementById("promptText").value;
   if(str){  
      window.returnValue = str;
   } else {  
      window.returnValue = false;
   }
   window.close();
}

In order to get the showModalDialog function to return a value, we have to make our form return a value. To do this, we have to specifically use window.returnValue. The code is set up to conditionally send either the content of the promptText field, or the value false, just like a normal prompt.

After returning the value to the page that called the prompt, we need to close the modal window by calling window.close.


function formCancel(){
   window.returnValue = false;
   window.close();
}

Prompts also have cancel buttons. We want our modal window to have a cancel button that behaves just like a prompt. To do this, we set up a function that returns the value false and closes the modal window.


window.onload = function (){
   var str = window.dialogArguments;
   if(str){
      document.getElementsByTagName("label")[0].innerHTML = str;
   }
}

This bit of code grabs the display text value being passed by iePrompt and dynamically inserts it into a label on our form. We’re passing the argument using the dialogArguments property of the modal window generated by showModalDialog.


<body onbeforeunload="formCancel();">

Lastly, we need to take into account the fact that many people choose to click the dialog window’s close button (X) instead of OK or Cancel. Using onbeforeunload allows us to call formCancel() if the user decides to close the window.

And that’s it! We now have a functioning prompt for IE.

Putting it into action

Now that we have our custom IE prompt, we need to integrate it with non-IE browsers. Fortunately, this is a pretty simple task.

Determining if the browser is IE

Rather than use some elaborate browser-sniffing technique, we’ll stick to supported feature detection, which is considered a best-practice by the DOM scripting crowd. This is easily accomplished by checking for window.showModalDialog. If the return value is true, the browser is a flavor of Internet Explorer (v4 and higher), and supports our new prompt method. If the return value is false, the browser isn’t IE and should be able to use a traditional prompt().


function cbPrompt(str){
   try {
      if(window.showModalDialog){ return iePrompt(str); }
         else { return prompt(str, ""); }
   } catch (e) { 
         return false; 
   } 	
}

I decided to use a try/catch statement for future expandability (being able to add extra functionality, such as using confirm() to validate the text entered by the user. You could rewrite it to use simple if/else statements.

I wrapped the logic into a new function called cbPrompt (“cb” meaning cross-browser). Now anytime I need a prompt, I simply call cbPrompt instead of prompt():


window.onload = function (){
   var name = cbPrompt("Please enter your name");
   if(name){
      alert("You entered '" +name +"'.");
   }
}

As you can see, it’s almost exactly the same as using the traditional prompt:


window.onload = function (){
   var name = prompt("Please enter your name", "");
   if(name){
      alert("You entered '" +name +"'.");
   }
}

Just remember that you have to ensure the iePrompt.html file is present and that the path defined in the iePrompt function is accurate.

Here’s a fully-functional cross-browser example.

New: SWFObject examples page

I’ve been creating assorted SWFObject examples for a few months now, usually in response to a forum post at the SWFObject forum. These examples have been piecemeal, just kinda floating out there with no clear links to them or documentation explaining what they are. I decided to create a guide for the examples, which gives a little bit of background for each example as well as the URL for each example.

http://www.pipwerks.com/lab/swfobject/

Displaying code on WordPress entries using CODE and PRE tags

Once again, a journal entry is borne from frustration.

I write these journal entries using WordPress, which is a very easy-to-use blogging solution.

Since I sometimes write about code, I decided to install the Code Markup WordPress plugin. It works very well, and I am enjoying it a great deal (thanks, Bennett).

However, one problem I kept encountering was overflow… when a line of code was so long it would break my layout.

A quick bit of CSS fixes this in most browsers:


pre {
   overflow: auto;
}

But once again, our friend Internet Explorer 6 said “No! I don’t want to play that way!” Grrr…

It turns out that if your content area has any padding and is also using a fluid sizing method (in my case percentages), the code area will expand beyond the boundaries of your content DIV. Yes, the old box model issue.

Solution? Set a specific width for the pre block element, but only do it for IE… Firefox and other browsers don’t suffer from the box model problem.

The pre block element’s width can be set to a fixed width, but in my case that defeats the purpose of a fluid layout. Then again, if I stick with percentages but set the width to too high of a percentage (“too much” depends on how much padding you’re using), I’d still have issues. Setting a width of 100% will only work if the pre block element has no padding, which would be pretty ugly!

For my purposes, setting pre to 96% width with 1em of padding works out fairly well:


pre {
   margin: 1em 0;  /* left and right margins set to zero */
   padding: 1em;
   background: #FFF;
   border: 1px solid #CCC;
   overflow: auto;
}

/* for IE 6... using "star" hack */
* html pre {
   width: 95%;
}

Email address obfuscation

Note: This post contains old code. Read about the updated code at http://itgotmethinking.com/2009/02/01/obfuscating-email-addresses-revisited/

Everyone knows the story: an innocent email address is posted online and a big bad spambot finds it, relaying it to every spammer on the face of the earth… the email address becomes useless due to the 500 spam emails you get every day!

I always try to encode email addresses on sites I build in an effort to make the addresses more difficult to abuse. This has become a very common practice, thanks largely to free encoder tools such as the Hivelogic Enkoder by Dan Benjamin.

Some encoding methods are easy to beat

However, precisely because of their popularity, some spambots are being written to overcome simpler encoding methods, such as obscuring the address using character entities. A block of text encoded as character entities is easy to defeat with automated decoders, even by an amateur like me. The patterns are still there: the ‘mailto’ protocol, the @ sign, the .com/.org/.whatever, etc… they just look like this:


&#121;&#111;&#117;&#114;&#110;&#097;&#109;&#101;
&#064;&#115;&#111;&#109;&#101;&#100;&#111;&#109;
&#097;&#105;&#110;&#046;&#099;&#111;&#109;

Javascript shouldn’t be required

More complex encoding methods, such as the aforementioned Hiveware enkoder, still seem to work well, but they also rely on Javascript. While the Javascript adds several layers of complexity for the bot to overcome, it also limits what your visitors can do on your site. What if your visitor has Javascript disabled? Many times, they’ll see nothing… no fake email address, no chunks of encoded text, nada. This is a big no-no if you’re a supporter of graceful degradation/progressive enhancement.

I decided to search around for encoding tricks that would work without Javascript. Guess what? I couldn’t find any, aside form the simple character entity encoding described above. It wasn’t much of a surprise, to be honest.

What about spelling it out at myaddress dot com?

I started looking at the common trick of writing “name at somewhere dot com.” I have a couple of problems with this approach: First of all, the address isn’t clickable. Sounds silly, but it’s a big usability component if you’re trying to encourage people to contact you. Secondly, I think it would be easy to write a bot that finds page content written in that format, especially in places where it’s a common practice, such as bulletin boards and forums. This seems like a temptation for a zealous hacker who wants to prove his or her worth. No thanks.

A simple compromise

After kicking around these thoughts for a while, I decided to implement a compromise between two of the three methods I’ve covered so far: using an altered email address to fake out the bots, and using Javascript to make the bots work a little harder.

Granted, I can tell you in advance this isn’t a foolproof method, but it’s very easy to implement and doesn’t leave Javascript-deprived visitors out in the cold.

Part one: Add some useless text to your address.

Yes, that means we’ll be using the standard email link technique:


<a href="mailto:someone@somewhere.com">Email me!</a>

Important! Notice that I didn’t type the email address between the ‘a’ tags… that would make this system pointless! I suggest typing sensible alternate text, such as “Email me!” or the email recipient’s name.

By adding a little bit of unrelated text to the username portion of the address (“REALLYNICE”), we can prevent spambots from knowing what our true email address is:


<a href="mailto:someoneREALLYNICE@somewhere.com">Email me!</a>

So what does this do? It keeps the link clickable, and it prevents the spambot from knowing what our real email address is.

What doesn’t this do? It doesn’t get rid of the useless text (“REALLYNICE”), which means that while the link remains clickable, it’s also useless if the visitor doesn’t manually edit the address.

As the webpage developer, it’s my duty to make the link human-readable, and make the dummy text as obvious as possible. The following example is much easier to read by the average person:


<a href="mailto:someoneRemoveThisText@somewhere.com">Email me!</a>

Part two: use Javascript to make it easier on the end user

At this stage, the email address is somewhat usable, but still requires effort on the end user’s part. If they click the link, it will appear in their email program with the full text “someoneRemoveThisText@somewhere.com”. Some people will see what they have to do and act accordingly, but others might not realize they need to take action.

Here’s where Javascript comes in as a progressive enhancement: we can use Javascript to remove the dummy text when the link is clicked!

A simple Javascript function examines the link text, finds the specified bit of dummy text, and removes it automatically:


function doMail(theLink, key){

    //Get the HREF tag. This includes the anti-spam 'key'
    var before = theLink.getAttribute('href');

    //If the anti-spam key is not found in the link, exit the function without doing anything
    //If the link is clicked more than once, this prevents the Javascript from throwing an error
    if(before.indexOf(key) == -1) return false;

    //Our new variable "addy" is a combination of the text that
    //comes BEFORE the key [0] and AFTER the key [1]
    var addy = before.split(key)[0] + before.split(key)[1];

    //Substitute the original link with the new link ("addy") 
    theLink.href = addy;

}

Sample usage:


<a href="mailto:someoneRemoveThisText@somewhere.com" 
   onclick="doMail(this, 'RemoveThisText')">email</a>

Because the dummy text is specified as a key when the function is called, you can use whatever dummy text you like. For instance, at my office all emails are formatted as: givenname.familyname@ouroffice.org. You could rewrite the address in the following way:


<a href="mailto:givenname.familyname.dummyText@ouroffice.org"
   onclick="doMail(this, '.dummyText')">email</a>

or


<a href="mailto:givennamenoSpam.familyname@ouroffice.org"
   onclick="doMail(this, 'noSpam')">email</a>

Of course, I recommend avoiding terms that are easy for spambots to recognize, such as “nospam”. Why not get creative with something like “SpamSucks”?


<a href="mailto:givennameSpamSucks.familyname@ouroffice.org"
   onclick="doMail(this, 'SpamSucks')">email</a>

You can even put the dummy text in the domain name, if you choose:


<a href="mailto:givenname.familyname@iDontWantSpamAtouroffice.org"
   onclick="doMail(this, 'iDontWantSpamAt')">email</a>

Nothing is invincible!

This email obfuscation method may wind up being easy to crack by the more sophisticated bots, but I feel comfortable knowing that I’ve added a reasonable layer of complexity the spambot must overcome. This in itself will prevent the majority of bots from harvesting my addresses!

I’m also happy because the email address is still human readable (if the dummy text is sensibly written), and is still clickable with or without Javascript. Plus the Javascript is extremely lightweight and the entire method is easier to implement than some of the crazier encoding methods being used today.

@media2007 San Francisco

I just finished the @media2007 conference in San Francisco, and all I can say is “wow.” I was totally in over my head, which in this case is a good thing!

The speakers were all renowned experts in their fields, and I’d be willing to bet 90% of the audience were seasoned web development veterans — no freshmen on this campus! Being an instructional technologist and merely a part-time web developer, I felt like the ugly girl at the school dance sitting against the wall hoping someone would talk to me. Ok, maybe I wasn’t that far out of my element, but it sure was amazing to not only meet the authors of books and blogs I’ve been reading for years, but also to hear them speak about topics they’re truly passionate about. Plus, many of them were very funny guys with excellent stage presence.

At times I enjoyed simply being a wallflower and listening to other people’s conversations. These people, after all, are the movers and shakers of the web world, including W3C members, design gurus and book authors. I mustered up enough nerve to ask a few questions from time to time, but once or twice I felt like a nitwit, as if I just asked a racecar driver if his car goes fast! Oi vay.

For the most part, the speakers were very nice, were approachable, and graciously answered questions, even if it was obvious they’d been asked the same question many times before. Some even handed out free books… I’d like to thank Dan Webb and Jeremy Keith for my books!

Overall, the conference was a success, and the energy/buzz/web-geekiness-run-amok was palpable. I’m very glad I went. Truth be told, I enjoyed it much more than the O’Reilly Web 2.0 conference a few weeks ago.

On a side note, while attending the “Hot Topics” discussion at the end of the event, I volunteered to produce some test suites (mockups) of proposed CSS 3.0 specifications for the W3C’s CSS working group. I must admit I’m pretty nervous about this, but I think it’s a great opportunity to give back to the community and also expand my knowledge and understanding of CSS. *Keep your fingers crossed for me*

I also have this amazing urge to completely redesign my website for fear it won’t live up to the standards of the @media2007 crowd…

Vertical centering — without using tables!

For many years, table-based web page layouts were the rule, not the exception. They were easy to build, they worked cross-browser, and WYSIWYG editors made it a breeze to create and edit tables. CSS-based layouts didn’t really grab hold until just a few years ago, thanks to the evangelism of people like Jeffrey Zeldman and Eric Meyer.

The benefits of CSS-based layouts are very well-documented, and include increased accessibility (for surfers who use assistive technology), ease of site updates (change an entire site’s look and feel with a single stylesheet), and improved search engine rankings (search engines are able to index your content more cleanly when it isn’t surrounded by table markup).

It’s getting easier and easier to avoid table-based layouts these days, and even WYSIWYG editors like Dreamweaver have shifted gears and started including CSS-based layout templates with their software. This is way cool.

But every now and then, a developer like myself will come up against something that was SOOOO easy with table-based layouts and winds up being a royal pain with CSS-based layouts. One of these “d’oh!” moments is when you try to vertically center an element on your web page. Umm… hang on, let me rephrase that: One of these “d’oh!” moments is when you try to vertically center an element on your web page when using Internet Explorer 6.

Firefox supports CSS standards better than Internet Explorer 6 (the dominant browser), and predictably, vertical centering in Firefox is a piece of cake!

This journal entry is devoted to explaining how to get vertical centering working in both Firefox and IE6.

First, an example page.

The ingredients

There are three keys to making this work:

  1. A set of nested DIVs (yes, I know some of you are anti-nested DIV, but hey the world keeps on spinning).
  2. CSS for standards-compliant browsers (Firefox et al)
  3. A separate chunk of CSS for our old friend IE6

Nested DIVs

The first requirement is nested DIVs. You may be thinking “what’s the point of avoiding tables if I still have bloated markup?” My response is that one small set of nested DIVs isn’t nearly as bloated as a table, and using DIVs still helps you keep your markup readable for accessibility purposes and search engines. It’s a good thing! DIVs are also much more flexible should you decide to change your layout later on.

Here’s the layout in all its glory:


<body>
<div id="outer">
  <div id="container">
    <div id="inner">
      <img src="http://pipwerks.com/images/posts/indicator_green.gif" alt="Please wait" width="32" height="32" /><br />
      Loading...<br/>
      Well, not really.<br/>
      But don't I look nice centered like this?
    </div>
  </div>
</div>
</body>

The ‘inner’ DIV is what will hold your content. The ‘outer’ and ‘container’ DIVs’ sole purpose in life is to get your content centered on the page! I’ll explain this in a moment.

The CSS

Let’s look at the CSS:


body {
}

* {
   margin: 0;
   padding: 0;
}

/* macs won't see this! */
html, body { 
   height:100%;
   width:100%;
}
/* END mac */

#outer {
   height:100%;
   width:100%;
   display:table;
   vertical-align:middle;
}

#container {
   display:table-cell;
   vertical-align:middle;
}

#inner {
   text-align: center;
   width: 50%;
   margin-left:auto;
   margin-right:auto;
}

We set all elements on the page to have a default padding and margin of 0. This avoids box-model issues. We also dealt with some Mac inconsistencies with the Mac hack.

As you can see from the CSS, the body and outer elements (outer, container) are set to be 100% wide and 100% tall. The outer element is told to behave like a table (display:table), and is vertically centered using the “vertical-align:middle” property.

By nesting the ‘container’ DIV inside the faux-table, we can make the container DIV behave like a table cell: “display:table-cell”. This DIV is also set to be vertically centered using the “vertical-align:middle” property.

That’s it for the vertical centering in CSS-compliant browsers!

In this case, the ‘inner’ DIV’s CSS is purely for horizontal centering. I set the width to 50% simply because I wanted a narrow DIV, and I centered the DIV horizontally using margin-left: auto and margin-right: auto.

Remember, the code up to this point is for standards-compliant browsers, not IE6.

Dealing with our friend, Internet Explorer 6

Now, you may be thinking that writing different CSS specifically for IE harkens back to the bad old days of browser sniffers and alternate web sites, and you would be correct. However, the alternate CSS is very easy to manage, and doesn’t require a browser sniffer! Thanks to Microsoft’s implementation of conditional comments in IE, we can simply insert a chunk of alternate CSS code into a conditional comment… whatever is inside the comment will be safely ignored by any non-IE browser. Here’s an example:


<!--[if IE ]>
   <style type="text/css">
      body {
         color: #ff0000;
      }
   </style>
<![endif]-->

You can read more about conditional comments at Peter-Paul Koch’s website.

On our vertical centering example, I used the following CSS code in a conditional comment:


#container {
   height: 1px; /* required for IE to properly center vertically */
   position:relative;
   top:50%
}

#inner {
   position:relative;
   top:-50%;
}

Internet Explorer doesn’t support the “display: table”, “display: table-cell” and “vertical-align: middle” properties. To get around this, we have told IE to use relative positioning to move the ‘container’ DIV 50% down from the top of its parent DIV (‘outer’), and to move the ‘inner’ DIV negative 50% from the top its parent DIV (‘container’).

Normally these two values would simply offset each other and cause the ‘inner’ DIV’s content to be displayed at the top of the screen. However, through the magical glitches of IE, by setting the ‘container’ DIV’s height attribute, the DIV suddenly jumps down to the middle of the page, right where we want it to be! I have no idea why, but hey, it works. Here’s the proof.

Important: the height must be set BEFORE the top percentage is declared.

Externalizing the CSS

It’s always a good idea to set up your CSS in external CSS files. This helps keep your web page clean, enables you to re-use your code on other pages, and much more. The CSS contained in the conditional code can also be placed in an external stylesheet. Here’s how I set up my page:


<link href="centering.css" rel="stylesheet" type="text/css" />

<!--[if IE ]>
   <link href="centering-IE.css" rel="stylesheet" type="text/css" />
<![endif]-->

The CSS is exactly the same, but cut and pasted into two external files: centering.css and centering-IE.css.

Now my page is looking clean as a whistle, and I can reuse my code by linking my other pages to my ‘centering’ css files!

Cool, can you do that with a Flash SWF, too?

Yes, you can! Here’s an example.

The code for the Flash example is exactly the same as the first example, except for content: I replaced the contents of the ‘inner’ DIV with some dummy text, and I embedded a Flash SWF using Geoff Stearn’s SWFObject method (the best embedding method I’ve used by far!). No CSS was altered except for the body and font colors.

And there you have it… a nice, (relatively) simple way to vertically center DIVs using CSS and NO tables.

Updates

Thanks to some sharp-eyed readers, here are a few corrections and/or additions:

Update: Here is an example of a Flash SWF embedded with a transparent background. The CSS has been altered slightly: an HTML background image has been specified, and the inner DIV has been set to the exact pixel size of the Flash SWF; this properly centers the SWF and causes scrollbars to appear when a minimum width or height has been reached. Tested successfully in IE6, FF2 and Safari 3 (beta), all Windows XP.

Update 9/03/2007: For IE7, if specifying the height of the inner div, you must also specify the height of the container div in the IE-specific CSS file. For example:

In standard CSS:


#inner {
   text-align: center;
   width: 550px;
   height: 400px;
   margin-left:auto;
   margin-right:auto;
}

In IE7’s CSS:


#container {
   /* required for IE to properly center vertically,
      must match #inner height */
   height: 400px;
   position:relative;
   top:50%
}

Unfortunately, in IE7 this also produces scrollbars (IE7 thinks the page content is larger than it actually is). I plan to do some more research into this when I have the time.

The Fonthead

My brother recently sent me a link to a webpage with free font downloads. This tickled my on-again off-again love affair with typography, and triggered me to post a quickie blog about the subject. The more intricate details of typography (kerning, leading, metrics and the like) seem as obscure as ever to the everday computer user these days. Most people — quite understandably — only know what MS Word requires them to know. Then there are people like me who know a bit about the subject but still get too lazy to follow all the etiquette, such as using em and en dashes appropriately (see the previous sentence for an example) or using ligatures in printed documents.

What’s the difference between a font and a typeface? No, they aren’t the same thing (at least they didn’t used to be). What about the difference between Times Roman and Times New Roman? And why is using Times Roman (either iteration) NOT a good idea for webpages and other on-screen purposes? (Short answer: it’s a serif font designed for newspapers — specifically the London Times in the mid-1800s — and therefore has a smaller x-height than fonts designed for on-screen use, such as Verdana.)

Anyway, being a geek about this sort of thing, I figured I’d present you with links to some typography sites I’ve been browsing recently. They contain excellent primers on typography, its uses and some typograhical history. Enjoy!

Now if I could only remember how to do that pesky em dash…