Bug Fixes: the Hidden Value of JS Libraries

Paul Irish points out a huge value added from jQuery that few people (even the jQuery home page) seem to acknowledge, which is that it transparently works around dozens of browser bugs. If you consider older versions of jQuery this is probably more like hundreds of bugs, and many of them have been big showstoppers that even crashed browsers.

Fans of the “vanilla.js” movement—an understandable reaction to mindless library use—tend to understate what a minefield browser APIs tend to be when you consider the wide variety of browsers in use (take a sobering dip into your site’s browser stats). There’s nothing wrong with using and learning the real APIs, of course. There are big sections of DOM, Events, and friends that are very well supported, but if you’re not extremely careful, as soon as your site picks up traffic you can count on “vanilla” code breaking for users with browsers that don’t auto-update.

The moral: Even if you’re a JS pro and know the native APIs, if you’re doing anything substantial with them, then using jQuery—or some other battle-tested library—isn’t so mindless; you’re improving UX for some end users.

Why doesn’t someone create a library that just fixes the bugs? Dean Edwards was an early experimenter here with base2 and IE7.js, but as far as I remember these were mostly combed over by the JS nerds who were working on their own libs. Combining workarounds with other useful bits just gave you more bang for the buck, and, frankly, the standard APIs like DOM have never been particularly elegant to work with anyway (it took the W3C way too long to even acknowledge that giving devs the ability to parse markup (innerHTML) was something browsers might want to offer!).

JSMin’s classic delimma: division or RegExp literal

JSMin uses a pretty crude tokenizer-like algorithm based on assumptions of what kind of characters can precede/follow others. In the PHP port I maintain I’ve made numerous fixes to handle most syntax in the wild, but I see no fix for this:

Here’s a very boiled down version of a statement created by another minifier:

if(a)/b/.test(c)?d():e();

Since (a) follows if, we know it’s a condition, and, if truthy, the statement /b/.test(c)?d():e() will be executed.

However, JSMin has no parser and therefore no concept that (a) is an if condition; it just sees an expression. Since lots of scripts in the wild have expressions like (expression) / divisor, we’ve told JSMin to see that as division. So when JSMin arrives at )/, it incorrectly assumes / is the division operator, then incorrectly treats the following / as the beginning of a RegExp literal.

Lessons: Don’t run minified code through JSMin*, and don’t use it at all if you have access to a JavaScript/Java runtime.

*Minify already knows to skip JSMin for files ending in -min.js or .min.js.

Simpler Masonry + Sortable Working Together

Since jQuery Masonry repositions elements purely by positioning, it does not play well with UI Sortable. People have posted complex solutions to this problem, but this simpler solution worked for me:

  1. Refresh masonry layout on Sortable’s start, change, and stop events
  2. While dragging, remove from the dragged item the class used to indicate it’s a masonry item
var $c = $('#my_container');
$c.masonry({
    itemSelector: '.masonry-item'
});
$c.sortable({
    start: function (e, ui) {
        ui.item.removeClass('masonry-item');
        $c.masonry('reload');
    },
    change: function (e, ui) {
        $c.masonry('reload');
    },
    stop: function (e, ui) {
        ui.item.addClass('masonry-item');
        $c.masonry('reload');
    }
});

jQuery.Deferred() is pretty easy

I was using an asynchronous file uploader and, for usability, wanted to make sure the upload progress bar was displayed for at least a couple seconds before changing the view. The jQuery.Deferred object made this a breeze, eliminating a bunch of callback/isDone checking mess:

var uploadFinished = $.Deferred(),
    timerFinished = $.Deferred();
$.when(uploadFinished, timerFinished).done(function () {
    changeView();
});

// immediately after starting upload
setTimeout(timerFinished.resolve, 2000);

// in upload completed event handler
uploadFinished.resolve();

The docs make the Deferred object a little more complicated than it really is. You don’t have to have to alter processes to return Deferred/Promise objects, you can just make them and pass them around as needed in a pinch.

