DOM events portability

It's not that hard

2010-12-11

The browser is tempting as a game development platform, for a variety of reasons. But while output is pretty much standardized now that we have <canvas>, input is still a problem. Or so I thought.

That’s because people keep complaining how making DOM events work across browsers is a pain, blah blah. And for a while I listened to the complaints and just didn’t bother. But I should have.

See, I have this game called Square Shooter (also available as an Opera widget). Due to the nature of the gameplay, I need to track the position of the mouse inside the canvas. Easy enough, right?

canvas.onmousedown = function (event) {
    alert(event.layerX + " " + event.layerY);
}

Trouble is, that only works in Gecko- and WebKit-based browsers (think Firefox and Safari). If you try actually using the value, Opera will throw a mysterious exception from somewhere inside the canvas object. But the problem is not there. It’s because Opera lacks event.layerX. Once I figured that out, a simple Web search brought me to this article, and suddenly the solution was crystal-clear:

canvas.onmousedown = function (event) {
    var x = event.layerX || event.offsetX;
    var y = event.layerY || event.offsetY;
    alert(x + " " + y);
}

There. Doesn’t seem so difficult anymore, eh? Shame on me for not figuring this earlier, and thinking Opera had a bug! (Well, arguably it does: native code in their canvas implementation should check the arguments more thoroughly, knowing the API will be used from a dynamic language. But that’s another story.)

Now, what about keyboard events? Because, you know, some kinds of gameplay actually require keyboard controls. Turns out, as long as you stick to the regular keys (think letters and digits, and not arrows), the keypress event serves you the actual character you typed on a platter:

window.addEventListener("keypress", function (event) {
    alert(String.fromCharCode(event.charCode));
}

Again, this works in Gecko and WebKit, but in Opera, nothing happens. Why? According to this, you can’t attach a keypress event to the window object in Opera. So? Big deal, I moved it to the document object. But it still didn’t work properly, because…

as this page says, there is no event.charCode in Opera. But for keypress events, the same information is passed in event.keyCode. So the code above becomes:

document.addEventListener("keypress", function (event) {
    alert(String.fromCharCode(event.charCode || event.keyCode));
}

And so, with three simple changes, I’ve made my DOM events compatible across all major browsers. It even works in IE9, reportedly. So now I can confidently implement any control scheme I want, knowing it will run everywhere.

Lesson learned? DOM events may have been less compatible across browsers years ago. But times change. Don’t get an old myth stuck in your head. Check the reality from time to time.