Resources Browser Sniffing

Error: JavaScript is needed to render this site: please enable it.

This gives information about making code which differs for different browsers, using client-side brow­ser snif­ing and other techniques.

Sometimes, to overcome browser differences, it’s necessary to use different code for different browsers, especially to cater to older browsers which comply poorly with browser standards. Different code should be used as seldom as possible: ideally never; this author’s experience is that modern browsers need different code very, very rarely.

There are several ways to do this, with different levels of reliability, for different purposes:

Examples are given below.

Internet Explorer Conditional Comments

This technique enables different HTML to be parsed by IE, by different versions of IE, or by all browsers other than IE. With this technique HTML can be enclosed in a special form of comments in such a way that browsers will view the HTML as comments instead of HTML unless the conditions specified in the comments are just right.

Microsoft introduced conditional comments with IE5 for Windows. They are supported by IE for Windows from versions 5 to 9, though not 10+.

The big advantage of conditional comments is that they are completely reliable, which isn’t something which can be said of other techniques.

The big disadvantages are that a they are HTML only, so can’t be put in CSS or JavaScript files, b they hide the conditional HTML, so validators can’t see the HTML, allowing hidden errors to lurk, and c they are not supported by all versions of IE. There are several simple and obvious ways to overcome the first problem, e.g. putting the code specific to certain browsers in separate CSS or JavaScript files, and using conditional comments to link to the appropriate files. The best way to overcome the second problem is to keep any HTML within conditional comments both short and simple. The only way to overcome the third problem is to carefully analyze the userAgent string.

One use of conditional comments, as noted in the sidebar, is to link to stylesheets which only certain versions of IE will see. For example, the following conditional comment appears in this page:

HTML (IE) <!--[if lt IE 6]> <link rel="stylesheet" type="text/css" href="style_ie5x.css"> <![endif]-->

This links to a special stylesheet for IE less than IE 6. The conditional comments follow HTML (not shown above) which links to a standard stylesheet, so the IE stylesheet simply augments the standard stylesheet, typically to overcome IE defects or hide IE-specific CSS such as filters and scrollbar styling. One benefit of putting IE-specific CSS in IE-specific stylesheets is that this CSS will be hidden from CSS validators, which avoids messy validator error messages.

Microsoft documents conditional comments completely. One detail which is commonly overlooked is that, to specify IE 5.5, it must be referred to as version 5.5000.

JavaScript Object Detection

This technique uses browser differences in JavaScript objects to do things differently in different browsers.

The big advantage of object detection is that it doesn’t require that the browser be identified, which dispenses with problems there might be with faulty browser identification.

The big disadvantage is that one must take great care in examining objects: an object must not be used unless it exists, but the fact that it exists doesn’t always means that it can be used. For example:

Object detection is typically used either to overcome a browser defect, or to take advantage of a browser feature which isn’t universal.

An example of object detection to overcome a defect appears in the above function, getElement(), which overcomes the failure of old versions of IE to support the standard document.getElementById().

An example of detection to take advantage of features which are not universal would be detection of canvas.getContext() to determine whether the browser supports JavaScript related to the HTML 5 <canvas> tag.

CSS Tricks

This technique uses browser CSS differences to make different browsers see different CSS.

This is complex, and quite well described elsewhere, for example in Eric Meyer’s Tricking Browsers and Hiding Styles, so won’t be discussed here further. Note that he mentions some tricks which apply only to extinct browsers.

Caution: some tricks depend on bugs in how browsers process invalid CSS, or on how well browsers support CSS features, so the tricks may not work, or may not work exactly the same, when browsers are updated.

Media Queries

This technique uses CSS 3 media queries to load stylesheets for devices with smaller screens, e.g. for smartphones and tablet PCs. Typically the stylesheet will override the normal stylesheet to adjust the normal layout for small screens: if the site is designed in accordance with fluid design principles, the mobile stylesheet is often fairly small. This technique is simple and well supported by better smartphones.

For example this loads stylesheet style_mob.css only if the screen width is 480px or less:

