Capturing Caps Lock

One of the more annoying aspects of having to remember passwords (along with having to remember loads of them) is that if you’ve got Caps Lock turned on accidentally when you type one in, it won’t work, and you won’t know why. Most desktop computers alert you in some way if you’re trying to enter your password to log on and you’ve enabled Caps Lock; there’s no reason why the web can’t do the same. What we want is a warning – maybe the user wants Caps Lock on, because maybe their password is in capitals – rather than something that interrupts what they’re doing. Something subtle.

But that doesn’t answer the question of how to do it. Sadly, there’s no way of actually detecting whether Caps Lock is on directly. However, there’s a simple work-around; if the user presses a key, and it’s a capital letter, and they don’t have the Shift key depressed, why then they must have Caps Lock on! Simple.

DOM scripting allows your code to be notified when a key is pressed in an element; when the key is pressed, you get the ASCII code for that key. Capital letters, A to Z, have ASCII codes 65 to 90. So, the code would look something like:

on a key press
	if the ASCII code for the key is between 65 and 90 *and* if shift is pressed
		warn the user that they have Caps Lock on, but let them carry on
	end if
end keypress

The actual JavaScript for this is more complicated, because both event handling and keypress information differ across browsers. Your event handling functions are passed an event object, except in Internet Explorer where you use the global event object; the event object has a which parameter containing the ASCII code for the key pressed, except in Internet Explorer where the event object has a keyCode parameter; some browsers store whether the shift key is pressed in a shiftKey parameter and some in a modifiers parameter. All this boils down to code that looks something like this:

keypress: function(e) {
	var ev = e ? e : window.event;
	if (!ev) {
		return;
	}
	var targ = ev.target ? ev.target : ev.srcElement;
	// get key pressed
	var which = -1;
	if (ev.which) {
		which = ev.which;
	} else if (ev.keyCode) {
		which = ev.keyCode;
	}
	// get shift status
	var shift_status = false;
	if (ev.shiftKey) {
		shift_status = ev.shiftKey;
	} else if (ev.modifiers) {
		shift_status = !!(ev.modifiers & 4);
	}

// At this point, you have the ASCII code in “which”, // and shift_status is true if the shift key is pressed
}

Then it’s just a check to see if the ASCII code is between 65 and 90 and the shift key is pressed. (You also need to do the same work if the ASCII code is between 97 (a) and 122 (z) and the shift key is not pressed, because shifted letters are lower-case if Caps Lock is on.)

if (((which >= 65 && which <= 90) && !shift_status) || 
	((which >= 97 && which <= 122) && shift_status)) {
	// uppercase, no shift key
	/* SHOW THE WARNING HERE */
} else {
	/* HIDE THE WARNING HERE */
}

The warning can be implemented in many different ways: highlight the password field that the user is typing into, show a tooltip, display text next to the field. For simplicity, this code shows the warning as a previously created image, with appropriate alt text. Showing the warning means creating a new <img> tag with DOM scripting, dropping it into the page, and positioning it so that it’s next to the appropriate field. The image looks like this:

A speech bubble with 'Warning: Caps Lock is on' written inside

You know the position of the field the user is typing into (from its offsetTop and offsetLeft properties) and how wide it is (from its offsetWidth properties), so use createElement to make the new img element, and then absolutely position it with style properties so that it appears in the appropriate place (near to the text field).

The image is a transparent PNG with an alpha channel, so that the drop shadow appears nicely over whatever else is on the page. Because Internet Explorer version 6 and below doesn’t handle transparent PNGs correctly, you need to use the AlphaImageLoader technique to make the image appear correctly.

newimage = document.createElement('img');
newimage.src = "http://farm3.static.flickr.com/2145/2067574980_3ddd405905_o_d.png";
newimage.style.position = "absolute";
newimage.style.top = (targ.offsetTop - 73) + "px";
newimage.style.left = (targ.offsetLeft + targ.offsetWidth - 5) + "px";
newimage.style.zIndex = "999";
newimage.setAttribute("alt", "Warning: Caps Lock is on");
if (newimage.runtimeStyle) {
	// PNG transparency for IE
	newimage.runtimeStyle.filter += "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://farm3.static.flickr.com/2145/2067574980_3ddd405905_o_d.png',sizingMethod='scale')";
}
document.body.appendChild(newimage);

Note that the alt text on the image is also correctly set. Next, all these parts need to be pulled together. On page load, identify all the password fields on the page, and attach a keypress handler to each. (This only needs to be done for password fields because the user can see if Caps Lock is on in ordinary text fields.)

var inps = document.getElementsByTagName("input");
for (var i=0, l=inps.length; i

The “create an image” code from above should only be run if the image is not already showing, so instead of creating a newimage object, create the image and attach it to the password field so that it can be checked for later (and not shown if it’s already showing). For safety, all the code should be wrapped up in its own object, so that its functions don’t collide with anyone else’s functions. So, create a single object called capslock and make all the functions be named methods of the object:

var capslock = {
	... 
	keypress: function(e) {
	}
	...
}

Also, the “create an image” code is saved into its own named function, show_warning(), and the converse “remove the image” code into hide_warning(). This has the advantage that developers can include the JavaScript library that has been written here, but override what actually happens with their own code, using something like:

<script src="jscapslock.js" type="text/javascript"></script>
<script type="text/javascript">
	capslock.show_warning(target) {
		// do something different here to warn the user
	}
	capslock.hide_warning(target) {
		// hide the warning that we created in show_warning() above
	}
</script>

And that’s all. Simply include the JavaScript library in your pages, override what happens on a warning if that’s more appropriate for what you’re doing, and that’s all you need.

See the script in action.

About the author

Stuart Langridge is a web hacker, author, and speaker living in the UK. When not writing books about JavaScript or trying to convince more people to use Ubuntu, he’s a founder member of the WaSP’s DOM Scripting Task Force and one quarter of the team at LugRadio, the world’s best open source radio show. Code and writings and (the occasional rant) are to be found at kryogenix.org; Stuart is to be found outside in the rain looking for the smoking area.

Photo: Lodewijk Schutte

More articles by Stuart

Comments