Primary Links:

Course Information

TTH 4:30-5:45
MTHW 301
J. Tirrell
jtirrell@purdue.edu
office: HEAV 207
office hours:
TTH 3:00-4:00

456 Berea Street

Syndicate content
Roger Johansson is a web professional specialising in web standards, accessibility, and usability.
Updated: 9 weeks 5 days ago

Accessible expanding and collapsing menu

Tue, 2007-05-29 15:08

Everybody makes mistakes, so occasionally things go wrong. One example is when Swedish design magazine Cap&Design recently published a tutorial on creating a collapsing and expanding menu.

While reading the article my jaw dropped as I realised that the tutorial uses invalid, non-semantic HTML, inline CSS and event handlers, bloated (but, amazingly, valid) CSS, and obtrusive JavaScript. In 2007.

I sent an email to the magazine's editor, who agreed that the article shouldn't have been printed and took the example code offline. Thank you very much for doing that – hopefully it will limit the number of people using the code in their projects.

Instead of just complaining about the lousy code and deriving satisfaction from making them take the code offline, I decided to recreate the menu from the tutorial in a more modern way, using leaner and more efficient HTML and CSS, and unobtrusive JavaScript. This kind of menu isn't anything new, of course. Nevertheless I think publishing this comparison of the two approaches may be useful.

The HTML

The HTML that appeared in the article is this (shortened, and with id values translated from Swedish):

  1. <div id="menu">
  2. <div id="mainmenu"><a href="#" onclick="toggle('submenu1')">Category 1</a></div>
  3. <div id="submenu1" style="display:none">
  4. <div id="submenu"><a href="#">Submenu 1</a></div>
  5. <div id="submenu"><a href="#">Submenu 1</a></div>
  6. </div>
  7. <div id="mainmenu"><a href="#" onclick="toggle('submenu2')">Category 2</a></div>
  8. <div id="submenu2" style="display:none">
  9. <div id="submenu"><a href="#">Submenu 2</a></div>
  10. <div id="submenu"><a href="#">Submenu 2</a></div>
  11. </div>
  12. <div id="mainmenu"><a href="#" onclick="toggle('submenu3')">Category 3</a></div>
  13. <div id="submenu3" style="display:none">
  14. <div id="submenu"><a href="#">Submenu 3</a></div>
  15. <div id="submenu"><a href="#">Submenu 3</a></div>
  16. </div>
  17. </div>

This markup is problematic for several reasons:

  1. id values must be unique, and may not be shared by several elements on the same page.
  2. Using CSS to hide something (via style="display:none") and JavaScript to show it makes the hidden content inaccessible to people who browse with CSS on and JavaScript off.
  3. It uses inline CSS.
  4. It uses inline event handlers.
  5. The menu consists of several lists of links, so list elements should be used instead of div elements.

I replaced the HTML with this:

  1. <ul class="menu">
  2. <li><a href="#">Category 1</a>
  3. <ul>
  4. <li><a href="#">Submenu 1a</a></li>
  5. <li><a href="#">Submenu 1b</a></li>
  6. </ul>
  7. </li>
  8. <li><a href="#">Category 2</a>
  9. <ul>
  10. <li><a href="#">Submenu 2a</a></li>
  11. <li><a href="#">Submenu 2b</a></li>
  12. </ul>
  13. </li>
  14. <li><a href="#">Category 3</a>
  15. <ul>
  16. <li><a href="#">Submenu 3a</a></li>
  17. <li><a href="#">Submenu 3b</a></li>
  18. </ul>
  19. </li>
  20. </ul>

The menu now consists of nested lists of links, the inline CSS and event handlers are gone, and a class name is used to allow for more than one menu on a page, should there be a need for that. Should the browser lack support for JavaScript, all sub menus will now be displayed, and the user is not prevented from navigating the site. Much better in my opinion.

The CSS