HTML <link type="text/css" href="style_mob.css" rel="stylesheet" media="only screen and (max-width:480px)">

For details see the pertinent CSS 3 specification.

JavaScript Browser Detection

This technique involves JavaScript code — commonly called a brow­ser snif­fer — which identifies a browser by exa­mining certain objects, chiefly the navigator.​userAgent string.

This presents two browser sniffers:

This is the least reliable approach to dealing with browser dif­fe­ren­ces, but it’s more reliable than many give it credit for, and it often works well when other techniques can’t be applied. Browser snif­fers have a sordid reputation, largely because of the widespread use of snif­fers which are far too simplistic. The earliest snif­fers dealt only with versions of IE and Netscape which today are extinct, e.g. IE 3–4 and Net­scape 3–4. The early snif­fers could not handle the range of browsers which appeared later, and in many cases the snif­fers were never updated, thus many sites broke with newer browsers. This en­couraged browser makers to set userAgent strings which mimicked those of more common browsers, to make broken sites think the browser was one they expected: but this made more subtle snif­fers necessary for correct identification. Also, many snif­fers updated to handle newer browsers are very na­ïve: e.g. they assume that, if 'MSIE' appears in the user agent string, the browser is Internet Ex­plor­er, overlooking the fact that userAgent strings of other browsers, such as Opera, may contain 'MSIE'. But truly good snif­fers are pos­sible, e.g. the ones below.

The big disadvantage to brow­ser snif­ing, of course, is that it requires that JavaScript be enabled.

Another disadvantage is that it relies on the userAgent string, and this string can be faked: e.g., someone using Firefox could use a plugin which gives it the same userAgent as IE. This dis­advantage is not, however, as serious as one might think, since one would use such a plugin only for a site which has a snif­fer so bad that the site can’t work with the right user agent. No one would need to fake the user agent for a site with a good snif­fer.

Version Vec­tors

One element of browser detection is extracting and dealing with version vec­tors.

A version vector indicates the version of a browser. It consists of substrings of digits separated by periods, the first substring of digits being the major version number. For example, 82.0.4227.43 is a version vector, with 82 being the major version number.

Naïve browser sniffers may assume limits to version numbers, e.g. may assume a major version number is 1–2 digits long. Such browser sniffers will break when the major version number exceeds 99. The browser sniffers presented below have no such limits.

Note: the browser sniffers presented below assume that ver­sion vec­tors consist only of digits and periods.

Browser Engines

It can be more useful to detect the browser engine (not the browser) and the browser engine version (not the browser version), since it is the browser engine and version, not the browser and version, which determines how pages are rendered.

A Modest Browser Sniffer

This describes a brow­ser snif­fer I designed which I think is very good. It has four parts:

See also: Browser Engine Sniffer.

If JavaScript is enabled the code below reliably detects IE for IE 5–9 for Windows with 100% certainty due to the depend­ence on conditional comments, even if the user agent has been faked.

Identification of other browsers will be extremely reliable: not 100%, but close. This is partly because the certain detection of IE 5–9 eli­mi­nates a lot of possible errors, but also because the code is very careful in analyzing the user agent: for exam­ple, before checking to see if the user agent contains 'gecko/', it excludes browsers such as Opera whose user agents sometimes contain 'gecko/' in order to mimic Gecko user agents. The code is likely to misidentify the browser only if the browser isn’t IE 5–9 and if the entire user agent string has been faked: and, as pointed out above, the user would have no need to fake the user agent string for a site with a good brow­ser snif­fer; in practical terms, therefore, this code is exceed­ing­ly reliable.

The least reliable information is the browser version vector: it should be correct for IE or Opera. More reliable identification of the version is pos­si­ble: I have code to do it, but isn’t listed here because it rarely matters, since few people use very old ver­sions of browsers other than IE. NB​: for IE 8–11 the version vector may be wrongly identified because IE 8–11 may emu­late an older version of IE; for example, if IE 8 decides to render a page in IE 7 mode, the version vector will be 7, not 8; but in this case the version vector, though wrong in fact is right in practical terms. NB​: reliable identification of the version of Safari can be very hard, because the user​Agent string for older versions of Safari doesn’t contain the version vector, but instead contains build numbers which can only with effort be mapped to the version vec­tors.

