Browser-Specific Styles with the Dojo Toolkit

By  on  

Every JavaScript library aims to cure the pain of dealing with cross-browsers JavaScript issues. The Dojo Toolkit takes that a step further by trying to help you deal with cross-browser CSS issues. Dojo features a file called "uacss.js" which adds CSS classes to the page's HTML element to allow you to more easily write browser-specific CSS. Lets take a look at how Dojo attempts to help us write more maintainable CSS.

The HTML Result

<html xmlns="http://www.w3.org/1999/xhtml" class="dj_gecko dj_ff3 dj_contentbox"> <!-- Firefox 3 / Gecko -->
<html xmlns="http://www.w3.org/1999/xhtml" class="dj_webkit dj_safari dj_contentbox"> <!-- Safari / Webkit -->
<html xmlns="http://www.w3.org/1999/xhtml" class="dj_webkit dj_chrome dj_contentbox"> <!-- Chrome / Webkit -->
<html xmlns="http://www.w3.org/1999/xhtml" class="dj_ie dj_ie8 dj_contentbox"> <!-- Internet Explorer 8 -->

All browser-specific CSS classes are added to the HTML element. How can you use these styles now?

The CSS Sample

.minHeight			{ min-height:600px; }
.dj_ie6 .minHeight	{ height:600px; } /* IE6-specific */

.dj_webkit .rounded	{ -webkit-border-radius:5px; }
.dj_gecko .rounded	{ -moz-border-radius:5px; }

All you need to do is place the browser-specific selector before the other piece of your selector.

The uacss.js JavaScript

dojo.provide("dojo.uacss");

(function(){
	// summary:
	//		Applies pre-set CSS classes to the top-level HTML node, based on:
	// 			- browser (ex: dj_ie)
	//			- browser version (ex: dj_ie6)
	//			- box model (ex: dj_contentBox)
	//			- text direction (ex: dijitRtl)
	//
	//		In addition, browser, browser version, and box model are
	//		combined with an RTL flag when browser text is RTL.  ex: dj_ie-rtl.

	var d = dojo,
		html = d.doc.documentElement,
		ie = d.isIE,
		opera = d.isOpera,
		maj = Math.floor,
		ff = d.isFF,
		boxModel = d.boxModel.replace(/-/,''),

		classes = {
			dj_ie: ie,
			dj_ie6: maj(ie) == 6,
			dj_ie7: maj(ie) == 7,
			dj_ie8: maj(ie) == 8,
			dj_quirks: d.isQuirks,
			dj_iequirks: ie && d.isQuirks,

			// NOTE: Opera not supported by dijit
			dj_opera: opera,

			dj_khtml: d.isKhtml,

			dj_webkit: d.isWebKit,
			dj_safari: d.isSafari,
			dj_chrome: d.isChrome,

			dj_gecko: d.isMozilla,
			dj_ff3: maj(ff) == 3
		}; // no dojo unsupported browsers

	classes["dj_" + boxModel] = true;

	// apply browser, browser version, and box model class names
	var classStr = "";
	for(var clz in classes){
		if(classes[clz]){
			classStr += clz + " ";
		}
	}
	html.className = d.trim(html.className + " " + classStr);

	// If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
	// We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).  
	// Unshift() is to run sniff code before the parser.
	dojo._loaders.unshift(function(){
		if(!dojo._isBodyLtr()){
			var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")
			html.className = d.trim(html.className + " " + rtlClassStr);
		}
	});
})();

You can see that this file is very key to Dojo's browser-specific CSS interactions. The classes object holds a list of possible CSS classes to add to the HTML element, ranging from browser names to browser versions and even "quirks mode" checking. The last step is to add those classes to the HTML element.

The one downside to using Dojo for your browser-specific CSS is that the user may not allow JavaScript. If your web application requires JavaScript and you're using Dojo, however, use Dojo's system of browser-specific CSS to create more readable CSS code!

Recent Features

  • By
    CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

  • By
    Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API...

Incredible Demos

  • By
    Create a Dojo Lightbox with dojox.image.Lightbox

    One of the reasons I love the Dojo Toolkit is that it seems to have everything.  No scouring for a plugin from this site and then another plugin from that site to build my application.  Buried within the expansive dojox namespace of Dojo is

  • By
    GitHub-Style Sliding Links

    GitHub seems to change a lot but not really change at all, if that makes any sense; the updates come often but are always fairly small. I spotted one of the most recent updates on the pull request page. Links to long branch...

Discussion

  1. good tut man :)

  2. I can’t help but think this is a very bad idea in general. Normally the only browser you need to target is IE, for which conditional comments work great. Past that, feature detection, not browser detection, is pretty much the accepted way to build web sites/applications. On top of the fact that browsers can spoof their user agents, I am trying to figure out how this could be useful.

    It seems you could build a site that would easily break with future browser updates by using this method.

  3. @Doug Neiner: I do like some of your points. If you’re married to Dojo, I still think this is very useful.

    Most would agree with you about user agent detection but I don’t. My feeling is that users who install plugins, etc. to spoof their UA know that the website may not work the same. I’m probably the minority there but it’s just how I feel.

  4. and this is some of the reasons why I go to a website and it looks aweful. This is a really bad idea. As Doug mentioned before you should NEVER target a specific browser (besides IE (always an exception))

    You can see this problem in effect in your code line #30.

    “// NOTE: Opera not supported by dijit ”

    Perfect example of the fail in this code. This also fails on the next versions of any browser that no longer need the hacks.

    You are just asking for more work and more errors in the future writing code like this.

  5. In practice, this is exactly how the uacss “sniff” gets used: a class hook for version-specific IE-targeted rules, and occasionally for rules targeting differing box models, or some janky FF or Safari bug. The other key value of this technique is for those use cases where you don’t own the entire page, so quirks/standards mode is not under your control, and adding conditional comments may also be out of the question.

    It’s also undoubtably handy for a quick hack, and while that might or might not come around to bite you in the long run, if you’ve never needed a quick targeted css fix, you’re living in some strange parallel universe I’ve not visited.

  6. This is a good method for browser-specific hacks, but I think parsing the useragent string with PHP might be a better way of going about it, since you already don’t care if someone has a spoofed useragent.

    It only takes 1 line of code for each browser, and you can just load in custom CSS styles.

    You might also like: http://www.modernizr.com/

  7. Buena esplicacion, nice man :-)

  8. Buena esplicacion, nice man :-)

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!