{"rowid": 162, "title": "Conditional Love", "contents": "\u201cBrowser.\u201d The four-letter word of web design.\n\nI mean, let\u2019s face it: on the good days, when things just work in your target browsers, it\u2019s marvelous. The air smells sweeter, birds\u2019 songs sound more melodious, and both your design and your code are looking sharp.\n\nBut on the less-than-good days (which is, frankly, most of them), you\u2019re compelled to tie up all your browsers in a sack, heave them into the nearest river, and start designing all-imagemap websites. We all play favorites, after all: some will swear by Firefox, Opera fans are allegedly legion, and others still will frown upon anything less than the latest WebKit nightly.\n\nThankfully, we do have an out for those little inconsistencies that crop up when dealing with cross-browser testing: CSS patches.\n\nSpare the Rod, Hack the Browser\n\nBefore committing browsercide over some rendering bug, a designer will typically reach for a snippet of CSS fix the faulty browser. Historically referred to as \u201chacks,\u201d I prefer Dan Cederholm\u2019s more client-friendly alternative, \u201cpatches\u201d.\n\nBut whatever you call them, CSS patches all work along the same principle: supply the proper property value to the good browsers, while giving higher maintenance other browsers an incorrect value that their frustrating idiosyncratic rendering engine can understand.\n\nTraditionally, this has been done either by exploiting incomplete CSS support:\n\n#content {\n\theight: 1%;\t // Let's force hasLayout for old versions of IE.\n\tline-height: 1.6;\n\tpadding: 1em;\n}\nhtml>body #content {\n\theight: auto; // Modern browsers get a proper height value.\n}\n\nor by exploiting bugs in their rendering engine to deliver alternate style rules:\n\n#content p {\n\tfont-size: .8em;\n\t/* Hide from Mac IE5 \\*/\n\tfont-size: .9em;\n\t/* End hiding from Mac IE5 */\n}\n\nWe\u2019ve even used these exploits to serve up whole stylesheets altogether:\n\n@import url(\"core.css\");\n@media tty {\n\ti{content:\"\\\";/*\" \"*/}} @import 'windows-ie5.css'; /*\";}\n}/* */\n\nThe list goes on, and on, and on. For every browser, for every bug, there\u2019s a patch available to fix some rendering bug.\n\nBut after some time working with standards-based layouts, I\u2019ve found that CSS patches, as we\u2019ve traditionally used them, become increasingly difficult to maintain. As stylesheets are modified over the course of a site\u2019s lifetime, inline fixes we\u2019ve written may become obsolete, making them difficult to find, update, or prune out of our CSS. A good patch requires a constant gardener to ensure that it adds more than just bloat to a stylesheet, and inline patches can be very hard to weed out of a decently sized CSS file.\n\nGiving the Kids Separate Rooms\n\nSince I joined Airbag Industries earlier this year, every project we\u2019ve worked on has this in the head of its templates:\n\n\n\n\n\nThe first element is, simply enough, a link element that points to the project\u2019s main CSS file. No patches, no hacks: just pure, modern browser-friendly style rules. Which, nine times out of ten, will net you a design that looks like spilled eggnog in various versions of Internet Explorer.\n\nBut don\u2019t reach for the mulled wine quite yet. Immediately after, we\u2019ve got a brace of conditional comments wrapped around two other link elements. These odd-looking comments allow us to selectively serve up additional stylesheets just to the version of IE that needs them. We\u2019ve got one for IE 6 and below:\n\n\n\nAnd another for IE7 and above:\n\n\n\nMicrosoft\u2019s conditional comments aren\u2019t exactly new, but they can be a valuable alternative to cooking CSS patches directly into a master stylesheet. And though they\u2019re not a W3C-approved markup structure, I think they\u2019re just brilliant because they innovate within the spec: non-IE devices will assume that the comments are just that, and ignore the markup altogether.\n\nThis does, of course, mean that there\u2019s a little extra markup in the head of our documents. But this approach can seriously cut down on the unnecessary patches served up to the browsers that don\u2019t need them. Namely, we no longer have to write rules like this in our main stylesheet:\n\n#content {\n\theight: 1%;\t// Let's force hasLayout for old versions of IE.\n\tline-height: 1.6;\n\tpadding: 1em;\n}\nhtml>body #content {\n\theight: auto;\t// Modern browsers get a proper height value.\n}\n\nRather, we can simply write an un-patched rule in our core stylesheet:\n\n#content {\n\tline-height: 1.6;\n\tpadding: 1em;\n}\n\nAnd now, our patch for older versions of IE goes in\u2014you guessed it\u2014the stylesheet for older versions of IE:\n\n#content {\n\theight: 1%;\n}\n\nThe hasLayout patch is applied, our design\u2019s repaired, and\u2014most importantly\u2014the patch is only seen by the browser that needs it. The \u201cgood\u201d browsers don\u2019t have to incur any added stylesheet weight from our IE patches, and Internet Explorer gets the conditional love it deserves.\n\nMost importantly, this \u201ccompartmentalized\u201d approach to CSS patching makes it much easier for me to patch and maintain the fixes applied to a particular browser. If I need to track down a bug for IE7, I don\u2019t need to scroll through dozens or hundreds of rules in my core stylesheet: instead, I just open the considerably slimmer IE7-specific patch file, make my edits, and move right along.\n\nEven Good Children Misbehave\n\nWhile IE may occupy the bulk of our debugging time, there\u2019s no denying that other popular, modern browsers will occasionally disagree on how certain bits of CSS should be rendered. But without something as, well, pimp as conditional comments at our disposal, how do we bring the so-called \u201cgood browsers\u201d back in line with our design?\n\nAssuming you\u2019re loving the \u201cone patch file per browser\u201d model as much as I do, there\u2019s just one alternative: JavaScript.\n\nfunction isSaf() {\n\tvar isSaf = (document.childNodes && !document.all && !navigator.taintEnabled && !navigator.accentColorName) ? true : false;\n\treturn isSaf;\n}\nfunction isOp() {\n\tvar isOp = (window.opera) ? true : false;\n\treturn isOp;\n}\n\nInstead of relying on dotcom-era tactics of parsing the browser\u2019s user-agent string, we\u2019re testing here for support for various DOM objects, whose presence or absence we can use to reasonably infer the browser we\u2019re looking at. So running the isOp() function, for example, will test for Opera\u2019s proprietary window.opera object, and thereby accurately tell you if your user\u2019s running Norway\u2019s finest browser.\n\nWith scripts such as isOp() and isSaf() in place, you can then reasonably test which browser\u2019s viewing your content, and insert additional link elements as needed.\n\nfunction loadPatches(dir) {\n\tif (document.getElementsByTagName() && document.createElement()) {\n\t\tvar head = document.getElementsByTagName(\"head\")[0];\n\t\tif (head) {\n\t\t\tvar css = new Array();\n\t\t\tif (isSaf()) {\n\t\t\t\tcss.push(\"saf.css\");\n\t\t\t} else if (isOp()) {\n\t\t\t\tcss.push(\"opera.css\");\n\t\t\t}\n\t\t\tif (css.length) {\n\t\t\t\tvar link = document.createElement(\"link\");\n\t\t\t\tlink.setAttribute(\"rel\", \"stylesheet\");\n\t\t\t\tlink.setAttribute(\"type\", \"text/css\");\n\t\t\t\tlink.setAttribute(\"media\", \"screen, projection\");\n\t\t\t\tfor (var i = 0; i < css.length; i++) {\n\t\t\t\t\tvar tag = link.cloneNode(true);\n\t\t\t\t\ttag.setAttribute(\"href\", dir + css[0]);\n\t\t\t\t\thead.appendChild(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nHere, we\u2019re testing the results of isSaf() and isOp(), one after the other. For each function that returns true, then the name of a new stylesheet is added to the oh-so-cleverly named css array. Then, for each entry in css, we create a new link element, point it at our patch file, and insert it into the head of our template.\n\nFire it up using your favorite onload or DOMContentLoaded function, and you\u2019re good to go.\n\nScripteat Emptor\n\nAt this point, some of the audience\u2019s more conscientious \u2018scripters may be preparing to lob figgy pudding at this author\u2019s head. And that\u2019s perfectly understandable; relying on JavaScript to patch CSS chafes a bit against the normally clean separation we have between our pages\u2019 content, presentation, and behavior layers.\n\nAnd beyond the philosophical concerns, this approach comes with a few technical caveats attached:\n\nBrowser detection? So un-133t.\n\nBrowser detection is not something I\u2019d typically recommend. Whenever possible, a proper DOM script should check for the support of a given object or method, rather than the device with which your users view your content.\n\nIt\u2019s JavaScript, so don\u2019t count on it being available.\n\nAccording to one site, roughly four percent of Internet users don\u2019t have JavaScript enabled. Your site\u2019s stats might be higher or lower than this number, but still: don\u2019t expect that every member of your audience will see these additional stylesheets, and ensure that your content\u2019s still accessible with JS turned off.\n\nBe a constant gardener.\n\nThe sample isSaf() and isOp() functions I\u2019ve written will tell you if the user\u2019s browser is Safari or Opera. As a result, stylesheets written to patch issues in an old browser may break when later releases repair the relevant CSS bugs.\n\nYou can, of course, add logic to these simple little scripts to serve up version-specific stylesheets, but that way madness may lie. In any event, test your work vigorously, and keep testing it when new versions of the targeted browsers come out. Make sure that a patch written today doesn\u2019t become a bug tomorrow.\n\nPatching Firefox, Opera, and Safari isn\u2019t something I\u2019ve had to do frequently: still, there have been occasions where the above script\u2019s come in handy. Between conditional comments, careful CSS auditing, and some judicious JavaScript, browser-based bugs can be handled with near-surgical precision.\n\nSo pass the \u2018nog. It\u2019s patchin\u2019 time.", "year": "2007", "author": "Ethan Marcotte", "author_slug": "ethanmarcotte", "published": "2007-12-15T00:00:00+00:00", "url": "https://24ways.org/2007/conditional-love/", "topic": "code"}