This brow­ser snif­fer detects a wider range of browsers than many sites will need to recognize, e.g. MSN-TV (WebTV). More­over, this snif­fer retains a complete version vector rather than a major version vector, e.g. 4.1.1 instead of just 4. This is intentional: a key concept of the above code is that it’s a standard block of code which can be plugged into any site, and which can later be safely replaced by an updated equi­va­lent. The overhead of detecting things unneeded for some sites is a small price for having a single reliable, reüsable module.

Caution: this brow­ser snif­fer may be updated from time to time in order to handle critical changes in user agent strings. For example, it was updated when IE 11 appeared because IE 11 has a user agent string which is so different that the pre­vious version of the brow­ser snif­fer failed to reliably detect IE 11. If, therefore, you use this brow­ser snif­fer, you will have to be careful to check here regularly for any updates.

Browser Sniffer Part 1 : HTML

The HTML goes in the <head> section, and creates a variable isTrulyIE only if the browser is IE 5–9 for Windows:

HTML (IE) <!--[if gte IE 5]> <script type="text/javascript"> var isTrulyIE = true; </script> <![endif]-->
Browser Sniffer Part 2 : cBrowser() Constructor 

This JavaScript constructs an object with browser information, taking two optional arguments: a string to be used as the user​Agent string, with the real userAgent as the default; and a string to be used as the appVersion string, with the real appVersion as the default. (You can find this code in the text file at ../js/cBrowser.js — with tabs assumed set at 4-space intervals.)

Note: this cannot identify a browser such as Brave, Vivaldi, or TOR, whose userAgent disguises them as, for example, Chrome or Firefox.

