你知道世界上有多少种浏览器吗?除了我们熟知的ie, firefox, opera, safari四大浏览器之外,世界上还有近百种浏览器。
浏览器兼容性是前端开发框架要解决的第一个问题,要解决兼容性问题就得首先准确判断出浏览器的类型及其版本。
javascript是前端开发的主要语言,我们可以通过编写javascript程序来判断浏览器的类型及版本。javascript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的useragent属性来判断的。在许多情况下,值判断出浏览器类型之后,还需判断浏览器版本才能处理兼容性问题,而判断浏览器的版本一般只能通过分析浏览器的useragent才能知道。
我们先来分析一下各种浏览器的特征及其useragent。
ie
只有ie支持创建activex控件,因此她有一个其他浏览器没有的东西,就是activexobject函数。只要判断window对象存在activexobject函数,就可以明确判断出当前浏览器是ie。而ie各个版本典型的useragent如下:
mozilla/4.0 (compatible; msie 8.0; windows nt 6.0)
mozilla/4.0 (compatible; msie 7.0; windows nt 5.2)
mozilla/4.0 (compatible; msie 6.0; windows nt 5.1)
mozilla/4.0 (compatible; msie 5.0; windows nt)
其中,版本号是msie之后的数字。
firefox
firefox中的dom元素都有一个getboxobjectfor函数,用来获取该dom元素的位置和大小(ie对应的中是getboundingclientrect函数)。这是firefox独有的,判断它即可知道是当前浏览器是firefox。firefox几个版本的useragent大致如下:
mozilla/5.0 (windows; u; windows nt 5.2) gecko/2008070208 firefox/3.0.1
mozilla/5.0 (windows; u; windows nt 5.1) gecko/20070309 firefox/2.0.0.3
mozilla/5.0 (windows; u; windows nt 5.1) gecko/20070803 firefox/1.5.0.12
其中,版本号是firefox之后的数字。
opera
opera提供了专门的浏览器标志,就是window.opera属性。opera典型的useragent如下:
opera/9.27 (windows nt 5.2; u; zh-cn)
opera/8.0 (macintosh; ppc mac os x; u; en)
mozilla/5.0 (macintosh; ppc mac os x; u; en) opera 8.0
其中,版本号是靠近opera的数字。
safari
safari浏览器中有一个其他浏览器没有的opendatabase函数,可做为判断safari的标志。safari典型的useragent如下:
mozilla/5.0 (windows; u; windows nt 5.2) applewebkit/525.13 (khtml, like gecko) version/3.1 safari/525.13
mozilla/5.0 (iphone; u; cpu like mac os x) applewebkit/420.1 (khtml, like gecko) version/3.0 mobile/4a93 safari/419.3
其版本号是version之后的数字。
chrome
chrome有一个messageevent函数,但firefox也有。不过,好在chrome并没有firefox的getboxobjectfor函数,根据这个条件还是可以准确判断出chrome浏览器的。目前,chrome的useragent是:
mozilla/5.0 (windows; u; windows nt 5.2) applewebkit/525.13 (khtml, like gecko) chrome/0.2.149.27 safari/525.13
其中,版本号在chrome只后的数字。
有趣的是,chrome的useragent还包含了safari的特征,也许这就是chrome可以运行所有apple浏览器应用的基础吧。
只要了解了以上信息,我们就可以根基这些特征来判断浏览器类型及其版本了。我们会将判断的结果保存在sys名字空间中,成为前端框架的基本标志信息,供今后的程序来读取。如果判断出谋种浏览器,sys名字空间将有一个该浏览器名称的属性,其值为该浏览器的版本号。例如,如果判断出ie 7.0,则sys.ie的值为7.0;如果判断出firefox 3.0,则sys.firefox的值为3.0。下面是判断浏览器的代码:
var sys = {};
var ua = navigator.useragent.tolowercase();
if (window.activexobject)
sys.ie = ua.match(/msie ([\d.]+)/)[1]
else if (document.getboxobjectfor)
sys.firefox = ua.match(/firefox\/([\d.]+)/)[1]
else if (window.messageevent && !document.getboxobjectfor)
sys.chrome = ua.match(/chrome\/([\d.]+)/)[1]
else if (window.opera)
sys.opera = ua.match(/opera.([\d.]+)/)[1]
else if (window.opendatabase)
sys.safari = ua.match(/version\/([\d.]+)/)[1];
//以下进行测试
if(sys.ie) document.write('ie: '+sys.ie);
if(sys.firefox) document.write('firefox: '+sys.firefox);
if(sys.chrome) document.write('chrome: '+sys.chrome);
if(sys.opera) document.write('opera: '+sys.opera);
if(sys.safari) document.write('safari: '+sys.safari);
我们把对ie的判断放在第一,因为ie的用户最多,其次是判断firefox。按使用者多少的顺序来判断浏览器类型,可以提高判断效率,少做无用功。之所以将chrome放在第三判断,是因为我们预测chrome很快会成为市场占有率第三的浏览器。其中,在分析浏览器版本时,用到了正则表达式来析取其中的版本信息。
如果你的javascript玩得很高,你还可以将前面的判断代码写成这样:
var sys = {};
var ua = navigator.useragent.tolowercase();
window.activexobject ? sys.ie = ua.match(/msie ([\d.]+)/)[1] :
document.getboxobjectfor ? sys.firefox = ua.match(/firefox\/([\d.]+)/)[1] :
window.messageevent && !document.getboxobjectfor ? sys.chrome = ua.match(/chrome\/([\d.]+)/)[1] :
window.opera ? sys.opera = ua.match(/opera.([\d.]+)/)[1] :
window.opendatabase ? sys.safari = ua.match(/version\/([\d.]+)/)[1] : 0;
//以下进行测试
if(sys.ie) document.write('ie: '+sys.ie);
if(sys.firefox) document.write('firefox: '+sys.firefox);
if(sys.chrome) document.write('chrome: '+sys.chrome);
if(sys.opera) document.write('opera: '+sys.opera);
if(sys.safari) document.write('safari: '+sys.safari);
这样可以使javascript代码更精简些。当然,可读性稍差一些,就看你是重视效率还是重视可维护性了。
使用不同特征来判断浏览器的方法,虽然在速度上比用正则表达式分析useragent要来的快,不过这些特征可能会随浏览器版本而变化。比如,一种浏览器本来独有的特性取得了市场上的成功,其他浏览器也就可能跟着加入该特性,从而使该浏览器的独有特征消失,导致我们的判断失败。因此,相对比较保险的做法是通过解析useragent中的特征来判断浏览器类型。何况,反正判断版本信息也需要解析浏览器的useragent的。
通过分析各类浏览器的useragent信息,不难得出分辨各类浏览器及其版本的正则表达式。而且,对浏览器类型的判断和版本的判断完全可以合为一体地进行。于是,我们可以写出下面的代码:
var sys = {};
var ua = navigator.useragent.tolowercase();
var s;
(s = ua.match(/msie ([\d.]+)/)) ? sys.ie = s[1] :
(s = ua.match(/firefox\/([\d.]+)/)) ? sys.firefox = s[1] :
(s = ua.match(/chrome\/([\d.]+)/)) ? sys.chrome = s[1] :
(s = ua.match(/opera.([\d.]+)/)) ? sys.opera = s[1] :
(s = ua.match(/version\/([\d.]+).*safari/)) ? sys.safari = s[1] : 0;
//以下进行测试
if (sys.ie) document.write('ie: ' + sys.ie);
if (sys.firefox) document.write('firefox: ' + sys.firefox);
if (sys.chrome) document.write('chrome: ' + sys.chrome);
if (sys.opera) document.write('opera: ' + sys.opera);
if (sys.safari) document.write('safari: ' + sys.safari);
其中,采用了“… ? … : …”这样的判断表达式来精简代码。判断条件是一条赋值语句,既完成正则表达式的匹配及结果复制,又直接作为条件判断。而随后的版本信息只需从前面的匹配结果中提取即可,这是非常高效的代码。
以上的代码都是为了打造前端框架所做的预研,并在五大浏览器上测试通过。今后,判断某种浏览器只需用if(sys.ie)或if(sys.firefox)等形式,而判断浏览器版本只需用if(sys.ie == ’8.0′)或if(sys.firefox == ’3.0′)等形式,表达起来还是非常优雅的。