Designing a Highly Reusable HTML Component

The UF College of Education uses a header/footer template designed to be applied to any page, and we use this across several applications such as WordPressMoodleElgg, and Drupal. Changes can be propagated quickly to all sites, and adding the template to new PHP apps is trivial.

If you need to create an HTML component that can be reused across a wide set of sites/apps, these guidelines might help.

Avoid HTML5 elements if you can

HTML5 elements like header must be accompanied by JS to fix compatibility in old browsers. Sticking to HTML4 also helps with validation under HTML4/XHTML doctypes. Of course, if you’ll only be deploying to HTML5 sites that already have the IE shim, go ahead and use the best markup elements for the job.

Guard the host markup against the component CSS

Any component CSS selectors that match host markup elements can cause massive problems in the host application, and if this component is applied across an entire site, it’s very difficult to predict the impact. The key then is following a few simple rules for the component CSS:

  1. Each class/id name must be sufficiently unique such that there’s practically no chance of name collision. Unfortunately even selectors like .hidden and .clearfix could be implemented in different ways in the host app and this could cause problems. Using a constant prefix in every name might help.
  2. Each selector must include at least one of the component classes/ids.
  3. Avoid using a CSS reset/normalizer. If you must, make sure each selector follows the above rules so the effect of this is limited in scope to the component.
  4. Selectors must not match non-component elements. E.g. the selector #component-root + div should not be used because it would select a DIV element after the component.
  5. Take care to avoid obscuring elements in the host page. E.g. negative margins could pull the component over a host element.

Guard the component markup against the host CSS

Similarly, host CSS could break the desired styling of the component markup.

  • Test the component in a wide variety of pages and applications. Especially test pages that use common CSS resets and normalizers, and that have a lot of element-only selectors in the CSS.
  • When interactions occur, make the affected element’s selector more specific until the component CSS “wins”. As always, test across the browsers you need to support; IE7 still has some specificity bugs for the selectors it understands, if you need to care about that.

Javascript Tips

Expose as little to the global namespace as possible

E.g., define all necessary functions and variables inside an anonymous function that is executed:

!function () {
  // your code here ...

  // explicitly expose an API
  this.myComponentAPI = api;
}();

Document your script’s dependencies and let the implementor supply those

Automatically including JS libraries may break the host app. Consider the case of jQuery: Many plugins extend the jQuery object, so redefining it removes those added functions (actually stores them away, but it will break the host app nonetheless). Don’t assume the user did this right. Wrap your functionality in a condition that first tests for the presence of the the library/specific features you need, and make it easy for the implementer to realize the problem if they have a console open.

Here’s an example of how to test for jQuery’s on function:

if (this.jQuery && jQuery.fn.on) {
  // code
} else if (this.console && console.log) {
  console.log('Requires jQuery.on, added in version 1.7');
}

Assume the component could be embedded after the page loads, and multiple times

Carefully consider the initialization process your component requires. In some cases it’s reasonable to leave the initialization to be triggered by the implementer. If you do automatically use DOMReady functions like jQuery’s ready(), consider allowing the implementer to cancel this and initialize later.

My Moodle Page, Now With 99.6% Fewer Queries

My work recently upgraded from Moodle 1.9 to 2.3, and some users were experiencing slow page loads while the site was zippy for others. Today we discovered that, for some users, the My Moodle dashboard page was requiring several thousands of DB queries. For one user, enrolled in four courses, the page required over 14,000 queries. I guess it could be worse: one user reported over 95,000!

This was completely unacceptable; this page is a common navigation point for all users, and every user is forwarded there on login.

With xdebug and Autosalvage rocking, it only took me about three hours to rework the page so that only the course links are displayed by default, and a button is provided beside each to load that course’s activities into the page via Ajax. Since most users just use the page for navigation between courses, this tradeoff seems well worth the performance gain. Now this page–without displaying activities–is down to ~60 queries for every user (sadly average for a Moodle page).

