Saturday, April 16, 2011

IE, Javascript and jQuery: some common pitfalls (and workarounds)

At www.kingranks.com we have two developers: me and a colleague. As much as we would love to have a front-end developer, we do not have the resources (or the necessity) to hire one and so we pretty much do all the HTML/CSS and Javascript work even though we are back-end engineers at our core. I don't hate doing it but there is one particular thing that ticks me off: cross-browser compatibility. I know (some) folks are trying really hard to move towards standardization, but is not a reality just yet, specially when you are trying to make everything work in Microsoft's Internet Explorer.

This is not meant to be a post detailing why some things don't work in IE, I don't even know for the most part. This is meant to be a quick post showing some of the problems we had making some particular things work in IE and how we got around them. This is not, by any means, a complete or comprehensive guide, it's just several things I always try to keep in mind when writing front-end code and markup.

Every item has been tested in IE8. Almost 90% of all our IE users use IE8 so that's our main focus. I do have reasons to believe it should also work in IE7 and IE9 but I haven't tested it myself.

1) Use the shortcut $. instead of jQuery()

    This is pretty mysterious. For some reason IE complained about the jQuery() but it accepted the $. shortcut. The jQuery Cookbook has a small recipe on how to avoid the shortcut $. from creating global conflicts but I never had any problems with it.

2) Preventing event default behavior with event.preventDefault() doesn't work. Use
event.preventDefault ? event.preventDefault() : event.returnValue = false;
instead. Notice that we test for the preventDefault function, and fallback to setting it's return value to false if it is not defined.
    When creating a web 2.0 experience, the "default" behavior is sometimes (or quite often) not what you want. You might expect a GET request and a new page load when you click a link, but nowadays you might as well expect an AJAX request that renders some new content without reloading a page. So preventing default behavior is actually quite essential.

3) If you use onClick="someFunction(event)" html attribute, event is not passed as a parameter

    In IE the event will not be passed as a parameter. Instead, it will be in window.event, example:

jQuery:
  
        function someFunction(event) {
            if(!event) event = window.event;
            // do something with event
        }

HTML:

        <a href="somelink" onClick="someFunction(event)">link</a>

    Some people might argue that using an onClick="someFunction(event)" on an HTML tag, is a bad practice since markup is not supposed to tell the behavior or functionality of it's elements (see [1]).
    I did have a performance problem using jQuery's register method $(element).click() with a table that had lots (hundreds or couple thousands) elements that had to be registered for a click and using the onClick() helped. I think the same goes for onChange, onBlur, etc...

4) If you want to get the target of the event (as a jQuery object), use:

        var targetElement = $(event.target || event.srcElement);

In IE, the element is not the target, but the srcElement. Again, if event.target is not defined, we fallback to the srcElement.

5) Always declare your variables with the var keyword.

    IE gave me a hard time when I didn't explicitly declared my vars.

6) Remove all console.log(). IE does not understand console unless you have debugging tools activated, which I don't believe normal users do. Use this nice little code instead (from[2]):

        function trace(s) {
            try { console.log(s) } catch (e) { return; }
        };

    Then you can use trace("message"); in your javascript code. Notice that it is not going to log anything on IE unless you have developer tool bar on but it is not going to generate errors. If console is not defined, the function doesn't do anything.

7) Use jQuery functions whenever you can. It's safer since jQuery people do an outstanding job at trying to make everything cross-browser and portable. For example, IE8 simply doesn't implement the string.trim() function (go figure...), as I imagine there might be a number of other functions it doesn't implement. So try looking for the equivalent jQuery function, for instance, instead of using string.trim() (and breaking your JS on IE8), use string = $.trim(string). Instead of using string.slice(), use string = $.slice(string) and so on and so forth.

8) This is more some piece of advice than a workaround for some problem: use IE8 developer tool bar. It actually works (surprise!) and you can set breakpoints and pinpoint exactly where the error is.

9) Use object detection. This is specially useful when you can't test on every browser (I don't have an IE6 to test on, for instance). Test to see if the function or attribute you are going to use actually exists before using it. This prevents your code from breaking on browsers that don't implement it. For instance:

        if (windows.focus) windows.focus();

There is a nice article about this on [3].

Let me know if you have any more pitfalls, and happy coding!

[1] http://en.wikipedia.org/wiki/Unobtrusive_JavaScript
[2] http://stackoverflow.com/questions/690251/what-happened-to-console-log-in-ie8
[3] http://www.quirksmode.org/js/support.html