JavaScript vs. Usability

The principle of Do No Harm

JavaScript isn't necessarily evil - it can be an awesome tool for enhancing the usability of a website - just make sure that you aren't doing harm by using it.

This article looks at the use of JavaScript in web application development, and demonstrates what is meant by ensuring that a script "Does No Harm".

Introduction

JavaScript has a bad rap. No, really. It does. Everyone is used to the "YOU MUST HAVE JAVASCRIPT ENABLED TO VIEW THIS SITE" vs. the "JAVASCRIPT IS A SECURITY VULNERABILITY! DISABLE IT" type things we see on so many websites today. The shame of the whole thing is that JavaScript can provide an incredible boost to website usability - so long as it is done properly.

"Properly" he says

Far be it from me to cast nasturtiums (and other flowery substances) but JavaScript gets abused. A lot. Because of this, and its bad reputation, we can't rely on it to do anything important. We simply can not assume it is there! People disable it, people use old browsers, people use browsers that don't support JavaScript right, people use browsers that don't have JavaScript support at all. Oh, and you have to cater for all of them. Sure, sure... Slap a "This page best viewed in Mozilla Firefox" at the bottom and everything is OK. Yup. Good work... 80% of the browsing public now don't get that.

JavaScript is the ultimate boon to usability, but we have to use it sensibly

Principle of "Do No Harm"

The principle of "Do No Harm" basically says if you are going to do something, make sure it doesn't do anything bad first. Wow, thats a complex summary of the idea. But how does this apply to JavaScript? Simple.

If your site doesn't work without JavaScript, you are doing harm.

There, I've said it. If you rely on JavaScript to verify your fields and it won't work without it - You are doing harm. If you rely on JavaScript to hash your passwords on the client, and your login won't work without it - You are doing harm. If your site uses links that require JavaScript to do some odd POST back to the page - You are doing harm.

Sure, some of these features are really useful. I for one use the idea of Salted MD5 hashed passwords rather than plain text passwords all the time. Problem is, if they don't have JavaScript, they have a problem. More correctly, YOU have a bug. Reliance on JavaScript is not a feature - it is a bug.

The MD5 Example

We shall examine the MD5 password example to further explain the Do-No-Harm philosophy.

The Harmful MD5 Example

Looking at the example of the MD5 hashed password, it is easy to see what I mean.

JavaScript MD5 Example

<script>
//Hash the password field
function hashPassword(form)
{
form.password.value=md5(form.password.value);
}

//MD5 function placeholder
function md5(text)
{
//MD5 LOGIC HERE
}

</script>
<form onSubmit="hashPassword(this)">
    <p>Username:<input type="text" name="username" /></p>
    <p>Password:<input type="password" name="password" /></p>
    <p><input type="submit" value="Login" /></p>
</form>

The idea of this technique is to get the browser to MD5 hash the password before sending it across the wire. This protects the password (it's no longer sent in plain text) and by salting the hash (not shown here), we can help to prevent a sniffed password from being re-used by an attacker. So thats great. We now have a more secure login system that's incredibly easy to implement. But what happens if JavaScript isn't enabled or the browser doesn't handle it correctly?

It don't work. It's that simple. If JavaScript isn't enabled the plain text password is sent to the server, which is expecting a MD5 hashed password. That's not going to work. Ideally, we want to accommodate the plain text password, but leave a note to the user letting them know that it's not as secure as it could be. We also need to let the server know that the password isn't plain text.

Just to make things worse, I've seen implementations where the "submit" has been replaced with a "button" and an onClick event used to hash the password and fire off the submit() for the form. Good work, now, if JS isn't available, not only are you stuck with a form that isn't submitting the correct data, but it's not submitting at all!

The Good MD5 Example

Following along the "Do No Harm" ideas, let's modify our example to work even in browsers where JS is not available. Do-No-Harm JavaScript MD5 Example

<script>

//Hash the password field
function hashPassword(form)
{
form.password.value=md5(form.password.value);
form.hashed.value=1;
}

//MD5 function placeholder
function md5(text)
{
//MD5 LOGIC HERE
}

</script>
<form onSubmit="hashPassword(this)">
    <noscript>
    <p>Your browser does not support JavaScript, and therefore your password will be sent in clear text. To use a more secure method, please enable JavaScript or use a newer browser.</p>
    </noscript>
    <p>Username:<input type="text" name="username" /></p>
    <p>Password:<input type="password" name="password" /></p>
    <input type="hidden" name="hashed" value="0" />
    <p><input type="submit" value="Login" /></p>
</form>

Here, we can see that if the browser supports JavaScript, then the password will be hashed, and a value sent to the server to inform it that this is the case. However, if the browser does not support JavaScript, the hard-coded value is sent to notify the server that the password is plain text, and displays a warning to the user. The login works regardless of whether JavaScript is enabled or not, but those using JavaScript get an enhanced login.

Another Example - Anchors

Now, we'll look at a more usability based example. One of the recent scripts I wrote was to hilight the current anchor that was being viewed in a page.

This came about because on some pages where you were jumping to an anchor at the bottom of the page, it was not possible to tell which item you were actually being directed to. As such, this script changes the class on a arbitrary parent containing the current anchor.

This script uses the principle of Do-No-Harm. If the JavaScript is not enabled, the page still jumps as per usual, just the difficulty remains working out which item. Decent naming and using titles on the items fixes this. If JavaScript is enabled, however, then the page has been made slightly more accessible. Sure, we could make a system that works all the time by using some server side include or other server based mechanism, but that has a few drawbacks :

  1. Another hit to the server for information on the same page, causing refresh delay, wasted bandwidth and wasted CPU load.
  2. More logic required in the server code JUST for a visual enhancement. Why are we bothering with CSS again?
  3. If the page structure changes, this may require substantial modification to server code. This method requires only a change to the JavaScript library. After all, if we're going to do that, why bother with JavaScript at all?

By implementing this usability enhancement, we enrich the users experience. Obviously, similar examples could be made with the Nice Titles script for example.

Last Example - Popups

Popups are another tool which also get a bad wrap because of their abuse, (but that's an entirely different argument, and an entirely different article) but one thing is certain - they are often implemented poorly.

Ideally, popups are a usability enhancement - they can suffice in the same window if need be, but ideally you don't want them to (yes, there are exceptions, as there always are). With xHTML 1.0 Strict, the target attribute on links have been deprecated (presumably, they are still available in the Frameset DTD??) and so it's no longer even possible to use the older (and horribly abused) technique of target="new" or "blank" or "_foo" or any other random non-standardised name you can come up with. But lo, a better way exists!

Do-No-Harm JavaScript Popup Example

<a href="/articles/js_usability" onclick="window.open(this.href);return false;">Hey! It's a link!<a>

It's that simple... There's no redundancy (this.href takes care of that). The page opens in a new window if JS is enabled, or opens in the current window if it doesn't. Oh, and did I mention this technique doesn't break tabbed browsers like the old JS-only technique of href="#" does?

Conclusion

I hope that in these few examples it is clear to see why the principle of "Do-No-Harm" is essential when working with JavaScript in any situation. It is essential not to break functionality just for the sake of an enhancement, be it security, usability or functional. If your site REQUIRES JavaScript to be enabled to work, I would suggest that you reconsider its use and ensure that it Does No Harm for the people that don't have it.