Ralph Holzmann

Web Development – jQuery, PHP, MySQL. In that order.

.fadeIn() and jQuery Goodness January 29th, 2010

While carousing the jQuery IRC channel on free node the other day, I noticed that one user in the chat was looking for a way to streamline multiple calls to jQuery’s traversal methods, namely, prev(), next(), and parent(). He asked, instead of writing parent().parent().parent().parent().parent(), can’t we just write .parent(5)? I was really excited to hear this for a few reasons. First of all, this is a feature I have always wished jQuery would implement in their traversal methods, and hearing that other jQuery users wished for this also, made me feel better about myself. Secondly, I had recently made a few commits to the jQuery source on github to facilitate the exact functionality that the said user described.

Unfortunately, after a few days, it looks like my pull requests weren’t picked up by the jQuery team, and it’s likely that they never will be. However, after continuing our conversation on IRC, Paul Irish of YayQuery podcast fame urged me to create what he calls a “monkey patch” (although after reading the Wikipedia entry, I think I’m more partial to the term “duck punching”). He showed me some examples, and my mind was blown. I never realized that this technique was possible, and I’ve been writing crappy work-arounds to achieve the same effects for over a year now. The technique allows you to tweak the functionality of a method by:

  1. Saving a reference to the original function.
  2. Redefining the original function, adding your desired functionality.
  3. Using the reference to the original function to facilitate the new functionality and backwards compatibility.

It’s really slick. But when would you ever use this? Here’s an example. Typically, in IE, when you would fade in (or out) an element, IE would apply their arbitrary filter attribute to facilitate opacity. This is all well and good, except that in IE6 and IE7, the filter attribute seems to disable cleartype font smoothing on the text contained in the element. This is not only noticeable, but quite ugly, and has been submitted to the jQuery bug tracker multiple times. The fix for this problem is to simply remove the “filter” attribute from the element once it has reached full opacity. You can do this by adding this snippet into the callback of fadeIn, like so:

$('#myElement').fadeIn('normal', function(){
    if(jQuery.browser.msie) {
        $(this).get(0).style.removeAttribute('filter');
    }
});

This solution works, however it sucks. It’s ugly and bloats your code. It’s particularly a pain if you hadn’t planned to use a callback function at all. Now you’re forced to not only include a callback function, but also the easing argument, just so content will display correctly in IE. This is super lame. The jQuery dev team has yet to address this IE-specific short coming, and in the past, I have resorted to writing ugly and cumbersome wrapper functions that looked something like this:

$.fn.myFadeIn = function(easing, callback) {
    return this.fadeIn(easing, function(){
      if(jQuery.browser.msie) {
          $(this).get(0).style.removeAttribute('filter');
      }
      if($.isFunction(callback)) callback();
    });
  }

I would argue that this solution also sucks, because I now have to go through my entire project writing myFadeIn(), or do a find and replace in my existing project. This monkey patch technique trumps both those solutions by far. It allowed me to redefine the original fadeIn function while still tackling the IE cleartype bug. Here’s my solution:

(function($){

    /***
    * fadeIn() and fadeOut() now removes filter attribute in IE to resolve
    * clearType issues in IE6 and IE7. Can be short-circuited by setting
    * cancel to true. Addresses tickets #2457, #3230, #4779 and others.
    */
    var _fadeIn = $.fn.fadeIn;

    $.fn.fadeIn = function(easing, callback, cancel) {
        return _fadeIn.call(this, easing, function(){
            if(jQuery.browser.msie && !cancel) {
                $(this).get(0).style.removeAttribute('filter');
            }
            if($.isFunction(callback)) callback();
        });
    }

    var _fadeOut = $.fn.fadeOut;

    $.fn.fadeOut = function(easing, callback, cancel) {
        return _fadeOut.call(this, easing, function() {
            if(jQuery.browser.msie && !cancel) {
                $(this).get(0).style.removeAttribute('filter');
            }
            if($.isFunction(callback)) callback();
        });
    }

})(jQuery);

This is the best solution, by far, because I don’t have to change the way I code. I can simply drop this snippet into any project, and all my fadeIn’s are magically transformed and shed this horrendous bug. Plus, it allowed me add a third argument to fadeIn and fadeOut to cancel the removal of the filter attribute, just in case the developer uses any of Microsoft’s other ridiculous filters and wouldn’t like the attribute removed.

Between this new “monkey patches” technique, and slide 18 from Rebecca Murphey’s Organize your jQuery presentation, the way I write JavaScript has changed forever. I have the source and some other nifty patches, including adding integer arguments to jQuery’s traversal methods, up on github. You have can find them here. I hope this technique helps save user’s time and headaches in the future.

1 Comment Time: Relative | Actual

  1. How to fulfill your own feature request -or- Duck Punching with jQuery! « Paul Irish said:

    [...] removing IE's filter attribute after a fade [...]

Other stuff …

Tweets I write

Posts I write

Companies I currently develop for

Past development gigs

Things I use everyday

Ways to contact me