I suspect that loading the activity list for a large course will still take a performance bite, but in my limited testing it seemed pretty instantaneous–yes, there’s a reason why modern apps are built on Ajax. Although good work has been done to cache front-end files, Moodle still seems to be in serious need of query reduction optimization when building HTML pages.

After getting some feedback over the weekend, I’ll release this patch for other Moodle providers. Our theme uses jQuery and the Javascript side of this was maybe 10 lines, but I imagine it would need to be ported to YUI to get into core.

Closure Compiler is smarter than you

I’ve known about Google’s Javascript minifier, Closure Compiler, for awhile, but after sending a couple snippets through it today I realized it’s a pretty impressive piece of work.

Input:

function howManyMatch(arr, pattern) {
   var l = arr.length;
   var total = 0;
   var i = 0;
   for (; i < l; i++) {
       if (pattern.test(arr[i])) {
           total++;
       }
   }
   return total;
}

Output:

function howManyMatch(b,d){for(var e=b.length,c=0,a=0;a<e;a++)d.test(b[a])&&c++;return c};

Or with whitespace re-added:

function howManyMatch(b, d) {
    for (var e = b.length, c = 0, a = 0; a < e; a++)
        d.test(b[a]) && c++;
    return c
}

Optimizations:

  • shortened parameter names and removed unneeded semicolons (YUI Compressor already did this much)
  • combined multiple var statements into one declaration
  • moved that declaration into the first section of the for loop (to save one semicolon!)
  • removed brackets around blocks with only one statement
  • replaced if statement with && operator (to save two bytes)

Several of these are often done by JS devs by hand. With closure compiler on tap, we no longer have to. Just concentrate on readability and maintainability and let CC worry about size optimization.

Using jQuery Before It’s Loaded

It’s better to include scripts like jQuery at the end of the BODY, but this makes its methods inaccessible earlier in the page (e.g. inside a WordPress post). What you can do is use a script like the one below to queue DOMReady functions before jQuery loads.

(function (w) {
    if (w.$) // jQuery already loaded, we don't need this script
        return;
    var _funcs = [];
    w.$ = function (f) { // add functions to a queue
        _funcs.push(f);
    };
    w.defer$ = function () { // move the queue to jQuery's DOMReady
        while (f = _funcs.shift())
            $(f);
    };
})(window);

Minified:

Setup

  1. Link to the script in the HEAD or at least before your typical page content area
  2. Link to jQuery at the end of BODY
  3. Below jQuery, include: <script>defer$()</script>

Now in the BODY you can use $(function(){ ... }); to queue up a function as if jQuery were already loaded. And, thanks to the fact that Javascript identifiers are referenced at run-time, you can call any other jQuery functions inside your queued function.

With some modifications you can use this pattern for any library with a similar DOMReady implementation.

Tiny Email Munging Script

I’ve seen a lot of these that are bloated/less effective/inaccessible, so I might as well put this out there. It’s simple enough to modify if you’re comfortable with Javascript.

Markup: <a href="mailto:john{@}example{.}org">john{@}example{.}org</a>

(function(){
  var a, i = 0, o = this.onload;
  onload = function(){
    o && o(); // run the old window.onload if existed
    while (a = document.getElementsByTagName("a").item(i++))
      if (0 == a.href.indexOf("mailto:")) {
        a.href = a.href.replace(/[{}]/g, "");
        a.innerHTML = a.innerHTML.replace(/{(.)}/g, "$1");
      }
  };
})();

Minified 234B:

for jQuery 150B:

IE9 May Raise the Bar

Wow.

IE9 passing a bunch of tests

IE9 is coming, and it looks like it’ll get Microsoft back in the game. Full Developer Guide.

The Good: New standards supported, hardware-accelerated canvas, SVG, Javascript speed on par with the other browsers, preview installs side-by-side with IE.

The Bad: Not available on XP and no guarantee it will be. XP users will be stuck with, you know, all the other browsers that run their demos fine.