Christian Heilmann

Posts Tagged ‘css’

Creating accessible JavaScript menu systems – some basic steps that get forgotten

Tuesday, June 23rd, 2009

OK, so you want to write yet another collapsing menu/section/widget thingamabob. This is really easy and in the following few examples I want to point out some ideas that make it much easier for you.

The following is what we’re going to build – it doesn’t have any bells and whistles like smooth animation, random unicorns, swoosh noises or whatever else you might want to come up with, but it is a good solid base to work from.

Collapsing menu example by  you.

Click the image to go to the live example.

The Markup

The first trick is to use easy to understand and highly styleable markup for your widget. You see a lot of widgets that use endless DIVs, SPANs, IDs on every element and classes on every sub-element. If you build a simple solution (and not a catch-all reusable widget that is part of a framework) none of that is needed. In this case the markup is the following:


In other words: a nested list with headings for the different sections. This makes sense without styling or scripting which is still and important point considering mobile devices or environments that have JavaScript turned off (and yes, they are not mythical but do exist).

Assistive access does not mean lack of JavaScript

The first mistake that people do is to think that this is all you need to be accessible – as users with assistive technology have no JavaScript, right? Wrong. Assistive technology is not a browser replacement but in most cases hooks into browser functionality and offers easier access. In other words, what the browser gets the assistive technology gets and you have to make sure it still makes sense without a mouse (to name just one example).

Thinking too complex

The next extra step people take is starting to loop through the menu construct to hide the elements with JavaScript. This is made much easier with clever libraries that don’t use the DOM to do that but piggy-back on the CSS selector engine, but for good old IE6 this is still not an option. And you don’t need to. In order to hide the nested lists in the above example all you need to do is to apply a class to the main list and leave the rest to CSS:




This means that in order to show any of the nested lists, all you need to to is to add a class called “open” to the parent list item. The CSS to undo the hiding is the following:



You can add the classes on the backend when you render the page – which makes a lot more sense to indicate a current open section of the page than any JavaScript analyzing of window.location or other shenanigans. See the first stage demo. You can also define a “current” class which means you can style it differently and the script will simply quietly skip that section and not collapse it at all.

That is the beauty in leaving all the showing and hiding to the CSS. Of course you can’t animate in CSS (unless you use webkit), but you can make your animation precede the change in classes.

Hiding and showing

So the way to show and hide things is simply to add and remove classes. We need to use event handling to listen for click events to do that. Click events are the only safe ones as they fire with keyboard and mouse. The problem is that a header can be made clickable, but is not keyboard accessible. To work around this, let’s use DOM scripting to inject a button element into the headers. You can use CSS to make it look not like a button:



The second stage demo shows how this works and here’s the code.


(function(){

// get the menu and make sure it exists
var m = document.getElementById(‘menu’);
if(m){

// add a class to allow the CSS magic to happen
m.className = ‘js’;

// loop over all headers
var headers = m.getElementsByTagName(‘h3’);
for(var i=0;headers[i];i++){
// get the content of the current header
var content = headers[i].innerHTML;
// create a button, delete the content of the header
// replace it with the button and set the content to
// the cached header content
var a = document.createElement(‘button’);
headers[i].innerHTML = ‘’;
headers[i].appendChild(a);
a.innerHTML = content;
}

// apply a single click event to the menu
m.addEventListener(‘click’,function(e){

// find the event target and chec that it was a button
var t = e.target;
if(t.nodeName.toLowerCase()===’button’){

// get the LI the button is in
var mom = t.parentNode.parentNode;

// check if its class name is not content and
// remove the open class if it exists, else
// add an open class

if(mom.className!==’current’){
if(mom.className = 'open'){
mom.className = '';
} else {
mom.className = 'open';
}

}

// don't do the normal things buttons do
e.preventDefault();
}

},true);
}

})()

This code also takes advantage of event delegation – there is so no need to apply and event handler to each heading – even if that is really easy to do with a library and an each() or batch() command. Event handling is a great concept and the tricks you find when you bother to read the docs are staggering.

Hiding is not removing

This is where most menu scripts stop. Great stuff, it expands and collapses and everybody is happy. Unless you are an unhappy chap and you need to use a keyboard to access it – or an older mobile device. Try it out yourself – use the second example by tabbing from link to link. You’ll find that you have to tab through the invisible links to reach the next section to open or hide. That surely can’t be the idea, right?

The trick to work around is to change the tabindex property of an element. A value of 1 will remove it from the tabbing order and setting it back to 0 will make them available again. So, to make keyboard access easier, let’s remove the tab order upfront and reset or remove it when we show and hide the menus. This is terribly annoying as we have to loop through the links. I hate loops, but browsers are not our friends.