JavaScript // August 16, 2023 'use strict'; function cBrowser ( ua, ver ) { if ( arguments.length == 0 ) ua = navigator.userAgent; this.UA = ua; this.ua = ua.toLowerCase(); if ( arguments.length < 2 ) ver = navigator.appVersion; this.isKhtml = this.isAvast = this.isChrome = this.isEdge = this.isEdgeChrome = this.isIE = this.isMidori = this.isNorton = this.isSafari = this.isOpera = this.isGecko = this.isGoanna = this.isNetscape = this.isVivaldi = this.isWebtv = false; this.isMobile = (this.ua.indexOf('tablet') != -1) || (this.ua.indexOf('android') != -1) || (this.ua.indexOf('ipad') != -1) || (this.ua.indexOf('iphone') != -1); if ( typeof(isTrulyIE) != 'undefined' ) { this.isIE = true; } else { if ( this.ua.indexOf(" edge/")!=-1 ) this.isEdge = true; else if ( this.ua.indexOf(" edg/")!=-1 ) this.isEdgeChrome = true; else { this.isAvast = (this.ua.indexOf("avast/") != -1); this.isKhtml = (this.ua.indexOf("khtml") != -1); this.isMidori = (this.ua.indexOf("midori/") != -1); this.isNorton = (this.ua.indexOf("norton/") != -1); this.isVivaldi = (this.ua.indexOf("vivaldi") != -1); this.isChrome = (this.ua.indexOf("chrome/") != -1) && !this.isAvast && !this.isMidori && !this.isNorton && !this.isVivaldi; this.isSafari = (this.ua.indexOf("safari") != -1) && !this.isAvast && !this.isChrome && !this.isMidori && !this.isNorton; this.isOpera = (this.ua.indexOf("opera") != -1) || (this.ua.indexOf("opr/") != -1); this.isOperaPresto = (this.ua.indexOf("opera") != -1) && (this.ua.indexOf("presto/") != -1); this.isGecko = !this.isKhtml && !this.isChrome && !this.isSafari && !this.isOpera && !this.isVivaldi && (this.ua.indexOf("gecko/") != -1); this.isGoanna = (this.ua.indexOf("goanna/") != -1); this.isNetscape = !this.isKhtml && !this.isOpera && ( (this.ua.indexOf('mozilla')!=-1) && ((this.ua.indexOf('spoofer')==-1) && (this.ua.indexOf('trident')==-1) && (this.ua.indexOf('compatible')==-1)) ); this.isWebtv = (this.ua.indexOf("webtv")!=-1); } this.isIE = !this.isWebtv && !this.isOpera && !this.isVivaldi && ( this.isEdge || this.isEdgeChrome || (this.ua.indexOf('msie') != -1) || (this.ua.indexOf('trident') != -1) ); this.version = parseFloat(ver); if ( this.isNetscape && (this.version>=5) ) this.isGecko = true; } if ( this.isIE ) { if ( this.isEdge ) this.version = new cVersion( parseFloat(this.ua.substring(6+this.ua.indexOf(" edge/"))) ); else if ( this.isEdgeChrome ) this.version = new cVersion( parseFloat(this.ua.substring(5+this.ua.indexOf(" edg/"))) ); else if ( this.ua.indexOf('rv:') != -1 ) this.version = new cVersion( this.ua.substring(3+this.ua.indexOf("rv:")) ); else this.version = new cVersion( parseFloat(this.ua.substring(4+this.ua.indexOf("msie"))) ); } else if ( this.isOpera ) { if ( this.ua.indexOf('version/') != -1 ) this.version = new cVersion( parseFloat(this.ua.substring(8+this.ua.indexOf("version/"))) ); else if ( this.ua.indexOf('opera/') == 0 ) this.version = new cVersion( parseFloat(this.ua.substring(1+this.ua.indexOf("/"))) ); else if ( this.ua.indexOf("opera/") != -1 ) this.version = new cVersion( parseFloat(this.ua.substring(2+this.ua.indexOf(")"))) ); else if ( this.ua.indexOf("opr/") != -1 ) this.version = new cVersion( this.ua.substring(4+this.ua.indexOf("opr/")) ); else this.version = new cVersion( parseFloat(this.ua.substring(6+this.ua.indexOf("opera/"))) ); } else if ( this.isAvast ) { this.version = new cVersion( this.ua.substring(6+this.ua.indexOf("avast/")) ); } else if ( this.isMidori ) { this.version = new cVersion( this.ua.substring(7+this.ua.indexOf("midori/")) ); } else if ( this.isNorton ) { this.version = new cVersion( this.ua.substring(7+this.ua.indexOf("norton/")) ); } else if ( this.isVivaldi ) { this.version = new cVersion( this.ua.substring(8+this.ua.indexOf("vivaldi/")) ); } else if ( this.isSafari && (this.ua.indexOf("version/") != -1) ) { this.version = new cVersion( this.ua.substring(8+this.ua.indexOf("version/")) ); } else { this.version = new cVersion( this.version ); } this.engine = 'Undetermined'; this.engineVersion = new cVersion( '0' ); if ( this.isIE && (this.ua.indexOf("trident/") != -1) ) { this.engine = 'Trident'; this.engineVersion = new cVersion( this.ua.substring(8+this.ua.indexOf('trident/')) ); } else if ( this.isEdge ) { this.engine = 'Edge::HTML'; this.engineVersion = new cVersion( this.ua.substring(5+this.ua.indexOf('edge/')) ); } else if ( this.isGoanna ) { this.engine = 'Gecko Goanna'; this.engineVersion = new cVersion( this.ua.substring(8+this.ua.indexOf(' goanna/')) ); } else if ( this.isGecko ) { this.engine = 'Gecko'; this.engineVersion = new cVersion( this.ua.substring(4+this.ua.indexOf(' rv:')) ); } else if ( this.ua.indexOf("chrome/") != -1 ) { this.engine = 'Blink'; this.engineVersion = new cVersion( this.ua.substring(7+this.ua.indexOf('chrome/')) ); } else if ( this.isSafari && (this.ua.indexOf("version/") != -1) ) { this.engine = 'Safari'; this.engineVersion = new cVersion( this.ua.substring(8+this.ua.indexOf("version/")) ); } else if ( this.isKhtml ) { this.engine = 'KHTML'; this.engineVersion = new cVersion( this.ua.substring(6+this.ua.indexOf('khtml/')) ); } else if ( this.isOperaPresto ) { this.engine = 'Opera Presto'; this.engineVersion = new cVersion( this.ua.substring(7+this.ua.indexOf('presto/')) ); } }

The above creates an object which has the following properties:

Browser Sniffer Part 3 : cVersion() Class 

This JavaScript constructs an object holding a version vector, with methods which act on the vector. The constructor is cVersion(). The methods are 1 cVersion.​toMajorString(), which returns a string corresponding to the major version number in the cVersion object, 2 cVersion.​toString(), which returns a string corresponding to the version vector in a cVersion object, and 3 cVersion.​comp() which compares two version vec­tors and returns a value indicating whether the first is less than, equal to, or greater than the second version vector. (You can find this JavaScript in the text file at ../js/cVersion.js — with tabs assumed set at 4-space intervals.)

JavaScript // Updated Feb 17, 2022 function cVersion ( version, separator, bSkipSpace ) { if ( arguments.length < 1 ) version = '0'; if ( arguments.length < 2 ) separator = '.'; if ( arguments.length < 3 ) bSkipSpace = false; if ( version instanceof cVersion ) { this.separator = (arguments.length < 2) ? version.separator : separator; this.v = new Array(); this.v = version.v; } else { var s = ( typeof(version) == 'number' ) ? version.toString() : version; this.separator = separator; this.v = new Array(); var vindex = 0; var sindex = 0; var c; this.v[vindex] = ''; if ( bSkipSpace) for ( ; sindex < s.length; ++sindex ) { c = s.charAt(sindex); if ( c != ' ' ) break; } for ( ; sindex < s.length; ++sindex ) { c = s.charAt(sindex); if ( c == separator ) this.v[++vindex] = ''; else if ( (c >= '0') && (c <= '9') ) this.v[vindex] += c.toString(); else break; } } return; } cVersion.prototype.toMajorString = function ( ) { return ( this.v[0] ); } cVersion.prototype.toString = function ( separator ) { if ( arguments.length < 1 ) separator = this.separator; var rv = ''; for ( var i = 0; i < this.v.length; ++i ) { if ( i == 0 ) rv += this.v[0]; else rv += separator + this.v[i]; } return rv; } cVersion.prototype.comp = function ( version2 ) { var operand; if ( arguments.length < 1 ) version2 = '0'; if ( version2 instanceof cVersion ) operand = version2; else operand = new cVersion( version2 ); var nLoops = Math.max( this.v.length, operand.v.length ); var rv = 0; for ( var i = 0; i < nLoops; ++i ) { var n1 = Number( (i < this.v.length) ? this.v[i] : 0 ); var n2 = Number( (i < operand.v.length) ? operand.v[i] : 0 ); if ( n1 == n2 ) continue; else if ( n1 < n2 ) { rv = -1; break; } else { rv = 1; break; } } return rv; }
Browser Sniffer Part 4 : cBrowser() Instantiation

An object may now be created to hold the browser information, e.g.:

JavaScript var browser = new cBrowser();

A Modest Browser Engine Sniffer

This describes a brow­ser engine snif­fer I designed which I think is very good. It has four parts:

See also: Browser Sniffer.

If JavaScript is enabled the code below reliably detects Trident for IE 5–9 for Windows with 100% certainty due to the depend­ence on conditional comments, even if the user agent has been faked.

