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 browser snifing 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.
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:
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.
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:
Unlike all other browsers, IE doesn’t use navigator.plugins[] to keep information about plugins: IE keeps such information
elsewhere; IE does offer the array, but it’s always empty. Checking to see whether the array exists, therefore, means nothing: the array always exists.
Checking the
contents of the array is somewhat more useful: if it isn’t empty, the browser isn’t IE, and the array may be used; however, if it’s empty,
this could mean either that the browser is IE, or that there are no plugins, or that the browser is hiding the plugins.
The document.all[] array is used by old versions of IE to get tag elements by ID, instead of the standard
method document.getElementById(), but it’s important to do object detection in the right order: some browsers other than IE
support the standard method, but also have a document.all[] array devoid of tag elements; JavaScript should therefore
check for the method first, and only if it doesn’t exist, check for the array. 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.
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.
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:
For details see the pertinent CSS 3 specification.
This technique involves JavaScript code — commonly called a browser sniffer — which identifies a browser by
examining certain objects, chiefly the navigator.userAgent string.
This presents two browser sniffers:
This is the least reliable approach to dealing with browser differences, but it’s more reliable than many give it credit for, and it often
works well when other techniques can’t be applied. Browser sniffers have a sordid reputation, largely because of the widespread use of sniffers
which are far too simplistic. The earliest sniffers dealt only with versions of IE and Netscape which today are extinct, e.g. IE 3–4 and
Netscape 3–4. The early sniffers could not handle the range of browsers which appeared later, and in many cases the sniffers were never updated, thus many sites
broke with newer browsers. This encouraged 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 sniffers necessary for correct
identification. Also, many sniffers 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 Explorer, overlooking the fact that userAgent strings
of other browsers, such as Opera, may contain 'MSIE'. But truly good sniffers are possible, e.g. the ones below.
The big disadvantage to browser snifing, 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 disadvantage is not, however, as serious as one might think, since one would use such a plugin only for a
site which has a sniffer 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
sniffer.
One element of browser detection is extracting and dealing with version vectors.
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 version vectors consist only of digits and periods.
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.
This describes a browser sniffer I designed which I think is very good. It has four parts:
isTrulyIE only if the browser is IE.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 dependence 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 eliminates a lot of possible errors, but also because the code is very careful in analyzing the user agent: for example, 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 browser sniffer; in practical terms, therefore, this code
is exceedingly 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 possible: I have code to do it, but isn’t listed here because it rarely matters, since few people use
very old versions of browsers other than IE. NB: for IE 8–11 the version vector may be wrongly identified because
IE 8–11 may emulate 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 userAgent 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 vectors.
This browser sniffer detects a wider range of browsers than many sites will need to recognize, e.g. MSN-TV (WebTV). Moreover, this sniffer 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 equivalent. The overhead of detecting things unneeded for some sites is a small price for having a single reliable, reüsable module.
Caution: this browser sniffer 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 previous version of the browser sniffer failed to reliably detect IE 11. If, therefore, you use this browser sniffer, you will have to be careful to check here regularly for any updates.
The HTML goes in the <head> section, and creates a variable isTrulyIE only if the browser is IE 5–9 for Windows:
This JavaScript constructs an object with browser information, taking two optional arguments:
a string to be used as the userAgent 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.
The above creates an object which has the following properties:
navigator.userAgent.navigator.userAgent.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 vectors 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.)
An object may now be created to hold the browser information, e.g.:
This describes a browser engine sniffer I designed which I think is very good. It has four parts:
isTrulyIE only if the browser is IE.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 dependence 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 eliminates a lot of possible errors, but also because the code is very careful in analyzing the user agent: for example, 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 browser sniffer; in practical terms, therefore, this code
is exceedingly reliable.
This browser engine sniffer does not reliably detect engines for legacy, extinct browsers.
The HTML goes in the <head> section, and creates a variable isTrulyIE only if the browser is IE 5–9 for Windows:
This JavaScript constructs an object with browser engine information, taking one optional argument:
a string to be used as the userAgent 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.)
The above creates an object which has the following properties:
navigator.userAgent.navigator.userAgent.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".
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 vectors 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.
An object may now be created to hold the browser engine information, e.g.:
Here are some examples of achieving different code for different browsers:
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:
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.
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:
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.
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:
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 browser sniffer. This is completely reliable for IE for Windows 5+.
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:
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:
getElement() function listed earlier is used to get the element associated with
the ID, and, as pointed out earlier, that function uses object detection to decide how to get the element.offsetTop property and the window has a scrollTo() method:
iff both exist — and not all browsers support both — the preferred method of scrolling, using window.scrollTo() is used; if either one doesn’t exist, another
method, using window.location.replace() is used instead.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.
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:
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.
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:
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:
One method to this with one rule uses a CSS trick which depends on how IE5 handles invalid CSS:
This works pretty well, but a better method is to put this in the standard stylesheet …
… and then to put the CSS for IE5 in an IE5-specific stylesheet, using conditional comments, for example …
… 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.
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:
Function myIsDarkMode does object detection to see whether the browser supports window.matchMedia:
Function mySetStylesheetForLightOrDarkMode uses the above function to select either a light stylesheet or a dark stylesheet: