Christian Heilmann

The future is hybrids – how JavaScript can purify pure CSS solutions

Monday, September 4th, 2006 at 8:19 pm

Over the last few months there were more and more requests on mailing lists and articles published how you can achieve functionality with pure CSS that was traditionally achieved with JavaScript. This post will explain why that is an interesting concept, but hopefully make you aware of the benefits of JavaScript enhanced solutions versus pure CSS solutions.

I have to thank a lot of people I asked in the research for this, foremost James Edwards, John Resig and Andrew Dupont. I’ve also given a short presentation – PDF -96KB on the same topic at the Barcamp in London last weekend.

First of all let’s investigate how pure CSS solutions became so popular.

In the days before the CSS revolution

Back when CSS was not properly supported in browsers, JavaScript was the only option to achieve effects that are commonly described as behaviour.

These could be a simple rollover effects over images and text that indicate that these elements are clickable right up to clever forms that show and hide fields that are logically connected (for example showing a second address field set when you select more than one applicant for a mortgage).

JavaScript support was not too hot either, and different implementations in different browsers
lead to convoluted code that made maintenance a nightmare as you had to change things in several places whenever there was an amendment to be made.

It just felt wrong that you had to learn a different language and know how to avoid mistakes implementing it just to achieve simple effects. Furthermore it is a pain to re-visit these implementations whenever a new browser showed up and the earlier script didn’t take that into account but tested only for certain browsers.

CSS superheroes to the rescue

When browsers finally clued up about CSS it was amazing. Things that meant hours of work – like changing the look and feel of an entire site – turned into a matter of minutes, and our HTML documents became a lot smaller without all the background attributes and font tags. A lot of clever developers started seeing this new web development technology as an option to tackle the problem of convoluted JavaScript for simple effects. Probably the first was Eric Meyer who showed at his experimental web site CSS/Edge what can be done with CSS when you push it to its limits. The real breakthrough though was probably Pixie’s rollover solution which finally made it possible to discard the horrible rollover scripts that were in use before that.

CSS - the one trick pony

The part of CSS that makes effects like these possible is pseudo classes. These define styles applied to an element when something happens to it. For example you hover over it with the mouse cursor, or you set the focus to it with your keyboard. The snag is that browser support right now is still flaky, especially in MSIE 6 which only allows for hover effects on links. This does actually make sense, as they are (apart from buttons) the only clickable elements in browsers. A hover effect was originally meant to be an indicator that what you are pointing your mouse at is in fact leading to someplace else or triggers a certain functionality.

A problem of scope

Regardless of the level of support for pseudo selectors, the biggest problem of CSS-only solutions is scope. You can only affect elements that are nested inside the element you define the hover style for. This, together with MSIE 6’s support issues limits you to inline elements for your CSS-only effects, unless you choose to create invalid HTML. Furthermore pseudo selectors don’t add to the specificity of a CSS selector, which can make it very tricky to define different styles.

A problem of accessibility, semantics and content

The necessity to nest all the elements affected by the hover styling also brings up the issue of altering the HTML and content to cater for an effect. While it is rather cool to see a link called “Products” and a styled tool tip saying “Check our range of toys, books and apparel for men and women” when you hover over it, it may be less fun having to listen to “Products Check our range of toys books and apparel for men an women” or see it when you don’t have CSS enabled or available. After all, you only wanted to know which link leads to the product page.

Assistive technology like screen readers allows you to quickly navigate through the document with a listing of all the links in the document. If all your links are that long – or even worse encompass whole paragraphs of text – this very useful feature becomes a pain to use.

Summary of CSS-only solutions:

In essence, CSS-only solutions were a good exercise to push the limits of CSS and advertise the necessity for browsers to support the W3C recommendations. However, for real “behavioural” solutions, CSS is simply not the correct technology:

  • You cannot test properly with CSS – there is no conditional syntax, instead you need to rely on browsers and visitors being able to understand and use your solution or get an unusable page.
  • You are at the mercy of the browser when it comes to keyboard support. While the focus pseudo class offers you the option to trigger a style application via keyboard, browsers only allow keyboard access to links and buttons. You also only allow for keyboard support for web pages, not for applications (more on that later).
  • With links being inline elements you can only nest other inline elements in them, and you may make it a real pain for users relying on assistive technology to use your site.
  • You can use conditional comments to only target MSIE 6 with the extra markup necessary for some effects, but that means mixing structure with presentation – in essence you do the same we already didn’t like about older JavaScript solutions: You add a lot of markup to achieve an effect.
  • CSS behaviour is a fixed state – either you are hovering over the element and you show the nested one or you are not. There is no way to allow the user to make a mistake or move the mouse slightly without losing the extra information or – in worse cases – collapse a whole menu.

JavaScript to the rescue

In order to counteract these shortcomings, you will need to have a CSS/JavaScript hybrid solution: JavaScript to provide the behaviour functionality and CSS the look and feel. This will also make it possible to re-style a menu or an effect without needing to know JavaScript.
Notice that we are talking about modern DOM scripting here, that works unobtrusively and follows the idea of progressive enhancement. It is easy to find examples of scripts that don’t help the cause, so please let the sins of the past be behind us.

Things JavaScript can do that CSS can not:

Real progressive enhancement

JavaScript is a programming language; you have conditionals (if statements), loops and you can test if the current user agent supports a technology before you apply it. The classic example of that was already in use in good image rollover scripts: if (document.images){ } made sure that only browsers with images enabled got a rollover effect. Other examples are testing for DOM support and if there is a certain element before trying to apply a certain class to it.

Reading and testing environments

As JavaScript has access to the browser window properties you can check the dimensions you have at your disposal before showing and hiding several elements. You can also recognise changes in the user agent (like resizing the window, or even the font size – by using a trick) and react to them – for example re-position a menu.

This is extremely important with multi level dropdown menus. A pure CSS multi level dropdown menu will blindly open new sub-menus to the left (unless you hardwire the x-th level not to do so), while a JavaScript powered menu can test if the next level menu fits the screen. If there is not enough space to the left, it can show it on the right and if there is not enough space downwards, it can open the next level above the current one. This is how real application menus work like.

Removal and generation of elements

By tapping into the DOM, JavaScript can add and remove elements from the document. CSS can only show and hide elements and hope that the user agent will not make them available. JavaScript can also generate superfluous elements (for example for flexible rounded corner solutions or drop shadows) without you having to add them to the document by hand.

Time based execution

JavaScript can use timing to trigger an effect only after a certain amount of time. As humans are prone to error, this is extremely handy when it comes to making menus and effects more usable and accessible. Instead of hiding a menu when the user leaves the parent element, you can wait a certain amount of time and then hide it. This allows for a lot more erratic mouse movement while still keeping the menu available.

Richer event handling

While the support for pseudo selectors in CSS is still not as good as it could be, the JavaScript equivalent – event handling – has been supported for several generations so far. You can react to almost anything: keys being pressed, the mouse moved, the mouse keys being pressed or released, and – by using a certain trick – you can even come up with your own custom events.

JavaScript can also react to events that are not the user’s doing – like the document being read, another document getting loaded, an element becoming available or the window being moved or closed.

Real keyboard support

While CSS can only apply styles to an element that has the focus (which could have happened either by keyboard or mouse) JavaScript can use several keyboard event handlers and even recognize which key is currently pressed.

This enables you to create menus that work like real application menus. While the normal “web way” of navigating through a list of links via keyboard is to jump from link to link via Tab (or the “A” key in Opera), real application menus like the Windows start bar or any dropdown in the browser menu can be navigated with the arrow keys.

Newer versions of Opera and modified versions of Firefox also allow for this kind of navigation and Sun has cooperated with Mozilla to come up with a reusable library of DHTML widgets that follow these rules. James Edwards also created a DHTML menu that works the same way.

On demand pulling of content

While CSS can only deal with what is available in the document when it is loaded (the exception being browsers that support the :after pseudo selector that allows to generate text or media – albeit not styleable) JavaScript can create new content, but also pull in data from other resources like Databases or files with Ajax.
You can for example offer visitors without JavaScript a very basic menu and allow others to pull in the whole site map and load the different page content without reloading the whole page.

The arguments against JavaScript

The main (and in a lot of cases only) argument against JavaScript is that it can and is turned off by a lot of visitors. While this is also to a lesser extent possible for CSS - a few visitors will have a user style sheet instead of yours applied to your web site.

The other argument is that CSS is a lot easier to learn and it is easy to mess things up in JavaScript when you don’t know what you are doing. The question however is that if the benefits of JavaScript are worth at least learning how to use it. A few years ago there weren’t many good and easy JavaScript tutorials – by now however there are a lot.

The big question

In summary, there is one big question you should ask yourself: what is a better solution:

  • A solution that doesn’t even show up for the visitor when something is turned off or
  • a solution that may not be usable without you being able to do anything about it as the technology it was built on was never meant to be used for application development.

Scripting best practices like Unobtrusive JavaScript, Progressive Enhancement and DOM Scripting ensure that you get the best possible behaviour and you are still able to fully style the application via CSS.

Share on Mastodon (needs instance)

Share on Twitter

My other work: