Developer Advocate versus Technical Evangelist; When names change the tone Passpack: Gears? AIR? Why choose!
Jul 04

Dealing with W3C Events; A story of running around in circles

Ajax, Tech with tags: , , Add comments

I am working on an interesting pet project that has a fairly rich UI. A rich UI means dealing with events, and I had a wake up call on what a pain it can be to deal with in the browser world.

I ended up on a wild goose chase that ended up having a simple solution.

Imagine one section of an application that is a canvas element, and another piece that is a input type="text". I wanted a way to jump between the two with a keystroke, e.g. Ctrl-J for jump.

Jumping from the canvas element to textbox was fine, but it wouldn’t work on the way back. I was trying to $('canvas').focus() but it wouldn’t work.

initMouseEvent

I stupidly thought that I should just simulate the click on the canvas element, since that was working just fine for me.

We are pretty fortunate that the ability to kick off true events programatically is bound into the event model itself.

This means that I could:

if (document.createEvent) {
   var evt = document.createEvent('MouseEvents');
   evt.initMouseEvent('click',
            true,     // Click events bubble
            true,     // and they can be cancelled
            document.defaultView,  // Use the default view
            1,        // Just a single click
            0,        // Don't bother with coordinates
            0,
            0,
            0,
            false,    // Don't apply any key modifiers
            false,
            false,
            false,
            0,        // 0 - left, 1 - middle, 2 - right
            null);    // Click events don't have any targets other than
                      // the recipient of the click
 $('canvas').dispatchEvent(evt);
}

This only slightly worked. I could test that the click was going through. I would add an onclick handler to canvas and it would fire just fine. However, something weird was happening. The focus was still in the textbox even with this click happening (which didn’t happen when I actually clicked on the darn thing).

Events from within events

Since the click was working, why not just force the blur on the textbox?

var events = document.createEvent('HTMLEvents');
var blur = events.initEvent('blur', false, false);
his.dispatchEvent(blur);

But that got me:

Component returned failure code: 0×80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIDOMEventTarget.dispatchEvent.....]

Hmm, on wait. This was happening from within an onkeypress handler and it appears that you can’t do this in a nested way. A setTimeout(..) to push it off until later didn’t make it any happier either.

tabindex=0

As I looked at this mess, I quickly realised that I had been unravelling a string, and I should take a step back.

I talked to Alex Russell, and he immediately said “erm, and you made sure that tabindex was set on the canvas element?”

Doh, it wasn’t. This is why the focus method (which did exist) didn’t do anything. I quickly added tabindex=-1 and now I could simply $('canvas').focus() and lo-and-behold it would focus!

Event.stop(e)

Great, the home stretch. I took the test and put it on the live code and it… still didn’t quite work. God damn it.

I took another step back.

A global key handler was set via document.onkeydown that was handling the world. When it saw a Ctrl-J it would short circuit and give focus to the textbox. The textbox has its own onkeydown that would do the opposite. A global flag held the state of global/textbox.

This is the problem. I had mucked up the event propogation, so in the case of jumping from textbox to canvas it would first go through the textbox handler, but after it was done it would STILL run the global handler which would kick it right back!

I just needed to e.stopProgagation(), or even better Event.stop(e) to stop any default action too.

Phew, all that running around to get back to square one. Now it works, and it reminded me of how the simplest things can be tricky.

3 Responses to “Dealing with W3C Events; A story of running around in circles”

  1. Peter Svensson Says:

    Wow! I really don’t envy you the oddysey, but how come you didn’t use a framework that manages events quite well – even canvas ones. Considering who you asked, Dojo might be a good idea :)

    Actually, dojo.gfx wraps all 2D graphics for both IE, FF and Safari, using canvas API lookalikes for all. And you can use the usual events on all ‘canvas’ elements as well.

    Perhaps I don’t understand well enough what you’re trying to do, but I felt that maybe it could have helped.

    Peace.

    Cheers,
    PS

  2. Ray Cromwell Says:

    Sounds like someone is making a game. :)

    I had this problem early in Chronoscope, but GWT has a widget that solves it (FocusPanel) in a cross browser way (tabindex doesn’t work on every browser) IIRC, on Safari 2 (yeah yeah, who’s still using it?), the only way to make non-form/non-link elements focusable was to use a hidden proxy element (like an invisible textbox)

  3. Subhash Pandey Says:

    Hi,
    I looked at this mess, I quickly realized that I had been unraveling a string, and I should take a step back.Great, the home stretch. I took the test and put it on the live code and it… still didn’t quite work.

    Subhash Pandey

    Wide Circles

Leave a Reply

Spam is a pain, I am sorry to have to do this to you, but can you answer the question below?

Q: What are the first four letters in the word British?