Mild-Mannered Canadian Fury

Doug Stephen is Politely Peeved

jQuery AJAX, jQuery Mobile, And Double-Fired Events


Sun, 15 Apr 2012 «permalink»

If you’ve spent any time with jQuery event handlers, you’ve no doubt encountered small annoyances like JavaScript’s event bubbling and such. It can be especially annoying when you’re hooking in to the click() event on an element that already has click behavior; anchor tags, submit-type buttons, etc. Usually, it’s simple enough to handle; a call through to event.preventDefault(), event.stopPropagation(), or even just returning false from the event can hamstring the default behavior of a clickable control. Other times, it is possible to get away with just ignoring the problem because the 2nd click has no impact on the site (though this is far from good practice).

Today, none of the above were of any use to me, and I found myself a little dumbfounded with little to no help from the Internet. I’m working on a little Arduino project with some guys from the lab, manipulating an Arduino on a network via a RESTful API I’m coding up in Ruby using the Sinatra platform. The current interface is done up on a website running on my local network. Currently, we’re controlling LED’s by using Pulse-Width Modulation to tune their brightness. I’m using AJAX requests to submit PWM values entered in to input fields on the locally hosted site.

Everything was working wonderfully yesterday, but today we added multiple LED’s to the board and I began seeing some strange behavior. Here’s the rundown:

The Arduino board operates by pulling bytes off the serial port, so when the Sinatra app sends out a command string we buffer this serial input in to a character array and then tokenize it. We then have to route the command to the appropriate digital IO pin. Our protocol is a simple delimited string indicating a pin and a PWM value; so sending a command of 3000 to pin 9 would look something like ‘9,3000’. We then use the built-in map function to compute a PWM byte and send that to the A/D converter. But we were getting garbage. Negative numbers, weird bits. We realized that we weren’t accounting for a delay in latency from the time the bits started coming down off the ether and in to the serial buffer. So we added a healthy delay in between when the Serial buffer became readable and when we actually started reading. From here, we were able to write data directly to the Arduino IDE Serial Monitor and get consistently correct information back. But the REST API was still bugging out. The one thing I did notice was that I wasn’t getting garbage anymore; I was getting wrong information that followed a pattern.

After poking around my JavaScript console for a while, I discovered the problem; my event handler for the Send button was issuing two asynchronous HTTP/1.1 POST requests for every click. Because we were providing a healthy latency delay to wait for the serial buffer to be accurate, the buffer was filling up with strange concatenated frankencommands. POSTing a ‘9,3000’ would actually end up with a buffer that looked like ‘9,30009,3000’. Because I was using the built-in strtok() functions from the C library with a comma as my delimiter, pin 9 would receive a value of 30009 instead of 3000.

Now, the easy/hacky way to fix this would have been to terminate every command with a comma and go on with my life. Everthing would still be posted twice, and because I flushed the serial buffer after each pin-command pair is pulled off I would be throwing out the duplicated command. But I was still issuing two POST requests, and that seemed to be wasteful and poor practice. I had already prevented propagation in my JavaScript code and Google was mostly useless in helping me figure out why my events were firing twice; all of my search results came from people who weren’t adequately handling the event bubbling up the DOM whereas I was.

I had seen some other more complex solutions suggested by people who didn’t understand event propagation and they would have suited my needs (seemingly), however they were needlessly complex. But then I had a little bit of an ah-ha moment after poking around the generated source on my test interface. Rather than using standard interface controls, I was building the web interface to the API in jQuery Mobile. I’m not too incredibly experienced with client-side javascript UI toolkits so I’m new to their quirks, but jQuery Mobile utilizes HTML5 custom data attributes to dynamically generate custom UI controls wrapped around the original markup. I’m not 100% sure of the specifics, but it seems that using jQuery plus jQuery Mobile leads to the $(document).onReady() event firing off a 2nd time after content generation, leading to my event handlers getting bound twice. If this isn’t the reason that the event handlers were getting bound twice, I would love to know the real reason, so please drop me a line. But until then, I will assume that this is the cause.

The solution to this was rather simple. Instead of using the pattern $(selector).click(…), I used the following pattern: $(selector).unbind('click').click(…). This insures that the handler is bounde no more than once. Our problems were solved, and we were able to execute commands with run-time pin selection on the Arduino board.