(function(){

// get the menu and make sure it exists
var m = document.getElementById(‘menu’);
if(m){

// add a class to allow the CSS magic to happen
m.className = ‘js’;

// loop over all headers
var headers = m.getElementsByTagName(‘h3’);
for(var i=0;headers[i];i++){
// get the content of the current header
var content = headers[i].innerHTML;
// create a button, delete the content of the header
// replace it with the button and set the content to
// the cached header content
var a = document.createElement(‘button’);
headers[i].innerHTML = ‘’;
headers[i].appendChild(a);
a.innerHTML = content;
}

// loop over all nested lists
// and set the taborder of the nested links to -1 to
// remove them from the tab order

var uls = m.getElementsByTagName(‘ul’);
for(var i=0;uls[i];i++){
if(uls[i].parentNode.className!‘open’ &&
uls[i].parentNode.className!==’current’){
var as = uls[i].getElementsByTagName(‘a’);
tabOrder(as,-1);
}

}

// apply a single click event to the menu
m.addEventListener(‘click’,function(e){

// find the event target and chec that it was a button
var t = e.target;
if(t.nodeName.toLowerCase()===’button’){

// get the LI the button is in
var mom = t.parentNode.parentNode;

// check if its class name is not content and
// remove the open class if it exists, else
// add an open class. Also, remove or add the
// links to the tab order

if(mom.className!==’current’){
if(mom.className = 'open'){
mom.className = '';
tabOrder(as,-1);
} else {
mom.className = 'open';
tabOrder(as,-1);
}

}

// don't do the normal things buttons do
e.preventDefault();
}

},true);
}

// remove from or add elements to the tab order

function tabOrder(elms,index){
for(var i=0;elms[i];i++){
elms[i].tabIndex = index;
}

}
})()

The third demo does exactly that – use your keyboard and marvel at being able to tab from parent element to parent element when sub-sections are collapsed.

Fixing it for Internet Explorer 6 and 7bad old browsers

Now, this is all good and fine but of course we need to fix the code to work around the quirks of browsers that don’t understand the W3C event model or consider an element with a name attribute the same as one with and id. We could fork and fix in the code we have here, but frankly I am tired of this. People give us libraries to work around browser differences and to make our lives easier. This is why we can use YUI for example to make this work reliably cross-browser:



Good start

This is just a start to make this work. A real menu should also have support for all kind of keys that a menu like this in a real app gives us and notify screen readers of all the happenings. For this, we need ARIA. Right now, I’d be happy to get some comments here :)

TTMMHTM: Yarn about CSS on mobiles with free music (or summat)

Tuesday, May 26th, 2009

Things that made me happy this morning:

So how do you add alternative text to background images?

Wednesday, February 25th, 2009

One thing that drives me up the wall is the inertia that happens in the accessibility world. We do our best to keep the web development world aware of accessibility concerns but in the other direction I just don’t see much drive to even understand the basic principles of web design.

My favourite example is the question that crops up almost every 3 weeks on different communication channels:

The WCAG guidelines state that every image needs alternative text but we are using CSS background images a lot [Ed: sometimes this question is also about CSS sprites] – how do you define alternative text for background images?

OK, here is the scoop, people:

CSS background images which are by definition only of aesthetic value – not visual content of the document itself. If you need to put an image in the page that has meaning then use an IMG element and give it an alternative text in the alt attribute. You can also add a title attribute to add extra information that will be displayed to every user as a tooltip. If your image has a lot of content (for example a graph) then consider using the longdesc attribute to link to a textual representation of the same data or display the same data for example as a data table in the same document.

That is it – images in CSS are only visual extras, not page content, hence they never need alternative text. “Rounded corner” or “blue-yellow gradient” does not help anybody as alternative text – on the contrary, it annoys the end users.

It is not rocket science, really!

People I’d like to see on stage more: Nicole Sullivan

Sunday, January 18th, 2009

This is a new series of posts I am starting, tying in with things I’ve been saying in presentations and at interviews lately: I think it is time we mixed the speaker circuit up a bit and hear from different people than the “rock stars” of web development.

In this series I will talk about people I very much enjoy following and had great experiences witnessing as presenters or colleagues. Hopefully it’ll inspire some conference organizers to consider them and you to have a lookout for them.

First up is Nicole Sullivan, which is the name of her real life mild-mannered self.

On the web she is known as Stubbornella and writes a lot about performance, image optimization, CSS maintenance and other things that are both highly technical and very related to the grey area of web development that is the blurry border between design and engineering.

nicole sullivan

Nicole is right now writing on a book about image optimisation together with Stoyan Stefanov one of her partners in battling those extra bytes clogging the web. Together they built Smushit, a ridiculously useful tool to optimize your images without changing their visual quality. She used to work in Yahoo in the exceptional performance team and was a large part of the research team that brought the best practices for images and CSS when it comes to performance.

