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.

One Line Random Boolean in JavaScript December 28th, 2009

Happy Holidays everyone! I hope everyone enjoyed the holidays this year. I’m back to work today. I just wanted to post a quick snippet that I used in a jQuery plugin I’m developing. It’s nothing earth shattering, but I thought it was worth mentioning. It’s a true random boolean in JavaScript:

!Math.round(Math.random());

This creates a random number between zero and (almost) 1, then rounds it either up or down, then uses the negation operator twice once to convert it to a true boolean (if you pass it to the typeof function, it will return “boolean”).

Now back to work!

Edit: Josh Dean pointed out in the comments that only a single negation operator is needed in this case.

Relative Date String in JavaScript December 14th, 2009

Here’s a script I wrote awhile ago that’s used in this site and a few other projects of mine. It’s a method attached to the JavaScript Date object to output the date relative to now. It works for both past and future dates.

Date.prototype.toRelativeString = function() {

  // Calculate difference between date and now
  var today = parseInt((new Date()).getTime() / 1000);
  var date = parseInt(this.getTime() / 1000);
  var second_difference = today - date;
  var day_difference = Math.floor(second_difference / 86400);
  var when_text, one_off;

  // Determine if date is in future or past
  if (second_difference < 0) {
    when_text = ' from now';
    one_off = 'Tomorrow';
    second_difference = -second_difference;
    day_difference = -day_difference
  } else {
    one_off = 'Yesterday';
    when_text = ' ago';
  }

  // Return relative string
  if(day_difference == 0) {
      if(second_difference < 60) {
          return 'Just now';
      } else if(second_difference < 120) {
          return '1 minute' + when_text;
      } else if(second_difference < 3600) {
          return Math.floor(second_difference/60) + ' minutes' + when_text;
      } else if(second_difference < 7200) {
          return '1 hour' + when_text
      } else if(second_difference < 86400) {
          return Math.floor(second_difference/3600) + ' hours' + when_text;
      }
  } else if(day_difference == 1) {
      return one_off;
  } else if(day_difference < 7) {
      return day_difference + ' days' + when_text;
  } else if(day_difference == 7) {
      return '1 week' + when_text;
  } else if(day_difference < (7*6)) {
      return Math.ceil(day_difference/7) + ' weeks' + when_text;
  } else if(day_difference < 365) {
      return Math.ceil(day_difference/(365/12)) + ' months' + when_text
  } else {
      years = Math.round(day_difference/365);
      return years + ' year' + (years != 1 ? 's' : '') + when_text;
  }
};

Here’s some examples:

var now = new Date();
document.write(now.toRelativeString() + '<br />'); // Just now

now.setDate(parseInt(now.getDate()) + 3);
document.write(now.toRelativeString() + '<br />'); // 3 days from now

now.setDate(parseInt(now.getDate()) - 4);
document.write(now.toRelativeString() + '<br />'); // Yesterday

now.setDate(parseInt(now.getDate()) - 4);
document.write(now.toRelativeString() + '<br />'); // 5 days ago

now.setDate(parseInt(now.getDate()) - 7);
document.write(now.toRelativeString() + '<br />'); // 2 weeks ago

now.setFullYear(parseInt(now.getFullYear()) - 7);
document.write(now.toRelativeString() + '<br />'); // 7 years ago

now.setFullYear(parseInt(now.getFullYear()) + 10);
document.write(now.toRelativeString() + '<br />'); // 3 years from now

It comes in very handy when dealing with dates. Hope you enjoy and please let me know if there are any bugs.

JS Bin Links Now in All Code Snippets December 14th, 2009

I’ve edited the core of the Syntax Highlighter plugin used on this site to highlight code snippets to include JS Bin links on its toolbar (where applicable). If the code snippet can be represented and played with in JS Bin, a link will appear in the toolbar to that code on jsbin.com. If you’re not familiar with JS Bin, it’s definitely worth checking out. It’s a very simple online collaborate HTML/JavaScript editing tool. You can create an HTML page with JavaScript, and then save it to a short URL that you can distribute to your friends, colleagues, blog readers etc. It’s very similar to Google’s AJAX APIs Playground, except you can save and distribute any JavaScript/HTML.

Strip Specific HTML Tags From String December 10th, 2009

In fiddling around with some code highlighters to use for this blog, I found myself needing a way to strip tags from a JavaScript string of HTML, while leaving others. I needed it so I could convert highlighted code to plain text so user’s could copy and paste code snippets without all the HTML garbage. This is what I came up with:

var saveTags = new RegExp('<(?!((\/)?(li|span)))(.)?([a-zA-Z?])[^><]*>', 'gi');

var html = '<div><ol><li><span>Some Text</span></li></ol></div>';
var strippedHtml = html.replace(saveTags, '');

alert(html);          // <div><ol><li><span>Some Text</span></li></ol></div>
alert(strippedHtml);  // <li><span>Some Text</span></li>

The regex matches all html tags except li and span. You then simply use the String method ‘replace‘ to replace the matches with an empty string. To edit the tags for the regex, simply change the tags in the expression, making sure to separate them with the bar ‘|’ character.

After figuring out how to strip the specific tags , I didn’t even end up using it because I moved from jQuery Chili 2.2 to SyntaxHighlighter which has a built in source code viewer, but I thought the information was valuable nonetheless.

New Design December 9th, 2009

I just “finished” the new design for my blog. There are still some optimizations to make, bugs to squash, and some cross browser issues to tackle, but this is it, in essence. If you’re viewing this page is Firefox 3.5, what you see is what I’d like it to look like in all browsers, however I may not be able to achieve that 100%.

One tool I’m using to help with cross browsers issues is Modernizr. It’s a piece of JavaScript that uses feature detection to give you booleans to refer to in terms of what the user’s browser supports, and more importantly, what it doesn’t. So for example, right now I’m using the following snippet to handle rounded corners (border-radius) in browsers that don’t support them.

if(!Modernizr.borderradius) {
  // Do stuff to handle browsers without border-radius support
}

What’s great about it is that it doesn’t use browser sniffing for feature detection. It’s not just a list of browser versions and their corresponding features, it actually attempts to use the features’ namespace, and sets a boolean if the attempt succeeds or not. Very cool.

Other stuff …

Tweets I write

Posts I write

Companies I currently develop for

Past development gigs

Things I use everyday

Ways to contact me