Identification of other browser engines will be extremely reliable: not 100%, but close. This is partly because the certain detection of IE 5–9 eli­mi­nates a lot of possible errors, but also because the code is very careful in analyzing the user agent: for exam­ple, before checking to see if the user agent contains 'gecko/', it excludes browsers such as Opera whose user agents sometimes contain 'gecko/' in order to mimic Gecko user agents. The code is likely to misidentify the browser engine only if the browser isn’t IE 5–9 and if the entire user agent string has been faked: and, as pointed out above, the user would have no need to fake the user agent string for a site with a good brow­ser snif­fer; in practical terms, therefore, this code is exceed­ing­ly reliable.

This brow­ser engine snif­fer does not reliably detect engines for legacy, extinct browsers.

Browser Engine Sniffer Part 1 : HTML

The HTML goes in the <head> section, and creates a variable isTrulyIE only if the browser is IE 5–9 for Windows:

HTML (IE) <!--[if gte IE 5]> <script type="text/javascript"> var isTrulyIE = true; </script> <![endif]-->
Browser Engine Sniffer Part 2 : cBrowserEngine() Constructor 

This JavaScript constructs an object with browser engine information, taking one optional argument: a string to be used as the user​Agent string, with the real userAgent as the default. (You can find this code in the text file at ../js/cBrowserEngine.js — with tabs assumed set at 4-space intervals.)

JavaScript // March 5, 2022 'use strict'; function cBrowserEngine ( ua ) { if ( arguments.length == 0 ) ua = navigator.userAgent; this.UA = ua; this.ua = ua.toLowerCase(); this.isB = this.isE = this.isG = this.isGoanna = this.isK = this.isS = this.isT = false; this.engine = this.engine2 = 'Unknown'; this.version = new cVersion( '0' ); this.version2 = new cVersion( '0' ); var n = -1; if ( typeof(isTrulyIE) != 'undefined' ) { this.isT = true; n = this.ua.indexOf(" trident/"); this.version = new cVersion( this.ua.substring(9+n) ); } else { if ( (n = this.ua.indexOf(" edge/") ) != -1 ) { this.isE = true; this.engine = 'Edge::HTML'; this.version = new cVersion( this.ua.substring(6+n) ); } else if ( (n = this.ua.indexOf(" chrome/") ) != -1 ) { this.isB = true; this.engine = 'Blink'; this.version = new cVersion( this.ua.substring(8+n) ); } else if ( (n = this.ua.indexOf(" gecko/") ) != -1 ) { n = this.ua.indexOf(" rv:"); this.isG = true; this.engine = 'Gecko'; this.version = new cVersion( this.ua.substring(4+n) ); } else if ( (n = this.ua.indexOf(" khtml/") ) != -1 ) { this.isK = true; this.engine = 'KHTML'; this.version = new cVersion( this.ua.substring(7+n) ); } else if ( (n = this.ua.indexOf(" trident/") ) != -1 ) { this.isT = true; this.engine = 'Trident'; this.version = new cVersion( this.ua.substring(9+n) ); } else if ( (n = this.ua.indexOf(" safari/") ) != -1 ) { n = this.ua.indexOf(" version/"); this.isS = true; this.engine = 'Safari'; this.version = new cVersion( this.ua.substring(9+n) ); } } if ( this.isG ) { if ( (n = this.ua.indexOf(" goanna/") ) != -1 ) { this.isGoanna = true; this.engine2 = 'Goanna'; this.version2 = new cVersion( this.ua.substring(8+n) ); } } } cBrowserEngine.prototype.toString = function ( ) { var rv = this.engine + ' ' + this.version.toString(); if ( this.engine2 != 'Unknown' ) rv += ', ' + this.engine2 + ' ' + this.version2.toString();; return( rv ); }

The above creates an object which has the following properties:

The above also creates a method cBrowserEngine.toString() which returns a string with the engine name and version, followed (if appropriate) by any sub-engine name and version, e.g. "Gecko 52.9, Goanna 3.5".

Browser Engine Sniffer Part 3 : cVersion() Class