Nicole gives presentations both in English and French and has a wonderfully pragmatic approach to her work. While a lot of performance presentations can be highly technical, academic or deal with edge cases, Nicole keeps her “down in the trenches web developer” hat firmly on that curly head of hers and gives advice you can use immediately and get a result for your sites. Want proof? Check out this video of her speaking at the internal Yahoo developer summit:


Nicole Sullivan: "Design Fast Websites" @ Yahoo! Video

I’ve seen Nicole several times, been with her at Paris Web and The Ajax Experience and can safely say it’ll be cool to have her share her wisdom with more people out there.

Will a new browser war help web innovation?

Friday, January 2nd, 2009

Aircraft in formationI just spent an hour on the cycle in the gym watching the video of Douglas Crockford’s Web Forward presentation on my iPod touch. Douglas makes some great points about the state of the current technology for the web – especially browsers – being counterproductive to innovation.

I agree with all of what Douglas says (especially the security aspects of JavaScript and the need for vats), but I am not too sure about the notion at the end that we need another browser war to go forward.

I understand Douglas’ point about browser vendors and users knowing what they need, but I also see a big danger in allowing the way we work on the web to become multi-track once again. I worked through the first browser wars, and I am thoroughly sick of having to write code to work for one or the other browser. This is why we use libraries to work around these issues. The thing that is a bit academic about the view that browser vendors could fuel innovation by navel-gazing is that the end users are not really going to upgrade their browsers just to make our lives easier. Even serious security flaws don’t really get people to upgrade their browsers (I am not talking about us geeks, I am talking about offices and home users that just want to read their mails and get the news). We can innovate until the cows come home, but if it doesn’t reach the people we work for this is progress that makes us move away rather than forward.

I agree with Douglas that the W3C standards are a failure when it comes to innovation. For starters they haven’t moved in ages and the standards are not nearly as good as they should be to make us work efficiently. The DOM standard is too complex, HTML does not really provide what we need to describe interfaces and interaction and CSS is not the layout engine it could be and we need to hack with positioning and floating just to get a multi column layout.

You have to cut the W3C some slack though – if browser vendors hadn’t concentrated on putting bespoke functionality in browsers and followed the guidelines we’d have had a much easier life as web developers in the last few years and could have concentrated on working with the W3C to get the standards extended. This has improved immensely in the last years and even the biggest evildoers now got the CSS2 specs supported in the 8th revision of their browser. Communication is happening, the problem is speed.

The process of the W3C is academic and broken, I do very much agree with that. The WHATWG are kicking butt left right and center with the HTML5 specs and got a good gig going working with browser vendors to get support for what they do. I think this is a great approach and seeing that the W3C is now looking at HTML5 in favour of the overly complex XHTML shows we are moving in the right direction.

What I lack in the proposals of innovating with techies is that a standard is much more than how it works technically. This is what we have already done in the first browser wars: we coded to make it work. It bit us in the butt a few years later as what we built was either flaky and broke or bloated and full of hacks that are not needed any longer (I doubt you’d ever need a if(document.layers){} these days).

Web Development is a very frustrating and complex job. Simply making things work to me is not enough – it needs to work, be usable and easy to understand for developer who take over from you. Hacks and browser specific solutions are the opposite of that.

To me, pragmatic development means “keep it easy to understand”, not “make it work in all browsers” as “all browsers” is a very moving target. The danger we are running into right now is that we are looking at (bleeding) edge cases and see them as innovation and great pragmatic ways of working. I am a big fan of performance tweaking and saving bytes wherever we can. However you can overdo that. As Dustin Diaz explained Google are using as their doctype to save on some bytes and David Calhoun proved that it is working across the browser board right now. Fine and in the case of Google or Yahoo this does make quite a difference. However, a DOCTYPE is not only there to trigger standards mode – this is a nice side-effect. Its purpose is to tell user agents (and that is more than a browser) what the document is, how it is structured and what elements are allowed in which hierarchy. If you wanted to convert a document with this “skinny doctype” you are in trouble as the conversion tool has to hope that all is fine and dandy. Systems like Yahoo Pipes or YQL are a great way of getting data from the web and re-using it. If the data we put out on the web is not in a format we can rely on being valid, this data is unavailable.

I like to see the web as a pool of semantic and linked information, not as a collection of documents that render correctly.

At least one thing is for sure: this year will be interesting in terms of innovation and how we build for the web.

Check out Douglas’ video:


Douglas Crockford: "Web Forward" @ Yahoo! Video

(I am tempted to add VNV Nation’s Darkangel as the ambient soundtrack)