Clicking a link in Javascript with Seaside

This is going to be a bit technical. But hey, isn’t that where the fun of programming lies? It’s going to involve a little bit of Smalltalk, Seaside, Javascript and jQuery, so it’s going to be interesting for only a handful (or two) of people, most of which will most likely know this already. So regard this post as a note to myself 😉

For a web application that I am working on I wanted to write some kind of autocompleter that does more than just let the user pick a piece of text and copy that into a text field. It needs to do some stuff on the server side whenever a user clicks on a suggested item in the new autocompleter.

So I took out my JavaScript and jQuery toolkit and implemented a little jQuery plugin that would do several things:

1) use the text from an input field and send it home to the server via ajax, so that the Seaside Application can tackle together a list of suggestions.

2) fadeIn a div and have it load the contents of another request that gets fired off to the server using $mydiv.load().

3) Use a component to render the suggestions on that div. This is the usual renderContentOn: stuff in Seaside

No. 1 is not too hard: bind a keyup handler to the text input field and use it to send an ajax request that is associated with a JSAjaxCallback. Stealing a bit from JQAutocomplete this is relatively easy.

Also, once you know how to hand a callback reference down to the browser so that you can have it handed back in an Ajax call, No.2 is just a little bit of homework.

No.3 is pure Seaside stuff, just renderContentOn: and some anchors with callbacks.

Thus far, I got this thing to work quite nicely. Once you enter a letter into the text input field, the div pops up, gets filled with some suggested entries in the form of links and you can click on them. The server will then do whatever is put into the clicked link’s callback block.

Things got hard when I wanted to have the whole thing work with keyboard shortcuts, like arrow up/down for moving from one selection to another and the tab key to activate one.

That very last part cost me a lot of time.

First I tried the obvious: $;
And soon found out it didn’t work. For debugging, I bound an event handler to the click event of $selectedLink. Safari’s developer tools and console are great for doing such things: ${alert(event)}). It turned out that the link got clicked, because I got a popup every time I pressed tab with an open autocompleter. But the server received no request. Thats’s because .click() does click the link, but it seems that does not include what links usually do: open a new document. (this last sentence is a hint per se, but I flew a few extra rounds before I realized that).

So the next thing I tried was to use jQuery’s ajax function to call the callback that is associated with the link. Therefor, I needed to extract the “address of the callback” from the <a href=”…”> element. It turned out to be possible with $selectedLinks.get(0).href. But nothing happened on the server side. My breakpoint in the code called by the callback Block never fired.

Same with $.get(). Safari logged the ajax request hoing out to the server and an empty reply coming back in both cases. So both my $.ajax and $.get calls were okay. What was wrong with that?

Over to the server image, put a Breakpoint in WAActionPhaseContinuation>>#runCallbacks and see what happens. The callback came in, was found in the list of registered callbacks and got…. ignored! Boom.

Why is that? Because anchor elements register a WAActionCallback, and the callback processing ignores WAActionCallbacks when they are coming in with an XMLHttpRequest (aka Ajax call).

Hmmm. Clicking the link with a mouse works. Sending Ajax doesn’t, and .click()’ing doesn’t. Strange, I thought.

So I put the problem aside for a moment. A little later I thought: well, if .click() does indeed click a link, but not follow it, what exactly does it tell me? It tells me that clicking is clicking and I need to find a hook that simoply does what links do: load a document that the link refers to.

And boom: the solution was there: document.location=$selectedLinks.get(0).href;

This does, of course, lead to the fact that a click – handler for the link doesn’t get called, but that can be accomplished by doing a .click() first.

So what exactly happens there? The href address of the link gets requested, seaside dispatches the request to the correct callback handler and initiates the next render cycle, just as if the link was clicked with the mouse.

So I’ve learned a lot from this exercise:

First, I once again had to debug into Seaside’s request handling, dusted off my Javascript and jQuery knowledge, did all kinds of inspection and debugging of JavaScript code in both Firebug and Safari’s developer tools and that there is a difference between clicking a link and actually following it.