09月07, 2013

IE的浏览器模式和文本模式(二)

一年半之前我写了一篇《关于浏览器模式和文本模式的困惑》,介绍了IE8+特有的浏览器模式(Browser Mode)和文本模式(Document Mode),以及我的测试和微软文档有出入的部分。其中有一些没有提到的内容,本文继续讨论。

判断真正的IE版本

很多JS框架都通过UA判断IE的版本。对于IE6,这种做法没问题(IE6没有浏览器模式的概念,也没有其它IE可以把浏览器模式改为IE6;IE7虽然也没有浏览器模式,但IE8+可以把浏览器模式设置为IE7模式)。但是从IE8开始引入的浏览器模式会产生不同的UA。例如,IE9有这些:

浏览器模式 navitor.userAgent 默认文本模式
IE7 MSIE 7.0 IE7标准
IE8 MSIE 8.0 && Trident/4.0 IE8标准
IE9 MSIE 9.0 && Trident/5.0 IE9标准
IE9兼容性 MSIE 7.0 && Trident/5.0 IE7标准

如果仅通过UA中的“MSIE X.0”来判断,会得到IE7~9三种不同结果。

实际上,对于IE8+,根据UA字符串只能确定当前是否是兼容性视图。因为兼容性视图的UA中,IE版本和Trident版本不匹配。例如UA里同时有“MSIE 7.0”和“Trident/6.0”,说明浏览器模式肯定是IE10兼容性。这是因为IE8才开始给UA加上Trident信息,而Trident/6.0是IE10所特有。

除此之外,上面IE7IE8这两种浏览器模式,UA和跟真正的IE7或IE8没有任何区别,根据UA完全没办法区分。甚至连IE9模式,我们也无法确认这是IE9浏览器的默认模式,还是IE10浏览器的IE9模式。

下面来看看文本模式,依然用IE9测试。选择不同的文本模式,documentMode的值也不一样。

文本模式 document.documentMode
IE7标准 7
IE8标准 8
IE9标准 9
IE5怪异(Quirks) 5

document.documentMode这个JS属性是IE8引入的,对于IE8+无论选择什么文本模式,这个属性都有值。而IE6和IE7下,这个属性是undefined。根据这一点,可以结合UA判断出用户使用的是不是真正的IE7:UA包含IE7时,如果documentMode等于undefined,就一定是真正的IE7浏览器。对于IE8+,这种方法就力不从心。例如IE10在浏览器模式为IE8,文本模式为IE8标准时,与真正的IE8比较,无论是UA,还是document.documentMode,都一模一样。

综上,我们可以通过检查UA中Trident版本和IE版本是否匹配,来判断浏览器是否工作在兼容性视图模式下。结合document.documentMode,还可以判断出用户是否使用真正的IE7浏览器。

JScript引擎版本号

JScript是IE的JS引擎,IE提供了一系列JS接口来获取它的JScript信息:

ScriptEngine() JS中固定返回“JScript”
ScriptEngineMajorVersion() 大版本号
ScriptEngineMinorVersion() 小版本号
ScriptEngineBuildVersion() 内部版本号

我用这些接口测试了IE6~10,发现JScript的版本号只与浏览器有关,与浏览器的浏览器模式或文档模式无关。

浏览器 JScript版本号
IE6 5.6.8827
IE7 5.7.22145
IE8 5.8.18702
IE9 9.0.16434
IE10 10.0.16521

忽略内部版本号,只关注前两个数字,我们会发现,从IE9把JS引擎换成Chakra开始,版本号的规律变了。令人欣慰的是,不同的JScript版本号对应着不同版本的浏览器,这对判断出真正的IE版本很有帮助。例如,要识别低于IE8的浏览器,下面这样写就可以了。

if(ScriptEngineMinorVersion() != 0 && ScriptEngineMinorVersion < 8) {
    //ie8- browser
}

实际上,IE支持的条件编译功能中,有个表示JScript版本的条件编译变量,如下(完整的条件编译变量清单见这里.aspx)):

这个变量是“major.minor”格式的JScript版本号,跟使用JS接口获取到的版本号一致,也只取决于浏览器版本,不受浏览器模式和文本模式的影响。

文本模式对JScript没影响?

看完上一节,再看我之前写的这段:文本模式决定:1)排版引擎;2)JS引擎,有明显的矛盾。难道之前的结论有误?

实际上,JS获取到的JScript版本号仅仅表示了当前浏览器自带的JScript引擎版本(例如IE9始终是9.0,IE7始终是5.7),并不代表任何情况都可以使用这个版本JS引擎的所有功能,页面使用哪种版本的JScript引擎还是由页面的文本模式来决定

例如,IE8的JScript版本是5.8,但是只有在文本模式等于IE8标准时才可以使用JScript5.8的功能,其它任何文本模式都会导致页面使用JScript5.7。

举个例子,JScript5.8增加了对JSON的支持,所以IE8支持原生JSON对象。但网上很多人问为什么在IE8/9下无法使用原生JSON。一种可能是页面没写DTD,导致页面进入了IE5怪异这种文本模式,进而启用了不支持原生JSON的JScript5.7导致的。

再举几个例子:[,].length在JScript9.0之前是2;[1,2,3].join(undefined)在JScript5.8之前是"1undefined2undefined3"。把IE9的文本模式分别改成IE9标准IE8标准IE7标准,可以得到下表:

文本模式 [,].length [1,2,3].join(undefined) 使用的JScript版本
IE7标准 2 "1undefined2undefined3" 5.7
IE8标准 2 "1,2,3" 5.8
IE9标准 1 "1,2,3" 9.0

类似的例子还有很多。大部分情况下,页面使用的JScript引擎版本会随着文本模式的降低而退化,页面对JS的支持度也随之退化。但教主franky提供了一个“for in顺序”的反例:

var o = {1 : '0', 0 : '1'}; for(var i in o) { console.log(i); }

对于IE9+浏览器,这行代码输出顺序始终是0 1;对于IE9以下的浏览器,输出顺序都是1 0,并不受文本模式的影响。关于这一点我没想到比较好的解释。

一些DOM相关的方法,如document.querySelectorAll,在文本模式为IE7标准IE5怪异时不可用;addEventListener在文本模式为IE8标准IE7标准IE5怪异时不可用。这说明IE浏览器对DOM的支持度也会随着文本模式的降低而退化。

但是一些BOM方法,却跟文本模式无关。例如IE8开始支持的postMessage和localStorage,只要浏览器是IE8+,无论什么文本模式,这两个功能都可用。IE9+支持的window.performance,在IE9+浏览器上,也始终可用,跟当前的文本模式无关。

小结下:随着文本模式的降低,页面上实际使用的JScript引擎会退化,一些高版本支持的语言特性不再可用(如JSON),但for in的顺序问题似乎不会退化;DOM相关功能也有相应的退化;但大部分BOM接口却不会退化(如localStorage)。

总结

本文讨论的内容在各部分都小结过,最后只说一个结论:在解决JS兼容性问题时,一定要使用能力检测和特性检测。因为无论是从UA中得到的浏览器信息,还是从JS接口中获取到的JScript引擎版本,都非常不可靠。例如UA中包含IE7,并不一定不支持IE9+的window.performace。也可能是IE9浏览器使用了IE9兼容性视图,UA确实会变成IE7,文本模式为IE7标准,但不影响对window.performace的支持。

另外,虽然IE的浏览器模式和文本模式非常复杂,组合起来有几十种情况,但大部分情况只能通过开发者工具来构造。例如UA中包含IE9,实际上使用JScript5.7的情况(浏览器模式为IE9,文本模式为IE7标准),正常情况下不会出现。

参考:

原文链接:http://www.imququ.com/post/browser-mode-and-document-mode-in-ie-2.html

本文链接:https://75team.com/post/ie的浏览器模式和文本模式(二).html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。