This JavaScript constructs an object holding a version vector, with methods which act on the vector. The constructor is cVersion(). The methods are 1 cVersion.​toMajorString(), which returns a string corresponding to the major version number in the cVersion object, 2 cVersion.​toString(), which returns a string corresponding to the version vector in a cVersion object, and 3 cVersion.​comp() which compares two version vec­tors and returns a value indicating whether the first is less than, equal to, or greater than the second version vector. (You can find this JavaScript in the text file at ../js/cVersion.js — with tabs assumed set at 4-space intervals.)

This is the same as used above for the browser sniffer.

Browser Engine Sniffer Part 4 : cBrowserEngine() Instantiation

An object may now be created to hold the browser engine information, e.g.:

JavaScript var engine = new cBrowserEngine();

Examples

Here are some examples of achieving different code for different browsers:

Linking to IE Stylesheets

One way to specify different CSS rules for IE, or for different versions of IE, is to link to one master stylesheet file which defines the standard rules for all browsers, and then to use conditional comments to link to other, much smaller stylesheet files, which set a few rules or override a few rules, to deal with the vagaries of various versions of IE. For example:

HTML (IE) <link rel="stylesheet" type="text/css" href="style.css" /> <!--[if lt IE 6]> <link rel="stylesheet" type="text/css" href="style_ie5x.css"> <![endif]--> <!--[if gte IE 5]> <link rel="stylesheet" type="text/css" href="style_ie.css"> <![endif]-->

This links to the standard stylesheet, style.css. If the browser is IE 5.x for Windows, this then links to style_ie5x.css. If the browser is any version of IE for Windows from 5.0 up, this then links to style_ie.css.

This is completely reliable for IE for Windows 5–9, and IE 10+ support the standards well enough not to need special CSS.

Linking to a Mobile Stylesheet

One way to cope with mobile devices is to link to a standard stylesheet used for all pages, and then to conditionally link to a stylesheet which copes with differences in mobile devices. For example:

HTML <link type="text/css" href="mobile.css" rel="stylesheet" media="only screen and (max-width:480px)">

This links to mobile.css if the browser supports CSS 3 media queries and the device has a screen width of 480px or less.

This is reliable for modern mobile browsers.

JavaScript for a Particular Version of IE

Sometimes JavaScript is needed which is different for a specific version of IE. For example, I have several sites with JavaScript that generates HTML to produce CSS-styled bar charts, but it doesn’t work well for IE 5.0x because its poor CSS support produces ugly results. What I did for these sites is use conditional comments to set a JavaScript variable calBarCharts, i.e:

HTML (IE) <!--[if lt IE 5.5000]> <script type="text/javascript"> var calBarCharts = false; </script> <![endif]-->

In some cases the bar charts were not critical and could be omitted, so the JavaScript generating the HTML would generate nothing if the variable existed and was false.

In some cases the bar charts were more important, so the JavaScript generating the HTML would generate different HTML, producing less attractive but adequate bar charts, if the variable existed and was false.

In either case, using conditional comments for this purpose eliminates the need for a complex brow­ser snif­fer. This is completely reliable for IE for Windows 5+.

Scrolling to a Bookmark

Sometimes JavaScript is needed which can scroll to a bookmark on the current page. Some browsers, however, don’t support the preferred method, so an alternate method is used for these browsers. In the following function, object detection is used to decide which method to use:

JavaScript function myScrollToAnchor ( id ) { if ( arguments.length < 1 ) id = window.location.hash; if ( id.indexOf('#') == 0 ) id = id.substring(1); var o = getElement( id ); if ( o ) { if ( (typeof(o.offsetTop) != 'undefined') && (typeof(window.scrollTo) != 'undefined') ) { var x = 0; var y = 0; while ( o ) { x += o.offsetLeft; y += o.offsetTop; o = o.offsetParent; } window.scrollTo( x, y ); } else window.location.replace( '#' + id ); } }

The function takes an optional argument, the ID for the bookmark. If the argument is omitted, the bookmark is extracted from the URL for the current page.

Object detection is done in two locations, marked above by gold underlines:

The above should be reliable unless the browser supports neither method: I am not aware of any which don’t.

