May 27

stylesheet.onload: or lack thereof

For a version of stylesheet.onload that works cross domain, please see: When is a stylesheet really loaded?by @stoyanstefanov

Loading stylesheets into your page dynamically can be a good way of reducing your initial page download size. You may want to load a print stylesheet in only when a user initiates a print action, you may want to allow your users to change the theme or skin of your site/ app or before displaying some content loaded in dynamically via AJAX.

There’s only one caveat, you need to know that the stylesheet has actually loaded before you start the print action, or display your content to avoid any mishaps or foucs. This is easy when using AJAX or inserting a script or image node into the page. Unfortunately though, most browsers have not implemented the same functionality for the humble link node.

Funnily enough, Internet Explorer supports the onreadystatechange event and Opera supports the onload event for link nodes. This still leaves us with a large gap of unsupported browsers and the IE and Opera only tell us that the stylesheet has completed loading and not whether it was loaded successfully. So, what are our options then?

  1. Don’t support this kind of functionality
  2. Use AJAX to load in the stylesheet’s content, create a style node and insert the XHR responseText into the style node.
  3. Dynamically create a link node, load the stylesheet in and use setTimeout to run a callback after a abitrary amount of time has elapsed (e.g. 250ms), and hope it all works out.

None of these options really seems viable, although option 2 is probably the nicest of the 3. There is one more option though: option 4. Oooohhhh…

Option 4: The bestest option so far.

Even though there is no event to listen to, we do have a way we can check a stylesheet has loaded in the remaining browsers: by checking it’s cssRules length.

In W3C compliant browsers the link node will have a sheet property which, in itself, contains a cssRules property, which, as you can probably guess, is a list of all the CSS rules in the style sheet. If the style sheet is loaded, and not empty, then:

link_node.sheet.cssRules.length > 0;

As you can expect, Internet Explorer, has a slightly different syntax:

link_node.styleSheet.rules.length > 0;

So using this little piece of knowledge and a little setInterval/ setTimeout magic we can create a script that allows us to load style sheet into the page dynamically and then fire a callback when it has completed.

With option 4, we can create a function that loads a style sheet in any browser and fires a callback when it is has loaded. The callback will tell us if it was successfull or unsuccessfull (it would only be unsuccessful if the file did not exist or the style sheet had no rules in it).

The basics

The most basic implementation of this can be done in a mesely 30 lines of — framework independent — JavaScript code:

function loadStyleSheet( path, fn, scope ) {
   var head = document.getElementsByTagName( 'head' )[0], // reference to document.head for appending/ removing link nodes
       link = document.createElement( 'link' );           // create the link node
   link.setAttribute( 'href', path );
   link.setAttribute( 'rel', 'stylesheet' );
   link.setAttribute( 'type', 'text/css' );

   var sheet, cssRules;
// get the correct properties to check for depending on the browser
   if ( 'sheet' in link ) {
      sheet = 'sheet'; cssRules = 'cssRules';
   }
   else {
      sheet = 'styleSheet'; cssRules = 'rules';
   }

   var interval_id = setInterval( function() {                    // start checking whether the style sheet has successfully loaded
          try {
             if ( link[sheet] && link[sheet][cssRules].length ) { // SUCCESS! our style sheet has loaded
                clearInterval( interval_id );                     // clear the counters
                clearTimeout( timeout_id );
                fn.call( scope || window, true, link );           // fire the callback with success == true
             }
          } catch( e ) {} finally {}
       }, 10 ),                                                   // how often to check if the stylesheet is loaded
       timeout_id = setTimeout( function() {       // start counting down till fail
          clearInterval( interval_id );            // clear the counters
          clearTimeout( timeout_id );
          head.removeChild( link );                // since the style sheet didn't load, remove the link node from the DOM
          fn.call( scope || window, false, link ); // fire the callback with success == false
       }, 15000 );                                 // how long to wait before failing

   head.appendChild( link );  // insert the link node into the DOM and start loading the style sheet

   return link; // return the link node;
}

This would allow you to load a style sheet with an onload callback function like this:

loadStyleSheet( '/path/to/my/stylesheet.css', function( success, link ) {
   if ( success ) {
      // code to execute if the style sheet was loaded successfully
   }
   else {
      // code to execute if the style sheet failed to successfully
   }
} );

