Dissecting a JavaScript CSS Browser Selector

If you are a web developer and have not seen Rafael Lima’s CSS Browser Selector yet, you should check it out.  It is a 1K JavaScript file that lets you easily add CSS to your web page for specific browsers, operating systems, and platforms.  I used it on our NITRC web site to fix a alignment problem I was having in the Chrome browser and it worked great.

Here is how it went for me.  I started with css that looked like this:

.ce_button {
       background: url(“/images/ce_button_right.png”) no-repeat scroll right top transparent;
       cursor: pointer;
       height: 34px;
       padding-right:10px;
       line-height: 34px;
       display: inline-block;
}
 

The problem was that the background image didn’t line up exactly the way I wanted…but only on Chrome and Safari.  For those browsers I needed to specify a smaller line height and padding.  There are a lot of ways to fix this problem.  You can write some code on the server side to emit specific CSS for specific browsers, but that can get messy because now you have extra code AND you have CSS in your source code rather than in its own files.  The best solution would let you tag a block of CSS in your style sheet as only active for certain browsers or platforms, and that is pretty much what CSS Browser Selector does.

 All I had to do was add the script line in my site template:

<script type=“application/javascript” language=“JavaScript” src=”/scripts/css_browser_selector.js”></script>

And then in my css file I put an extra selector just below my original.  So the new css looked like:

.ce_button {
       background: url(“/images/ce_button_right.png”) no-repeat scroll right top transparent;
       cursor: pointer;
       height: 34px;
       padding-right:10px;
       line-height: 34px;
       display: inline-block;
}

/* Fix a gap between right and left side of buttons only on webkit */
.webkit .ce_button {  
       line-height: 30px;
       padding-right: 8px;
}

 

No changes were needed on the server side or in my HTML.  My span tag with the .ce_button had the adjusted height and padding when viewed on Chrome and Safari.  All other browsers were unaffected.  Exactly the effect that I wanted.

Of course my immediate question was “how does it work?”  Like many production level JavaScripts, the code was lean and mean.  Single letter variable names, no extra spaces, etc.  Here is the entire script:

/*
CSS Browser Selector v0.4.0 (Nov 02, 2010)
Rafael Lima (http://rafael.adm.br)
http://rafael.adm.br/css_browser_selector
License: http://creativecommons.org/licenses/by/2.5/
Contributors: http://rafael.adm.br/css_browser_selector#contributors
*/
function css_browser_selector(u){var ua=u.toLowerCase(),is=function(t){return ua.indexOf(t)>-1},g=‘gecko’,w=‘webkit’,s=‘safari’,o=‘opera’,m=‘mobile’,h=document.documentElement,b=[(!(/opera|webtv/i.test(ua))&&/msie\s(\d)/.test(ua))?(‘ie ie’+RegExp.$1):is(‘firefox/2′)?g+’ ff2’:is(‘firefox/3.5′)?g+’ ff3 ff3_5’:is(‘firefox/3.6′)?g+’ ff3 ff3_6’:is(‘firefox/3′)?g+’ ff3’:is(‘gecko/’)?g:is(‘opera’)?o+(/version\/(\d+)/.test(ua)?’ ‘+o+RegExp.$1:(/opera(\s|\/)(\d+)/.test(ua)?’ ‘+o+RegExp.$2:”)):is(‘konqueror’)?‘konqueror’:is(‘blackberry’)?m+’ blackberry’:is(‘android’)?m+’ android’:is(‘chrome’)?w+’ chrome’:is(‘iron’)?w+’ iron’:is(‘applewebkit/’)?w+’ ‘+s+(/version\/(\d+)/.test(ua)?’ ‘+s+RegExp.$1:”):is(‘mozilla/’)?g:”,is(‘j2me’)?m+’ j2me’:is(‘iphone’)?m+’ iphone’:is(‘ipod’)?m+’ ipod’:is(‘ipad’)?m+’ ipad’:is(‘mac’)?‘mac’:is(‘darwin’)?‘mac’:is(‘webtv’)?‘webtv’:is(‘win’)?‘win’+(is(‘windows nt 6.0′)?’ vista’:”):is(‘freebsd’)?‘freebsd’:(is(‘x11’)||is(‘linux’))?‘linux’:”,‘js’]; c = b.join(‘ ’); h.className += ’ ‘+c; return c;}; css_browser_selector(navigator.userAgent);

 

