Object Sniffing New Browsers, Part 2: Other Browsers | 3
Object Sniffing New Browsers, Part 3: Opera
An Operatic Family
Sometimes known as “the third browser”, Opera (with its relatively small user base compared to Microsoft’s Internet Explorer), has garnered a strong and loyal following of users who like its many features – such as tabbed browsing, using mouse gestures to navigate pages, its “zoom” feature and wide operating system support, as well as its relatively small size and speed.
The first version of Opera was version 2.1 (not version 1.0), launched in 1996 during the peak of the then-raging browser wars. With the arrival of version 3.5, the Opera browsers have boasted far better support for CSS properties, XML and DOM, than their rivals in the browser marketplace. For a long time, if you wanted to see how a particular standards-compliant feature was supposed to work, you checked its behavior in Opera. This support has been incremental in nature, with significant improvements appearing in each version up to and including the latest 7.1 release. Its strong support for W3C standards and the features they support add up to good reasons to go “sniffing” for it when it hits your Web server.
Opera is also the most problematic browser to sniff for using the traditional navigator.userAgent string approach, since it’s easy for users to spoof Opera to look like other browsers. Since at least version 4.0, changing the string navigator.userAgent displays to a Web server is a simple preference that users can pick from a menu. So while you can’t take the navigator.userAgent string Opera delivers at face value, the browser spoofs this value in such a way that you can still effectively distinguish it.
Traditional, Non-Object Based Sniffing and Opera
It’s worthwhile investigating a non-object oriented approach to identifying browsers, if only to see its strengths and weaknesses in this case.
The indexOf method is handy when looking for a particular sequence of characters from a given string. Not only will it tell you whether or not a particular character or character sequence appears within a string, it will also return a value of “-1” when it does not find the character or characters you are looking for. So all you have to do is a positive search for any Opera browser to assess whether the following variable is true or not:
var is_opera = (agt.indexOf("opera"))?true:false;
What we’re doing is looking for any instance in which the word “opera” appears when a browser visits a Web site. You could use this same code substituting “opera” with “mozilla”, “; nav” or “msie” to determine whether or not a browser is Mozilla, Netscape or Internet Explorer.
You can also put this same code another way, like this:
var is_opera = (agt.indexOf("opera")) != -1;
This yields the same results, but it offers an easier way of assessing more than one set of character strings within a larger string than the traditional ternary arrangement we’ve been using up until now. In order to differentiate between one version of Opera and another, you have to look for an additional number associated with this keyword. Next, you can put them in sequence and assess their validity:
var is_opera2 = (agt.indexOf("opera 2") != -1 || agt.indexOf("opera/2")
var is_opera3 = (agt.indexOf("opera 3") != -1 || agt.indexOf("opera/3") != -1);
var is_opera4 = (agt.indexOf("opera 4") != -1 || agt.indexOf("opera/4") != -1);
var is_opera5 = (agt.indexOf("opera 5") != -1 || agt.indexOf("opera/5") != -1);
var is_opera6 = (agt.indexOf("opera 6") != -1 || agt.indexOf("opera/6") != -1);
var is_opera7 = (agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1);
Using this method, it’s relatively simple to distinguish between the various versions of Opera, and this sequence by itself will even catch Opera when it’s trying to spoof itself as being another browser. But what do you do if you’re also sniffing for Internet Explorer using this method, by inserting “msie” instead of “opera”? If this happens, a browser will evaluate both “true” when evaluating for both browsers. The following variable will catch whether or not Opera is trying to spoof Internet Explorer if it finds the words “Opera” and “Internet Explorer”.
var opera_spoofing_ie = ((agt.indexOf("msie") != -1) || (agt.indexOf("opera") == -1));
At this point you have to begin checking for all of the ways in which Opera can spoof itself, and while this can certainly be done, things start getting a bit messy code-wise. This is where taking an object-oriented approach to this process is an advantage, as it bypasses the spoofing mechanism Opera uses and zeroes in on Opera, no matter the circumstance.
Object Sniffing for Opera
To be able to distinguish Opera from other browsers that may contain the same objects, the simple non-object approach that provides a true or false value to the is_opera variable is sufficient. Once that’s been determined, you can run the following tests in order to determine which version of Opera being used.
Opera version 5 incorporated the windows.opera object, but didn’t incorporate the window.print object which came with version 6, so to distinguish between the two versions you can use the following lines of code:
var is_op5 = ((window.print&&typeof(window.print)!=-1&&typeof(window.print)==-1)?typeof(window.print):window.print)?false:true;
var is_op6 = ((window.print&&typeof(window.print)!=-1&&typeof(window.print)!=-1)?typeof(window.print):window.print)?true:false;
Note the bit of somewhat unorthodox jiggery-pokery going on here: in the is_op5 variable we are looking for a positive case where window.print does not exist, so its value should equal “-1”; then if this statement is true, we want “false” to be reported, since we are looking at a negative condition, hence the reversal of the usual true:false statement at the end. This also makes things easier for our processing script to deal with, as all we want to deal with are positive, “true” values. The is_op6 variable is more straightforward, and these two statements are sufficient to distinguish between these two versions of the Opera browser.
You can detect Opera 7 just by looking for a positive case where the window.opera and document.childNodes objects both exist, as in the following statement:
var is_childNodes =
((document.childNodes&&typeof(document.childNodes)!= -1 &&typeof(document.childNodes)!= -1)?typeof(document.childNodes):document.childNodes)?true:false;
Since this state also exists in Opera 6, all you have to do is see whether the values for the “is_op6” variable and is_childNodes variables are equal. If they are, you’re looking at Opera 7, and you can tell the browser to render the is_op6 variable as “false”. This can be done using the following two lines of code:
if (is_op6 == is_childNodes) is_op7 = 'true';
if (is_op6 == is_childNodes) is_op6 = 'false';
Using this code, you get a positive match if the browser is Opera 7, and if
you’re using Opera 6 the value for is_op7 has no match, and is never displayed.
Created: March 27, 2003
Revised: August 12, 2003