jQuery events: setting a property that has only a getter

I ran into a new problem in jQuery the other day, and I couldn't find a good solution for it. Hence, a new post in the hopes of helping out the next individual.

In case you didn't know, I'm working on a modern learning management system for Instructure. Good stuff, but I can't spill too many beans yet. Sorry.

Anyway, as part of that I need a good javascript WYSIWYG editor (love to do just WYSIWYM, but I don't think it's general enough for all faculty) and I've settled on TinyMCE for the time being -- mostly because they've actually got some accessibility functionality baked in. I've written a jQuery wrapper around it for all the features I need to get at (because let's face it, TinyMCE's API is NOT intuitive, and I'd prefer to touch it as little as possible), but I also need to capture key events so I can treat the "escape" key like a cancel click. Sadly, TinyMCE uses iframes and eats up javascript events before you can get to them.

No problem, TinyMCE's API lets you catch key and mouse events and do what you will with them. To make things simple I'm listening for key events on all form text elements, so I just want to take TinyMCE's key event and pretend like it got called on the textarea that TinyMCE replaced. jQuery has a .trigger method that lets you call events as if they happened on the selection, so I just pass the event TinyMCE gives me into jQuery.

Bzzzt! Firefox throws an error, "setting a property that has only a getter". Blast. I futz around with it for a bit, can't solve it, so I finally muster the courage to dive into jQuery code and figure out what the problem is. The error's being thrown because the jQuery trigger method asks you to specify the event type ("keydown", "mouseup", etc.) and then sets the event.type property to make sure it's the right type. Problem is, javascript native events don't let you SET the event type, only get it. Hence the error.

Double-blast. Okay, so now what? I dig a little deeper (like I said before, no good documentation that I could find on this) to see what jQuery's doing with events. When jQuery catches an event, it wraps it in a jQuery event object to make things happy (and you can set the event.type property on this wrapped object to your heart's content). So if I just wrap the event, problem solved. I realized I should probably do this anyway, because while the only error I hit was setting an unsettable property, there's probably other kinks I don't want to deal with.

So how do you wrap events? Turns out it's pretty easy. Instead of:

if(e.type == 'keydown') {
$("selection").trigger(e.type, e);
}

You use:

if(e.type == 'keydown') {
$("selection").trigger(e.type, $.event.fix(e));
}

That's if you have "$" as an alias for jQuery. Otherwise you'd call jQuery.event.fix(e) to get the wrapped object.

Also, I'm actually using triggerHandler instead of trigger. Trigger just fires the event, while triggerHandler triggers everything except the default behavior (that way I'm not initiating a keydown on the textarea and inadvertently adding text).

Problem solved!

Comments

Popular Posts