Caution: the above function has been observed to fail with certain old browsers if the function is called just after the HTML containing the anchor has been dynamically changed, e.g. using innerHTML. It appears that the browser tries to scroll to the bookmark before the dynamic changes have completed.

Extracting the Filename from a Pathname

Consider the problem of extracting a filename from a pathname. The following function was created to do this, which uses a method akin to object detection to do it:

JavaScript function myGetFilename ( pathname ) { var sFilename; if ( (arguments.length == 0) || (pathname == null) ) pathname = document.location.pathname; var nSlash = pathname.lastIndexOf( '/' ); if ( nSlash == -1 ) sFilename = pathname; else sFilename = pathname.substring(nSlash+1); nSlash = pathname.lastIndexOf( '\\' ); if ( nSlash != -1 ) sFilename = sFilename.substring(nSlash+1); return( sFilename ); }

This function takes a pathname as an optional argument: if the argument is omitted or null, the pathname to the current document is used.

The basic algorithm looks for the last '/' in the pathname: if not found, the filename is the pathname; if found, the filename is the string to the right of the '/'. However, some older versions of IE use a '\' instead of a '/' to separate directory names and the filename in a pathname: so the function then looks for the last '\' in the pathname: if not found, the filename is the pathname; if found, the filename is the string to the right of the '\'. This enables the function to work no matter which browser is used: not exactly by object detection, but, like object detection, the identity of the browser need not be determined, as it suffices simply to recognize the different behaviours of different browsers.

Setting Basic Font Sizes

When using CSS to style text, one common thing to do is set the basic font size. I often prefer to do so using a method which honours the user’s preferred font size. With CSS, one should be able to do this using:

HTML html { font-size:medium; }

Unfortunately, this assumes that the default size is medium, which is what it should be, but IE5 wrongly uses small. This means that, to get consistent results, setting the size for IE5 requires:

HTML html { font-size:small; }

One method to this with one rule uses a CSS trick which depends on how IE5 handles invalid CSS:

HTML html { font-size:small; voice-family: "\"}\""; voice-family:inherit; font-size:medium; }

This works pretty well, but a better method is to put this in the standard stylesheet …

HTML html { font-size:medium; }

… and then to put the CSS for IE5 in an IE5-specific stylesheet, using conditional comments, for example …

HTML (IE) <!--[if lt IE 6]> <link rel="stylesheet" type="text/css" href="style_ie5x.css"> <![endif]-->

… which has the merit of using an IE5-specific file into which other CSS can also be put to get around IE5’s many other defects.

Checking for HTML 5 Support

Object detection can be used to see whether the browser supports various HTML 5 features. Here are JavaScript code fragments for detecting support of the <audio>, <video>, and <canvas> tags:

Check for <audio> Support
JavaScript if ( window.HTMLAudioElement ) { // <audio> is supported }
Check for <video> Support
JavaScript if ( window.HTMLVideoElement ) { // <video> is supported }
Check for <canvas> Support
JavaScript if ( window.HTMLCanvasElement ) { // <canvas> is supported }

 

Checking for Dark Mode

Function myIsDarkMode does object detection to see whether the browser supports window.matchMedia:

JavaScript // Return true if O/S is configured for dark mode function myIsDarkMode () { var isDarkMode = false; if ( window.matchMedia ) { var o = window.matchMedia( '(prefers-color-scheme: dark)' ); if ( o ) isDarkMode = o.matches; } return( isDarkMode ); }

Function mySetStylesheetForLightOrDarkMode uses the above function to select either a light stylesheet or a dark stylesheet:

JavaScript // Set stylesheet for dark or light mode function mySetStylesheetForLightOrDarkMode () { var aStyleSheet = [ 'style_light_mode.css', 'style_dark_mode.css' ]; var i = myIsDarkMode() ? 1 : 0; var head = document.getElementsByTagName('head')[0]; var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = aStyleSheet[i]; link.media = 'all'; head.appendChild(link); }

 Top of Page         Legal Notices 

Logo