Capturing the @ key in javascript

I regularly use Atlassians confluence and I'm a fan of the simplicity with which I can insert macros, create links or reference people with the { [ and @ keys respectively. Its a great alternative to using keyboard shortcuts or using the mouse to click on menus.

I'm interested in trying similar UI in some of my applications so decided to try out capturing the @ key to load an intellisense menu for peoples names.

First I tried plain javascript to capture the keyCode on a keyUp event but couldn't get the callback to fire as expected.

I've used libraries like mousetrap before in my apps to good effect so I tested that quickly to no avail. It wouldn't fire on the binding to the @ key either. In fact I tried a bunch of key binding libraries and online testing pages and I couldn't find one that worked.

At this point after some research I found out three relevant bits of information.

  1. There is no standardized way to detect keyboard layouts in javascript. Most libraries target US qwerty keyboard layout.
  2. The most reliable way to detect the key pressed is by converting and comparing it using String.fromCharCode()
  3. The only cross-browser way to reliably get the charCode is on the keypress event.

So with this in mind I wrote the following which worked in chrome:

document.getElementById("content").onkeypress(function (e) {  
    if (String.fromCharCode(e.which) === "@") {
        console.log("@ key pressed!");
    };
});

To make this code cross-browser friendly there is more to do though. Some versions of IE dont populate the e.which property. The following function I found in this article which proved very helpful in navigating the x-browser minefield.

// event.type must be keypress
function getChar(event) {  
  if (event.which == null) {
    return String.fromCharCode(event.keyCode) // IE
  } else if (event.which!=0 && event.charCode!=0) {
    return String.fromCharCode(event.which)   // the rest
  } else {
    return null // special key
  }
}

Putting it all together I arrived at the following which I think is reasonably robust.

document.getElementById("content").onkeypress(function (e) {  
    var char = getChar(event || window.event);
    if (!char) return; // special key
    if (char === "@") {
        console.log("@ key pressed");
    }
}

I thought it would take me about two minutes to capture the @ input key. Several hours later, boy was I wrong.