Archive for December 26th, 2012

The bilirubin graph uses jQuery UI and used to use the official theme switching widget, which I just discovered has been broken since September 2012. That's the problem with graceful degradation; when something breaks the page still works so smoothly I don't notice unless I'm paying close attention. Anyway, David Hoff has written a replacement, Super Theme Switcher which works well, but requires an explicit option to tell the plugin where to find the sample images:

$('#themeswitcher').themeswitcher({
	imgpath: '/inc/themeswitcher/images/'
});

The other thing I needed was a way to detect the change in theme. themeswitcher has an onSelect callback, but that is called when the new stylesheet is added to the page, not when it is actually loaded. I previously used a hack with fake images. But that was a terrible hack. Chrome still doesn't trigger load events for stylesheets, so that won't work. But all the modern browsers support CSS transitions, including the transitionend event (webkit requires a prefix).

So to detect a change in stylesheet that affects, say, elements with a class of ui-state-default, do the following:

var div = $('<div class=ui-state-default>').css({
  transition: 'color 0.01s',
  webkitTransition: 'color 0.01s',
  visibility: 'hidden' // make sure the element is invisible
}).appendTo('body').on('transitionend webkitTransitionEnd', function(){
  alert $(this).css('color'); // or whatever
});

Internet Explorer, of course, doesn't support CSS transitions (we'll see about IE 10), but luckily they are the only ones to correctly support stylesheet load events and we can use $('link:last').one('load', function(){ whatever });. So we can detect stylesheet loading in either case, and create a new version of themeswitcher that calls the onSelect callback when the stylesheet is loaded, not when it is added:

// create the test div
var div = $('<div class=ui-state-default>').css({
	transition: 'color 0.01s',
	webkitTransition: 'color 0.01s',
	visibility: 'hidden'
}).appendTo('body');

 // decide if we can handle CSS transitions
$.support.transition =
 document.body.style.transition !== undefined ||
 document.body.style.webkitTransition !== undefined;

$.fn.themeswitcher2 = function(opts){
	if (opts.onSelect && $.support.transition){
		div.on('transitionend webkitTransitionEnd', opts.onSelect);
		delete opts.onSelect;
	}else if (opts.onSelect){
		// this is really browser sniffing; it turns out that the only major browser that doesn't support transitions is IE, and they support
		// the stylesheet onload event
		// We know that themeswitcher adds the last stylesheet, so tie into that load event
		var onSelect = opts.onSelect;
		opts.onSelect = function(){
			$('link:last').one('load', onSelect);
		};
	}
	return this.themeswitcher(opts);
};

And this works in Chrome, Firefox, Opera and IE. See it in action on the bilirubin graph.