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

发表于 2013-09-07 Author: Jerry Qu

一年半之前我写了一篇《关于浏览器模式和文本模式的困惑》,介绍了 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 所特有。

除此之外,上面 IE7 IE8 这两种浏览器模式,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-
}

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

<script type="text/javascript">
    /*@cc_on
        alert(@_jscript_version);
    @*/
</script>

这个变量是「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标准),正常情况下不会出现。

参考:

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

获取真实的IE版本(转)的更多相关文章

  1. 关于httpservletrequest的获取真实的ip

    via 值为: 下面是一些DemoWTP/1.1 GDSZ-PS-GW010-WAP05.gd.chinamobile.com (Nokia WAP Gateway 4.0 CD3/ECD13_C/N ...

  2. 容器中JVM获取真实的CPU核数

    容器中JVM获取真实的CPU核数 基于 libsysconfcpus的方案,可以为各个版本的JDK提供一个通用的解决方案. libsysconfcpus.so的原理是截获JVM获取CPU核数所用的系统 ...

  3. React Native中ref的用法(通过组件的ref属性,来获取真实的组件)

    ref是什么? ref是组件的特殊属性,组件被渲染后,指向组件的一个引用.可以通过组件的ref属性,来获取真实的组件.因为,组件并不是真正的DOM节点,而是存在于内存中的一种数据结构,称为虚拟的DOM ...

  4. Atitit linux获取项目运行环境版本

    Atitit linux获取项目运行环境版本 1.1. Nginx版本1 1.2. Php版本1 1.3. Mysql版本2 1.4. Redis版本2 1.1. Nginx版本 [root@iZ25 ...

  5. .NET 获取客户端的操作系统版本、浏览器版本和IP地址

    我们在使用.NET做网站的时候,很多情况下需要需要知道客户端的操作系统版本和浏览器版本,怎样获取客户端的操作系统和浏览器版本呢?我们可以通过分析UserAgent来获取. .NET 获取客户端的操作系 ...

  6. 小米抢购(简单版v0.1)-登录并验证抢购权限,以及获取真实抢购地址

    小米(简单版)-登录并验证抢购权限,以及获取真实抢购地址! 并不是复制到浏览器就行了的   还得传递所需要的参数 这里只是前部分  后面的自己发挥了 { "stime": 1389 ...

  7. JavaScript获取浏览器类型与版本

    从网上找到一段使用JavaScript判断浏览器以及浏览器版本的比较好的代码,在此记录一下: <script type="text/javascript"> var S ...

  8. 阿里云SLB后Nginx、Tomcat获取真实IP

    一.SLB后Nginx如何获取真实IP 前提:nginx作为slb获取真实ip是使用 http_realip_module,默认一键安装包安装的nginx没有安装这个模块需要重新重新编译nginx并加 ...

  9. nginx+tomcat集群配置(3)---获取真实客户端IP

    前言: 在初步构建的nginx+tomcat服务集群时, 发现webserver获取到的客户端ip都是同一个, 皆为作为反向代理服务的nginx所在的机器IP. 这不太符合我们的基本需求, 为将来的数 ...

随机推荐

  1. Delphi控件开发浅入深出(三)

    三.开关控件TlincoSwitch 用过Delphi1(好古老的东东呀!)的人相信都记得这个开关控件 ,不知道当初Borland为什么把这么一个在开发普通应用程序中应用不到的工控控件放到Delphi ...

  2. 如何查看Oracle日志

    Oracle日志查看 一.Oracle日志的路径: 登录:sqlplus "/as sysdba" 查看路径:SQL> select * from v$logfile; SQ ...

  3. python中常用的推导(字典推导和列表推导)

    在python开发中经常需要书写这样的代码 result = list() for data in datas: if data not in ['a', 'b']: result.append(da ...

  4. 怎样删除Weblogic Domain?

    转自:http://blog.csdn.net/biplusplus/article/details/7433558 旁白 由于没有现成的配置工具可以做这件事,我们需要手工来删除. 正题 以下方法适用 ...

  5. poj 2253 (dis最短路径)

    Frogger Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 24979   Accepted: 8114 Descript ...

  6. vue - package.json

    描述:包管理信息(npm || yarn) npm 和 yarn 站在了对立面. 不过我还是首推 yarn. 

  7. android精确绘制文字位置的方法

    android 中使用Canvas的drawText绘制文本的位置,是基于基线的. 例如以下图: 当中字母Q的小尾巴在横线以下了. 怎么样找准字母的中心位置呢? 先看以下的样例:(右边的数字,表示字体 ...

  8. STL源码剖析(迭代器)

    在STL中,容器跟算法是分开设计的,算法是通过迭代器来对容器进行操作的. 在算法运用迭代器的时候,可能会用到其相应的型别,例如返回值为容器中元素的型别,又或者说根据迭代器的类型来选择更好的算法等等. ...

  9. java异常处理Exception

    我看别人的面经中有一道题目就问到了Exception,即java的异常处理的,我曾经也学了java的异常处理,可是我查了下,看了别人的博客关于写的exception异常处理.我发现,自己学的不坚固,仅 ...

  10. java数据库操作:JDBC的操作

    1,JDBC注意操作类及接口: 数据库操作过程: 1)打开数据库服务 2)连接数据库:一般都要输入用户名,密码, 3)操作数据库:创建表:查询表,更新,记录. 4)关闭数据库. 1,DriverMan ...