Ever wondered to have personalised shortcuts in your website? It's not that hard, here is how you can do that...
Attention: this work is under the Creative Commons Attribution 3.0 Unported licence
shortcuts.zip (contains the shortcuts class).
The idea is to provide a very simple shortcut system (single characters, or
[shift|ctrl|alt]+character). You can easily extend this once you'll have understood how the system works.
Class definition and private variables:
keyCodes is used to keep modifier key codes. When a key is being pressed, it's passed to the handler via an event object, which doesn't have the character's symbol, but only its Unicode digit. Yes: Unicode, not UTF-8. If you want to support UTF-8 (or UTF-16), you'll have to work it out a little bit (you can find several libraries that do this for you).
The defined shortcuts variable is an array and... oh well, I think it self-explains. We'll fill it later with a proper method (addShortcut).
And here it is: this method will fill our definedShourtcuts private variable. The idea is to let the library-user easily add shortcuts just in the common way people write about shortcuts, for example "CTRL+X". This method is case-insensitive, so it's not a problem if the same shortcut is called "Ctrl+x" or whatever. If you want, you can strip out whitespace to make it more dumb-proof.
The method accepts two arguments: the shortcut as we've just talked about, and a function, which will be called when the shortcut is being pressed.
insert variable is the object we'll push into the defined shortcuts class variable. The first thing then is to define its basic structure.
Secondly we keep a copy of the shortcut we're going to elaborate, but just for the ending log. If you wish, you can simply strip off both for keeping the code cleaner.
Now it's time to analyse the shortcut: we want to transform from something like
insert variable). So we need to split (PHP users: explode) it by the plus operand. Because we're making it case-insensitive, we first transform it lowercase.
We've got an array of strings now, so we need to iterate them to differentiate between modifiers and the actual character. Apparently browsers, or at least Chrome with which I've tested the code, will send the upper case variant of the character pressed (i.e. if you press e, it will send E), so in case it's the character and not a modifier, we need to transform it upper case, then we save its key code (because we're going to work with this in the key event).
The last lines are just some checks. Eventually, if it's all ok, the shortcut and the relative function are being stored in our shortcuts array.
This is the second and last public class' method: it simply attaches the key event handlers to the onKeyUp and onKeyDown events. The former event must be handled because otherwise shortcuts like CTRL+S could interfere with the default shortcuts, the latter will actually call the relative function.
This is the handler which will block any default behavior if the shortcut has been defined in our class. The key event handlers accept one parameter: the key event itself, which contains useful information about which keys have been pressed (or released, or whatever). The idea is to iterate through all the defined shortcuts and search for the one which matches the keys pressed.
You may ask about the
^ operator: it's the XOR (eXclusive OR). If you don't know what a XOR is, it's simply an OR which will return 0 if both the operands are true.
a ^ b | result 0 ^ 0 | 0 0 ^ 1 | 1 1 ^ 0 | 1 1 ^ 1 | 0
For example, the first XOR is equal to
shift && !e.shiftKey || !shift && e.shiftKey.
e.preventDefault() is the key of this method: because the handler is executed before the standard browser functions, you're able to stop them before they even start. In this way you can safely bind any combination of modifiers and characters without worrying about browser-specific shortcuts.
This is similar to the keyDownHandler. I've added a check to interrupt the method earlier if the user is pressing modifier keys (as they can't be alone a shortcut). The cycle is the same as the previous one, but instead of preventing the default behavior, it will execute the function defined in the addShortcut method's second parameter.
Easy, isn't it?
Now, an example on how to use the class:
- 17th August 2013