I recently encountered what appeared to be a cross-browser issue with jQuery, which was both surprising and frustrating. After all, eliminating those cross-browser inconsistencies is no small part of jQuery’s fundamental appeal.

After some investigation, the source of the trouble actually stemmed from an oversight on my part. I doubt that many of you will have to deal with exactly the same situation, but the lessons I learned may apply to cross-browser jQuery problems you encounter in the future.

Tables all the way down

It all started with a relatively simple HTML table template being rendered on the client-side via jTemplates. The rendered markup looked something like this:

A series of these tables would be rendered in chronological order, one per invoice.

The bit of jQuery in question

Once those tables were rendered, the user often needed to trigger an action on the first row of the last table. The action itself isn’t relevant, but one important detail is that it blocked interaction with the rest of the page until its workflow was complete.

Glancing at the markup, the selector to accomplish that seemed simple enough:

Nice, simple, and it worked great. We tested it thoroughly, got user-approval, and I moved on to other features.

Unfortunately, that single line of jQuery would come back to haunt me later.

IE failed me? It’s always IE!

Originally, I was fortunate enough that all this application’s users were using recent versions of Firefox. Since all my development took place in Firefox too (you still can’t beat Firebug), the application received very little cross-browser testing.

Over time, the application became a victim of its own success. It worked so well that other departments began to adopt it too; departments with Internet Explorer users. All of the IE users were using IE8, so that wasn’t as bad as it could have been, but it brought an interesting cross-browser problem to my attention.

The problem was that the bit of code which called triggerSomeAction() on the first row of the last table wasn’t working right in IE. Worse yet, it was still locking the interface though. IE users were unable to complete that bit of the workflow and remained locked out of the invoices once that action had been triggered.

I cursed IE, out of habit, and went to work debugging the issue.

Nope, I had failed myself

Tracing through the code, it seemed like the selector was finding the <thead> row instead of the first row of the table body. That didn’t make much sense at first, but then I inspected the rendered markup side-by-side in both Firebug and IE’s developer tools:

Comparing the rendered DOM in Firebug and IE's Developer Tools

See the difference?

Firefox had been rendering the table’s markup exactly as given, even though omitting a <thead>’s <tr> isn’t valid markup, but IE tried to fix my mistake by inserting the missing <tr>. In fact, this behavior isn’t unique to IE. Chrome and Safari both do the same thing that IE does with that particular malformation.

Because jQuery’s selectors act on the DOM itself, not the raw HTML that was originally fed to the browser, this “help” was changing the result of my jQuery selector. Selecting tr:first was actually returning the row of <th> elements in every browser other than Firefox.

I stopped cursing IE and began cursing myself.

The fix

The fix was simple enough. I changed the selector to be more precise:

That fixed the immediate problem. Then, I corrected the template’s missing <tr> for good measure.

What I learned

Validating HTML has a reputation as something only obsessive purists do, but Validating you HTML is a useful tool. It’s lint for your HTML. I’m often lax about validating internal-only sites, but doing that would have helped me avoid this problem before it happened.

If a jQuery selector seems inconsistent across browsers, inspect the DOM in those browsers. It’s not safe to assume that every browser will interpret your markup as its written. Thankfully, most browsers now include tools suitable for doing so.

If you think you’ve “found a problem” with a mature library like jQuery, blame yourself first. I should have known better than to think there was a problem with jQuery in such a common scenario.

And you?

I hope that having watching my mistake unfold in slow-motion will help you if you run into a similar issue in the future. Even better, maybe it will help you avoid the problem in the first place!

Do you have a similar story? What are the cross-browser issues you’ve run into when using jQuery?