Next a look at the CSS used in the article (id selectors translated from Swedish):

  1. #menu {
  2. font-family: Verdana, Arial, Helvetica, sans-serif;
  3. font-size: 10px;
  4. font-weight: normal;
  5. left: 20px;
  6. top: 20px;
  7. }
  8. #menu #mainmenu a {
  9. color: #FFFFFF;
  10. text-decoration: none;
  11. display: block;
  12. background-color: #990066;
  13. padding-top: 2px;
  14. padding-right: 5px;
  15. padding-bottom: 2px;
  16. padding-left: 5px;
  17. width: 150px;
  18. position:relative;
  19. }
  20. #menu #mainmenu a:hover {
  21. background-color: #CC6699;
  22. }
  23. #menu #submenu a {
  24. color: #FFFFFF;
  25. text-decoration: none;
  26. display: block;
  27. background-color: #CC0066;
  28. padding-top: 2px;
  29. padding-right: 5px;
  30. padding-bottom: 2px;
  31. padding-left: 5px;
  32. width: 135px;
  33. left: 15px;
  34. position:relative;
  35. }
  36. #menu #submenu a:hover {
  37. background-color: #CC6699;
  38. }

While that CSS is not invalid, it is definitely not optimal, for the following reasons:

  1. It suffers from what I call selectoritis, that is the use of overly specific selectors (#menu #mainmenu etc.).
  2. No use of shorthand, making the CSS bulkier than necessary.
  3. It uses positioning for no apparent reason.

Here is what I replaced it with:

  1. .menu,
  2. .menu ul {
  3. margin:0;
  4. padding:0;
  5. list-style:none;
  6. }
  7. .menu {width:200px;}
  8. .menu li {
  9. margin:0;
  10. padding:0;
  11. margin-bottom:1px;
  12. }
  13. .menu a {
  14. display:block;
  15. padding:2px 5px;
  16. color:#000;
  17. background:#b0c23d;
  18. text-decoration:none;
  19. }
  20. .menu a:hover {background:#d9dcb0;}
  21. .menu ul li {padding-left:15px;}
  22. .menu ul a {background:#ced174;}
  23. .hidden {display:none;}

I removed the font declarations since font-family and font-size are normally specified elsewhere in the CSS. While I was at it I also changed the colours to make looking at the menu a bit easier on my eyes, and added some padding and a bottom margin to the links.

The JavaScript

Finally a look at the JavaScript. The article uses the following (variable names translated from Swedish):

  1. function toggle(submenu) {
  2. if (document.getElementById(submenu).style.display == "none")
  3. document.getElementById(submenu).style.display = "block";
  4. else if (document.getElementById(submenu).style.display == "block")
  5. document.getElementById(submenu).style.display = "none"
  6. else alert("An error occured in the menu. Contact the web master.");
  7. }

The problems with this script are:

  1. It doesn't check if the browser supports the method it uses.
  2. It works by directly changing the element style.
  3. If anything should go wrong it displays a completely useless message.

Ok, I'll admit that the script does have an advantage over my replacement: it is a lot shorter than this:

  1. var toggleMenu = {
  2. init : function(sContainerClass, sHiddenClass) {
  3. if (!document.getElementById || !document.createTextNode) {return;} // Check for DOM support
  4. var arrMenus = this.getElementsByClassName(document, 'ul', sContainerClass); // Find all menus
  5. var arrSubMenus, oSubMenu, oLink;
  6. for (var i = 0; i < arrMenus.length; i++) { // In each menu...
  7. arrSubMenus = arrMenus[i].getElementsByTagName('ul'); // ...find all sub menus
  8. for (var j = 0; j < arrSubMenus.length; j++) { // For each sub menu...
  9. oSubMenu = arrSubMenus[j];
  10. oLink = oSubMenu.parentNode.getElementsByTagName('a')[0]; // ...find the link that toggles it...
  11. oLink.onclick = function(){toggleMenu.toggle(this.parentNode.getElementsByTagName('ul')[0], sHiddenClass); return false;} // ... add an event handler to the link...
  12. this.toggle(oSubMenu, sHiddenClass); // ... and hide the sub menu
  13. }
  14. }
  15. },
  16. toggle : function(el, sHiddenClass) {
  17. var oRegExp = new RegExp("(^|\\s)" + sHiddenClass + "(\\s|$)");
  18. el.className = (oRegExp.test(el.className)) ? el.className.replace(oRegExp, '') : el.className + ' ' + sHiddenClass; // Add or remove the class name that hides the element
  19. },
  20. /* addEvent and getElementsByClassName functions omitted for brevity */
  21. };
  22. // Initialise the menu
  23. toggleMenu.addEvent(window, 'load', function(){toggleMenu.init('menu','hidden');});

The replacement script works by adding or removing a class name instead of directly changing the style property of the sub menu elements.

The addEvent() and getElementsByClassName() functions are included in the full togglemenu.js script. If you use a JavaScript library that contains similar functions (they all do, probably) I suggest using those functions instead to avoid code bloat.

You can try it all put together on the Accessible expanding and collapsing menu demo page.

Caveats and some homework for the reader

Like I said at the beginning of this article, this has been done before. But hopefully showing the before and after versions of the HTML, CSS, and JavaScript will make it easier to see why I made the changes.

I should also note that this script does inherit a problem from the script published in the magazine: it disables the links to the top level category pages, preventing the user from navigating to them. That could be a serious problem depending on how the site is structured.

This script also does not handle multiple levels. Extending it to allow for that is no big deal, but I'd think twice before doing so. Remember that there will be a lot of links for people with JavaScript disabled to wade through.

I'll leave it as a learning exercise for the reader to alter the script so that it inserts a separate link for expanding and collapsing each sub menu and lets the top level links behave normally. And, if you want the functionality, add support for multiple levels.

Update (2007-05-30): As has been mentioned in a couple of comments, this kind of menu can also cause usability issues, making users confused when clicking a link does not open a new page. Be aware of that, and I would suggest at least styling the links in a way that makes it obvious that they contain hidden information.

Visit site to read or post comments…

Add 456 Berea Street to your Technorati favorites.

Posted in .

WCAG 2.0 Working Draft May 2007: A closer look

Mon, 2007-05-28 13:10

Since learning about the May 2007 update of the WCAG 2.0 Working Draft, which I wrote about it WCAG 2.0 Working Draft updated, I've been trying to find time to read the whole thing in order to be able to write a more thorough piece on it. However, because of the size of the WCAG 2.0 documentation I just haven't been able to get through it yet.

You don't have to wait until I manage to read it, understand it, and write something about my opinion on it though. Jack Pickard has already done that work, and presents his verdict in WCAG 2.0: Woeful to Wonderful in One Easy Draft?, an article split into three pages (WCAG is a large document, so commenting on it may require using quite a few words after all).

Judging by the result of Jack's assessment, the updated WCAG 2.0 Working Draft is a vast improvement over the Working Draft released in April 2006:

I was critical of WCAG 2.0 before, and it deserved that criticism. Now, I'm prepared to praise it, because it deserves that praise.

What I've read of WCAG 2.0 so far is definitely much better than the April 2006 Working Draft, but I'm not going to stand up and praise it before I have read it thoroughly.

Visit site to read or post comments…

Add 456 Berea Street to your Technorati favorites.

Posted in .

Is it time for CSS 2.2?

Fri, 2007-05-25 14:12

As most designers and developers who use CSS on a daily basis know, it's been a while since a new CSS Recommendation was released. There are several possible reasons for it taking so long, something Andy Budd brings up in CSS2.2.

While I agree that the W3C's process is way too slow, I don't think we can lay all the blame on the W3C. Browser vendors are a big part of the problem. Especially one of them (you-know-who).

Of course there are many neat features in CSS 3 that I would love to be able to use now, but just imagine for a moment that all browsers had full support for CSS 2.1. They really should, considering how long they have had to implement it. Don't you think that would be a great first step forward, and that it would open up for completely new approaches to designing with CSS?

I do. So I think the first thing is to get CSS 2.1 fully implemented. How to make you-know-who do that, I have no idea.

In the meantime, some people could form the 2nd Generation CSS Samurai and write a CSS 2.2 specification in the hopes that it will be adopted, or at least put some pressure on the W3C and browser vendors.

Visit site to read or post comments…

Add 456 Berea Street to your Technorati favorites.

Posted in .

Why styling form controls with CSS is problematic

Thu, 2007-05-24 14:40

As someone who has spent lots of time taking screenshots of various CSS applied to form controls, I know that styling form controls consistently across browsers and operating systems is impossible. If you don't know what I'm talking about, have a look at Styling form controls, Styling even more form controls, and Styling form controls with CSS, revisited.

In the discussions following those articles (and any article that discusses styling of form controls), there tends to be a bit of frustration expressed by designers who feel that being able to specify exactly how form controls appear in a graphical browser is necessary for their design to work well. I really cannot agree with that. In my opinion form controls should be left mostly alone (though some light styling may be acceptable) in order for the user to quickly recognise them for what they are.

Regardless of whether or not you agree with my opinion on this you will probably find Eric Meyer's article Formal Weirdness a good read. In the article, Eric explains some of the technical reasons for form controls being so hard to style consistently across platforms with CSS. He also asks a lot of good questions related to how various CSS properties should affect form controls if browsers would let them.

Eric wrote the article as a follow-up to his series of articles about resetting the default CSS in browsers in order to get a consistent, cross-browser base stylesheet. His reset stylesheet evolved over several articles, and the final version is described in Reset Reloaded. Several people posted comments asking why Eric did not use a universal selector to set the margin and padding properties of all elements to zero. Eric's response to that is that he avoided doing so because of the problems it causes for form controls.

After reading Formal Weirdness I think you will understand why.

Visit site to read or post comments…

Add 456 Berea Street to your Technorati favorites.

Posted in .

Creating bulletproof graphic link buttons with CSS

Tue, 2007-05-22 15:08

A CSS problem I have been wrestling with lately is how to create a bulletproof shrinkwrapping graphic button. By that I mean an image-based button that will expand and contract to fit the amount of text it contains. It is a very useful technique for CMS-driven sites that allow the client to change the text that is displayed on buttons, as well as for multilingual sites.

A successfull bulletproof image-based button should:

  • Automatically grow horizontally to fit any amount of text
  • Grow horizontally and vertically if text size is increased or if the text wraps to multiple lines
  • Retain its appearance within reasonable limits
  • Be able to have rounded (or other non-square) corners
  • Have no unclickable areas
  • Be readable when images are disabled

Doing this with CSS may sound easy at first, but there are several things to be considered that make it a little tricky. I also ran into several browser related problems before ending up with the solution described in this article.

Before I go on to describe the problems and the solution, I'd like to give you a friendly reminder that this technique should not be used to mindlessly replace real form buttons.

A real form button (normally <input type="submit">) works equally well without CSS and JavaScript. A styled link, however, can only submit a form through the help of some JavaScript. And since You cannot rely on JavaScript being available. Period., you need to take that into account. What I did in the project I came up with this solution for was to use JavaScript to replace the input element with one of these buttons. That way, if JavaScript is not available, the user can click a real button instead.

OK, let's move on. The problems, as always when it comes to CSS, occur in Internet Explorer. The first problem is one that I'm not actually sure whether it is a bug or just a different implementation of the specification. I mentioned the problem in Transparent custom corners and borders, version 2, and it consists of IE having problems figuring out the width of block level elements inside floated elements with no width assigned.

When a floated element has no specified width, the browser should adjust the element's width to fit its content – shrinkwrapping. In the case of a floated link acting as a button, that would come in handy as it will make the link automatically adjust its width depending on the amount of text it contains. Sadly, Internet Explorer will not properly shrinkwrap the following combination of HTML and CSS I use for the Transparent custom corners and borders technique.

  1. <div class="cb">
  2. <div class="bt"><div></div></div>
  3. <div class="i1"><div class="i2"><a href="#" class="i3">A box</a></div></div>
  4. <div class="bb"><div></div></div>
  5. </div>

When div.cb is floated without a specified width, it will shrinkwrap to fit its content. The width depends on the content of div.i1, and that part works fine. The problem is with the div.bt and div.bb elements, which don't expand to the same width as div.i1. That kind of makes sense, since they are empty siblings of div.i1. A workaround is to give div.cb a width, but I really needed shrinkwrapping buttons, so I had to to come up with something else.

I ran into the second problem while exploring other solutions and experimenting with absolutely positioning the corners. That actually worked, except for a one pixel gap that showed in IE at certain sizes, probably due to a rounding error.

What I ended up with is a link that contains four nested span elements. Here's the markup for a button (nicely indented here for readability):

  1. <a class="button" href="#">
  2. <span>
  3. <span>
  4. <span>
  5. <span>Shrinkwrap me!</span>
  6. </span>
  7. </span>
  8. </span>
  9. </a>

Yes, I know. That is a lot of span elements. It isn't pretty, but I can't think of a better way to achieve this effect until more browsers than Safari support Layering multiple background images. In a production situation I would use a script similar to the one in Transparent custom corners and borders, version 2 to remove the span elements from the markup. The end result is the same to the browser, but the markup is cleaner for us humans who have to edit it.

And of course we need a bit of CSS to turn that markup into a nice-looking button:

  1. .button { /* Top left corner, top edge */
  2. float:left;
  3. color:#ddd; /* Text colour */
  4. background:#333 url(button.gif) no-repeat; /* Fallback bg colour for images off */
  5. font:1.2em/1.0 Georgia,serif;
  6. text-decoration:none;
  7. }
  8. .button * {display:block;}
  9. .button span { /* Top right corner */
  10. padding:6px 0 0;
  11. background:url(corners.gif) no-repeat right top;
  12. }
  13. .button span span { /* Bottom left corner, left and bottom edges */
  14. padding:0 0 0 6px;
  15. background:url(button.gif) no-repeat left bottom;
  16. }
  17. .button span span span { /* Bottom right corner */
  18. padding:0 0 6px;
  19. background:url(corners.gif) no-repeat right bottom;
  20. }
  21. .button span span span span { /* Right edge */
  22. padding:3px 12px 3px 6px; /* Extra padding (3px vertical, 6px horizontal) added to give the text some breathing room */
  23. background:url(button.gif) no-repeat right center;
  24. }
  25. .button:hover,
  26. .button:focus,
  27. .button:active { /* Help keyboard users */
  28. outline:2px solid #ff0; /* Not supported by IE/Win :-( */
  29. color:#fff;
  30. }

You'll need to adjust the paddings to fit your image if it has wider or narrower corners and edges.

Oh, I made a demo page that contains a few buttons.

Ideally, this kind of button would be accomplished by using a single background image, so that was my initial goal. I did not manage to reach that goal, however, but had to resort to using two images. The two images are button.gif and corners.gif. If you take a look at them, you will notice that they are much larger than any button should become since they define the maximum size of the button. If you need even larger buttons, just increase the size of the images.

Note that corners.gif is transparent except for in the corners. That way it can be used to put each corner in place without covering any of the other corners.

And that's it. A bulletproof, resizable, shrinkwrapping graphic button.

There are a few caveats though:

  • The corners can't be transparent
  • It may be tricky to give the button a non-flat background colour
  • It only expands as far as the size of your image

To me, those are completely acceptable drawbacks in most situations.

So, does anyone see how this could be further improved?

Update (2007-05-23): The class attributes on the span elements are gone. I was sure I had a reason for using classes instead of descendant selectors at the time, but I can't see the need for them now. So no more classitis, and a little leaner markup.

Visit site to read or post comments…

Add 456 Berea Street to your Technorati favorites.

Posted in .

WCAG 2.0 Working Draft updated

Mon, 2007-05-21 15:30

On April 1 I posted WCAG 2.0 released today, which of course was an April Fool's joke. But this time I am not joking: WCAG 2.0 has been updated, and a new WCAG 2.0 Working Draft was published on May 17.

The Web Content Accessibility Guidelines Working Group (WCAG WG) received a lot of criticism (read To Hell with WCAG 2 for details) after publishing a Last Call Working Draft of WCAG 2.0 in April 2006, and appears to have listened to much of the feedback from the accessibility community.

A rundown of what has changed since the previous version is available in Summary of Issues, Revisions, and Rationales for Changes to WCAG 2.0 2006 Last Call Draft.

I have not read the whole thing yet. WCAG 2.0 has been shortened and reorganised, but it still consists of a number of documents, some of which are quite long. It will be a while before I have time to read all of it. I have read the summary of changes and skimmed through the Techniques for WCAG 2.0 document though, and what I've seen so far really seems like an improvement.

Like last year, the WCAG WG has issued a Call for review and is accepting comments on this Working Draft until June 29. Instructions for Commenting on WCAG 2.0 Documents are available.

Visit site to read or post comments…

Add 456 Berea Street to your Technorati favorites.

Posted in .