Or if you want to your callback to maintain its scope/ context, you could do something kind of like this:

loadStyleSheet( '/path/to/my/stylesheet.css', this.onComplete, this );

If you want to see it in action I’ve created a test page. I’ve also created a nicer version of loadStyleSheet which:

  • Allows you to add multiple callbacks per stylesheet
  • Uses onreadystatechange and onload events for browsers that support them; and
  • Does not try and load the same stylesheet if it is still in the DOM — if you try and load the same style sheet twice it will simply fire the callback you passed, with success == true.

It has it’s own test page test page. loadStyleSheet.js is about 1kb gzipped.


Apr 6

the dragging and droppings

For the past two weeks I’ve been working on the drag and drop module for thud. Even though I had read PPK’s: The HTML5 drag and drop disaster. I thought, “Wow! I can use the new HTML5 drag and drop events to build my drag and drop module!” WRONG!!!

I won’t go into everything he’s already talked about in his post, it’s brilliant, and if you haven’t read it you absolutely should. I would like to, however, extend the list of grievances:

  • Setting a drag image — the html snippet or image you see following the cursor — is convoluted. What if I wanted to use the item I clicked on rather than a copy of it? There isn’t a method in place for that.
  • Scrolling the document instead of exiting the browser window. Currently dragging allows you to drag outside of the browser window. What if i want to restrict the dragging to within the current document and scroll the document in the direction i am dragging when i hit the edge?
  • Constraining the drag to specific coordinates in the document (e.g. { bottom : 400, left : 10, right : 400, top : 10 }).
  • Limiting the drag to only horizontal or vertical directions.
  • Snap to grid dragging (e.g. a 16px snap would only move the drag image in increments of 16px in any direction).

These options are all available in the drag and drop implementations of various frameworks I’ve worked used — MooTools, Ext, Scriptaculous, etc… — and are there for good reason, we need them to give the user a rich experience when building UI components!

