On most web pages where a user needs to register / login, there is a password field where the input is masked. One issue there is that a user cannot see whether what he’s typing is actually what he thinks he’s typing. When logging in, it’s not such a big issue, since if you mistyped your password, you’ll see that you’re not authenticated.
When registering, it’s more of an issue. The common solution is to ask the user to type his password again. If a user pressed a wrong key once, the second time the chances are high that he won’t press the exact same key. Of course there are a few exceptions:
- Wrong keyboard layout: German keyboard on a US English OS.
- Num Lock on, on a notebook.
- Caps Lock on.
For the first one, there isn’t much you can do about it, except taking a snapshot of the keyboard with the webcam and figuring out which layout it is ;-).
For the second one, you can’t handle it in JavaScript. To read the num lock state, you’d need some ActiveX objects or VBScript.
It leaves us the caps lock thing. This one can be handled in JavaScript. If you google for it, you’ll find many posts, articles and forum answers about it. All kind of look like this:
<html> <body> <script language=javascript> function checkCapsLock( e ) { var myKeyCode = e.which ? e.which : ( e.keyCode ? e.keyCode : ( e.charCode ? e.charCode : 0 ) ); var myShiftKey = e.shiftKey || ( e.modifiers && ( e.modifiers & 4 ) ); var charStr = String.fromCharCode(myKeyCode); if ( ( ( myKeyCode >= 65 && myKeyCode <= 90 ) && !myShiftKey ) || ( ( myKeyCode >= 97 && myKeyCode <= 122 ) && myShiftKey ) ) { alert( "caps lock on" ); } } </script> <form> <input type="password" name="password" size=20 onkeypress="checkCapsLock( event )" /> </form> </body> </html>
Basically, it does the following:
- Checks which character was typed using the which, keycode or charcode property of the keyboard event (whichever is provided by this particular browser.
- It checks whether the Shift key was pressed either using the shiftKey property or the modifiers flags.
- It then checks whether the character was between A and Z (capital letters) and shift wasn’t pressed or between a and z and the shift key was pressed. If it’s the case it notifies the user.
This check with the shift key is required because the behavior in most browser is that the shift key turns a capital letter into a non-capital one and vice versa. So if you have caps lock on, you’d get capital letters except if you press shift. So the check whether we get capital letters without shift pressed or non-capital letters with shift pressed.
This method has 3 major problems:
- It assumes that you’re actually typing the password (and e.g. not copy&pasting).
- Safari (at least on macs) ignores the shift key if the caps lock is on.
- It assumes that all letters are between A and Z.
For the first point, you can detect that the user pasted something (vs. typed it in), by using an onpaste on the input box. You could also forbid copy and pasting using:
<input type="password" name="password" size=20 onkeypress="checkCapsLock( event )" onpaste="return false;" />
For the second point, you should probably detect whether you’re running on Safari and disable the check (especially since Safari already shows you with a small icon that caps lock is on). You can detect it this way:
var ua = navigator.userAgent.toLowerCase(); if ( ua.indexOf ( 'safari' ) != -1 ) { if ( ua.indexOf( 'chrome' ) == -1 ) { // It's Safari. } }
Or this way (assuming Apple doesn’t have a second browser line when you read this post ;-)):
var vendor = navigator.vendor.toLowerCase(); if ( vendor.indexOf('apple') != -1 ) { // It's Safari }
Now let’s come to the third point. If you have a German or French keyboard, you’ll have many more keys available to type in a password. The code above will not notice that caps lock is on if you type Ö or Ä. This is because ö and ä are not between a and z (in terms of character code) but afterwards. So just defining ranges will not work for all languages.
My first idea was to compare the typed character with it’s upper case version. If they are the same, then it’s an upper case character. This works for letters but makes the script think that all numeric and non-alphanumeric characters are upper case letters. So you need an additional condition to determine whether it’s an upper case letter. That’s kind of obvious once you’ve used the trick of converting the character to uppercase: convert it to lower case. If the character is now different, then it was an upper case character.
Here the sample code:
<html> <body> <script language=javascript> function checkCapsLock( e ) { var myKeyCode = e.which ? e.which : ( e.keyCode ? e.keyCode : ( e.charCode ? e.charCode : 0 ) ); var myShiftKey = e.shiftKey || ( e.modifiers && ( e.modifiers & 4 ) ); var charStr = String.fromCharCode(myKeyCode); if ( (charStr.toUpperCase() == charStr ) && ( charStr.toLowerCase() != charStr ) && !myShiftKey ) { alert( "caps lock on" ); } } </script> <form> <input type="password" name="password" size=20 onkeypress="checkCapsLock( event )" /> </form> </body> </html>