After a little reformatting, it starts to look more legible:

function css_browser_selector(u) {
       var ua = u.toLowerCase(),
       is = function(t) {
              return ua.indexOf(t) > ‑1
       },
       g = ‘gecko’,
       w = ‘webkit’,
       s = ‘safari’,
       o = ‘opera’,
       m = ‘mobile’,
       h = document.documentElement,
       b = [(!(/opera|webtv/i.test(ua)) && /msie\s(\d)/.test(ua)) ? (‘ie ie’ + RegExp.$1) : is(‘firefox/2′) ? g + ’ ff2’ : is(‘firefox/3.5′) ? g + ’ ff3 ff3_5’ : is(‘firefox/3.6′) ? g + ’ ff3 ff3_6’ : is(‘firefox/3′) ? g + ’ ff3’ : is(‘gecko/’) ? g : is(‘opera’) ? o + (/version\/(\d+)/.test(ua) ? ’ ’ + o + RegExp.$1 : (/opera(\s|\/)(\d+)/.test(ua) ? ’ ’ + o + RegExp.$2 : ”)) : is(‘konqueror’) ? ‘konqueror’ : is(‘blackberry’) ? m + ’ blackberry’ : is(‘android’) ? m + ’ android’ : is(‘chrome’) ? w + ’ chrome’ : is(‘iron’) ? w + ’ iron’ : is(‘applewebkit/’) ? w + ’ ’ + s + (/version\/(\d+)/.test(ua) ? ’ ’ + s + RegExp.$1 : ”) : is(‘mozilla/’) ? g : ”, is(‘j2me’) ? m + ’ j2me’ : is(‘iphone’) ? m + ’ iphone’ : is(‘ipod’) ? m + ’ ipod’ : is(‘ipad’) ? m + ’ ipad’ : is(‘mac’) ? ‘mac’ : is(‘darwin’) ? ‘mac’ : is(‘webtv’) ? ‘webtv’ : is(‘win’) ? ‘win’ + (is(‘windows nt 6.0′) ? ’ vista’ : ”) : is(‘freebsd’) ? ‘freebsd’ : (is(‘x11’) || is(‘linux’)) ? ‘linux’ : ”, ‘js’];

       c = b.join(‘ ’);
       h.className += ’ ’ + c;
       return c;
};

css_browser_selector(navigator.userAgent);

 

So what’s going on? 

There is one main function css_browser_selector().  The last line of the script calls the function with the argument of the current userAgent.  Inside the main function is a nested function, is(), which returns true if the function input is contained anywhere in the userAgent. 

b is the most interesting variable.  It’s initialization takes up most of the script.  There are three elements, the first is the browser, the second is the platform, and the third is the constant ‘js’.  The browser and the platform component of the array are set by a series of chained ternary operators to save space, but logically it is the same as a series of nested if-then-else statements.  The code block checks the userAgent against every supported browser and platform and returns the correct string for the given input.  When b’s initialization is complete it will be an array that looks something like: [ ‘webkit chrome’, ‘win’, ‘js’] for chrome or [‘ie ie9’, ‘win’, ‘js’] for Internet Explorer 9.

Once the contents of b is all set, the array is converted into a single string and is added to the class name attribute of the document.  That way your document will always have the class ‘ie’ or ‘webkit’ or ‘android’ as appropriate. 

Now any css selectors can use .ie or .iphone or whatever you need and it will only apply for that browser.

2 responses to “Dissecting a JavaScript CSS Browser Selector

  1. I’ve tried implementing this script in a number of ways on a wordpress site I am using. I can’t get it to work. The site is different across browsers and on the same browser on different operating systems. This would be perfect. http://concursolectoresinfantiles.cl
    Can you offer some advice?
    Thanks,
    Eileen

Comments are closed.