My feeling is I will need 2 different types of drag and drop in thud. One will be for restricting UI components to a page (non-html5 implementation); the other will be for dragging objects from external sources to the document and vice versa (html5 implementation). WAH! :’(


Apr 1

i see your schwartz is bigger than mine

scene from the movie spaceballs

A week or two ago a perl 6 enthusiast friend of mine mentioned how he’d, “just used a Schwartzian transform to answer a guys question in #perl” during the brief passing of an otherwise routine instant message conversation.

Knowing nothing about the Schwartzian transform — in fact, only hearing of its existence for the first time — and not wanting to look like a narc in front of my nerd fanboy friend. I did what any self-respecting good guy would do. I turned to the google.

I skimmed through the wikipedia page felt slightly confused, read it properly and gained a better understanding. I also happened on this page which lists the implementation of the Schwartzian tranform in several different languages. Except one, your favourite AND mine: JavaScript.

So I decided I would implement a Schwartzian transform in JavaScript and design a crude test to see the performance benefits for myself.

The test sorts arrays of strings — containing numbers with thousands separators — in ascending order of lengths: 20, 200, 800, 1600 & 2000; and uses arrays in descending order, as well as randomly ordered arrays.

Before I explain the benefits of using a Schwartzian transform over a regular sort, I’ll go through the code required to implement a Schwartzian transform.

As it turns out, this is hella-easy and — the actual sort — only requires one line of code, if you like squeezing code onto one line. For the purposes of explaining this technique though, I will be more verbose.

function getValue( o ) {
    /* returns a specific value from item: o */
}
function compareFunction( a, b ) { // depending on how you want to sort, asc or desc, your sort function would look something like this
    var _a = a[1], _b = b[1];
    return _a == _b ? 0 : _a < _b ? -1 : 1;
}
var unsorted_array      = [/* array of stuff */], // the schwartz starts here
    paired_array        = unsorted_array.map( function( o ) { return [o, getValue( o )]; } ),
    paired_array_sorted = paired_array.sort( compareFunction ),
    sorted_array        = paired_array_sorted.map( function( o ) { return o[0]; } );
 

Breaking it down

unsorted_array — is the array of unsorted values we want to sort.

paired_array — is the result of iterating through unsorted_array to create and return, a new array, of tuples, containing the original item and the value we want to sort on. This would look something like this:


[[original_item_1, sort_value_1], [original_item_2, sort_value_2], ..., [original_item_N, sort_value_N]]
 

paired_array_sorted — is the result of sorting paired_array, though as sorting happens inline, paired_array_sorted is the same as paired_array.

sorted_array — is the result of iterating through paired_array_sorted and returning the first, original, item from each tuple.

Putting it all on one line we can get something like this:


sorted_array = unsorted_array.map( function( o ) { return [o, getValue( o )]; } ).sort( compareFunction ).map( function( o ) { return o[0]; } );
 

What’s the benefit of using a Schwartzian tranform?

When I first saw this, I thought it was kind of complex and that running a map on the array we want to sort, twice, was excessive. But have a look at the code for just using a regular sort:

function getValue( o ) {
    /* returns a specific value from item: o */
}
function compareFunction( a, b ) { // depending on how you want to sort, asc or desc, your sort function would look something like this
    var _a = getValue( a ), _b = getValue( b );
    return _a == _b ? 0 : _a < _b ? -1 : 1;
}
var unsorted_array = [/* array of stuff */],
    sorted_array   = unsorted_array.sort( compareFunction );

At first glance this looks much more elegant, I mean there is a lot less code. But in actual fact this sort can take a significantly longer amount of time to complete than the Schwartzian tranform sort, depending on the time it takes to retrieve each value from the getValue function. Why?

…the Schwartzian transform actually reduced the order of complexity of the number of times the keys are calculated from O(N*log(N)) to O(N).

Optimizing Code for Speed - wikibooks

In plain english this means:

  • Using the Schwartzian method, we calculate the values we want to sort on, for every item in the array, ONCE, storing them with their original item in a new array.
  • Using a regular sort, we are calculating the values we want to sort on, EVERY TIME the sort function compares two items in the array.
Chart time!

So with this post almost at an end, it’s time to display a chart, showing the efficiency of the Schwartzian tranform over a regular sort, for the test I created.

graph: regular sort vs schwartzian transform

I ran the test in the latest stable versions of each browser on a MacBook Pro with OS X 10.6.3. Except Internet Explorer which I tested using a VM running Windows XP. Sorry the graph times aren’t incrementing nicely, Internet Explorer’s times blow the whole graph out and I wanted to show the distributions more clearly.

As you can see though, in each browser the Schwartzian transform far out performs a regular sort for this test.

Conclusion

The Schwartzian transform is a simple method we can use to improve the performance of sorting arrays of items, where the values we wish to sort on involve expensive calculations. However, you should always consider when to use this method on a case by case basis.

PS. This is NOT an April fools’ day prank. :¬P


Mar 5

a.b.a: cross browser css

I first saw this technique in Jack Slockum’s Ext JS framework.

There are a range of CSS hacks and other types of work arounds available to target the inconsistencies in the various CSS rendering implementations of different browsers; and different browser versions.

A lot of developers I’ve worked with vehemently support the use conditional comments — this is, to insert IE specific stylesheets into a page, to target the various versions of Internet Explorer. Their reasons being:

  • It’s only picked up by Internet Explorer so other browsers don’t have to worry about downloading irrelevant CSS code.
  • You can keep all your different Internet Explorer CSS hacks separate so as you stop supporting versions you can simply remove the offending file and conditional comment code.
  • It’s neater than having all your CSS hacks in the one file.
  • CSS hacks aren’t generally valid CSS and your stylesheet will not validate if you include them in your stylesheets.

Needless to say I tend to disagree with all four of these reasons. My reasons being:

  • What about versions of Firefox, Opera and Safari that need specific hacks in place to get them to display your page correctly? Now you have some hacks in your main stylesheets and others in another file. All your hacks are all over the place, it makes bug fixing a lot more time consuming!
  • Having some styles, in a separate stylesheet, that override styles in your main stylsheet — for a specific browser/ version — makes it harder to see the overall all picture. I have to either remember, or worse, if I am not familiar with the codebase, constantly check the IE specific stylesheet to see if IE specific hacks are in place. It makes developing and debugging a lot more frustrating and time consuming.
  • I now not only have separate stylesheets to worry about, I have to add conditional comments to my templates to make sure they pick that stylesheet up, then manage the stylesheets AND the conditional comments when new versions of a browser come out. Generally this means — for sites with badly designed templates, which, from what I have seen, is a lot — regenerating, retesting and releasing the whole site again. Very time consuming!
  • It’s an extra HTTP request for only IE, especially IE6 which only supports 2 downloads per domain, and probably for only a small amount of extra CSS code.
  • IE is probably your biggest market share and you’re punishing them with this extra download.
  • It’s easier to see what’s going on when you have all your hacks for each browser grouped together rather than scattered all over the place.
  • How many users care whether or not your CSS validates? Chances are it’s going to be less than a percent of them.

A Better Approach

I’ve used Ext JS on a few big “single page application” projects over the past 4 years. It’s an amazing framework, In my opinion, it’s probably the best and most complete — inlcuding documentation and samples — JavaScript UI framework on the market, at the moment.

One of the first things I wanted to know was: how does it get it’s UI components to look the same in all supported browsers?

It definitely doesn’t use conditional comments because you only need to include one stylesheet and one JavaScript file if you want to use everything Ext has to offer. I started looking at its stylesheets to see if I could find any hacks, none.

What I did find were styles like:

.ext-ie6 .x-form-text, .ext-ie7 .x-form-text {...

In case you can’t tell, this specific rule is targetting IE’s versions 6 & 7.

So I looked at the Ext JS API doc’s generated markup in Firebug and saw this:

<body class="ext-gecko ext-gecko3 ext-mac"/>

And in Safari’s debugger, this:

<body class="ext-safari ext-mac"/>

In case you can’t guess, these classes were added to the body tag via JavaScript.

To me this is a brilliant idea! So brilliant I have used it on other projects to make it easy for all developers to write cross browser CSS. I will also be using this approach in the thudjs framework for the same reasons.

But this will only work with JavaScript turned on…

All I can say to that is, “So?”. How many of your users are actually using your site WITHOUT JavaScript turned on? Do you actually test your site(s) without JavaScript turned on, in every browser? Chances are you’re either living in a fantasy world or your site is so simple you don’t require any hacks or cross browser CSS.

This technique still conforms to the principles of graceful degredation/ progressive enhancement. You’re site may look a little b0rked to 0.001% of your users, if that. It does not mean that this miniscule percentage of users will not be able to navigate your site.

This is the technique you want to use if you want to make your CSS easier to maintain and reduce your overall development/ bug fixing time.

Imagine opening a CSS file and seeing code similar to this:

#container { background-position : left top ; background-repeat : no-repeat ; width : 100px ; }
.gecko  #container { background-image : url(firefox.png) ; height : 97px ; }
.ie     #container { height : 94px ; }
.ie6    #container { background-image : url(msie6.png) ; }
.ie7    #container { background-image : url(msie7.png) ; }
.ie8    #container { background-image : url(msie8.png) ; }
.safari #container { background-image : url(safari.png) ; height : 88px ; }
.chrome #container { background-image : url(chrome) ; height : 104px ; }

Now to me, that is an easy to read snippet of CSS. Rather than having to waste time looking for hacks or sifting through different files looking for the correct rule(s) to change. I can see the main rule and what browsers/ versions required adjustments. If there are any bugs raised with specific browsers/ versions I can make quick adjustments. If a new browser version comes out that fixes a previous CSS rendering bug or updates its CSS3 support, I can take care of it in a matter of a few minutes.

Your choice…

If your interested in this approach to cross browser CSS, I have created a standalone script available for download (less than 1kb gzipped). It creates a global object called UserAgent which you could very easily change to sit under your own namespace. There’s also a test page to display all the values contained in the UserAgent object, the classes assigned to the page’s documentElement (HTML tag) and an approximation of how long it took to complete the operation, for anyone interested in how this script will affect page speed — it’s generally less than 5ms.

However, if you get your kicks from making things difficult for yourself, well then there are, as we’ve seen, a few choices at your disposal. :^)


Page 1 of 2