使用JavaScript检测浏览器的相关特性
一、检测浏览器的名称
问题:
不同的浏览器对
JavaScript
的标准支持也有不同,有时希望脚本能够在不同的浏览器上都能运行良好,这时需要对浏览器进行检测,确定其名称,以针对不同的浏览器编写相应的脚本。
解决方案:
使用
navigator
对象的
appName
属性。
比如,要检测浏览器是否为
IE
,可以这么做:
var
isIE = (navigator.appName == "Microsoft Internet Explorer");
document.write("is IE?" + isIE);
对于
FireFox
,
navigator
对象的
appName
属性值为
"Netscape"
;
Opera9.02
的
appName
属性值为
"Opera"(
其更早版本可能不同
)
;
二、检测浏览器的版本号:
问题:
随着浏览器的版本的更迭,浏览器所支持的脚本特性也在变化,有时候就需要针对不同的版本编写相应的脚本,那么如何获得浏览器的版本号?
解决方案:
通过解析
navigator
对象的
userAgent
属性来获得浏览器的完整版本号。
IE
将自己标识为
MSIE
,后面带一个空格,版本号以及分号。所以我们只要取空格和分号之间的部分即可。如
Windows XP SP2
所带的
IE
的
userAgent
属性值为
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"
,可以看到其版本为
6.0
。可以用如下的函数来获取
IE
浏览器的版本号:
function
getIEVersonNumber()
{
var
ua = navigator.userAgent;
var
msieOffset = ua.indexOf("MSIE ");
if
(msieOffset < 0)
{
return
0;
}
return
parseFloat(ua.substring(msieOffset + 5, ua.indexOf(";", msieOffset)));
}
假设我们要为 IE5 及以上版本编写脚本,可以这么写:
var
isIE5Min = (getIEVersonNumber() >= 5);
if
(isIE5Min)
{
// perform statements for IE 5 or later
}
对于
FireFox
和
Opera
等浏览器,也可以用
navigator.userAgent
属性来获取其版本号,只不过其形式与
IE
有所不同,如
FireFox
:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Opera
:
Opera/9.02 (Windows NT 5.1; U; en)
根据这些形式,我们不难获得其版本号。但这些浏览器的其它版本没有测试过,其具体值不明确,如果要使用这种方法检测,请自行验证。
下面讨论下,上面的那段为
IE5
及以上版本浏览器编写的脚本,使用这种写法要注意:要用
>=
而不是
==
,一般情况下,我们可以假定浏览器是向后兼容的,所以使用
==
显然不能适应新版本;另一方面,我们上面的假定也仅仅是假定,不能确保是这样,如果浏览器的某些对象或属性不能向后兼容,我们的代码也会产生问题,所以建议,少用浏览器版本的比较,更多情况下,应检测是要用的对象或属性是否得到支持。
三、检测客户端的操作系统类型
根据上面的讨论可以看到,
navigator.userAgent
属性通常含有操作系统的基本信息,但很不幸,没有统一的规则去根据
userAgent
获取准确的操作系统信息,因为这些值与浏览器的种类、浏览器的版本甚至浏览器的
OEM
版本都有关系。
通常我们能做的是,检测一些更为通用的信息,比如操作系统是
Windows
还是
Mac
,而不是去看是
Windows 98
还是
Windows XP
。其规则是所有的
Windows
版本都会含有
"Win"
,所有的
Macintosh
版本都含有
"Mac"
,所有的
Unix
则含有
"X11"
,而在
Linux
下则同时包含
"X11"
和
"Linux"
。如:
var
isWin = (navigator.userAgent.indexOf("Win") != -1);
var
isMac = (navigator.userAgent.indexOf("Mac") != -1);
var
isUnix = (navigator.userAgent.indexOf("X11") != -1);
通常用在为不同的操作系统设置不同的字体或位置等样式。
四、检测浏览器对特定对象的支持
问题:
如果需要编写对多种浏览器或浏览器的多个版本都能适用的脚本,就要进行检测一下,浏览器是否支持某个对象。当然这种检测主要是针对那些潜在的不兼容对象的语句。
解决方案:
早期的浏览器对于
img
元素的支持差别很大,所以要在脚本中操作
img
元素,需要检测浏览器是否支持。这时我们不需要对所有可能的浏览器一一检测,只需在必要的地方用下面的方式检测:
function
rollover(imgName, imgSrc)
{
//
如果支持
images
对象
if
(document.images)
{
// statements go here
}
}
这种方法能够生效是基于一个事实:如果
document.images
对象不存在,那么
if
求值的结果为
false
。
使用这种方法,使得对对象的检测变得简单易行,但是我们要注意,对于那些不支持该对象的浏览器要如何较好得处理。看下面的代码:
function
getImgAreas()
{
var
result = 0;
for
(
var
i = 0; i < document.images.length; i++)
{
result += (document.images[i].width * document.images[i].height);
}
return
result;
}
function
reportImageArea()
{
document.form1.imgData.value = getImgAreas();
}
这里没用对象支持的检测。如果浏览器支持 document.images ,这两个函数运行正常;否则就会抛出异常。下面是改进的脚本:
function
getImgAreas()
{
var
result;
//
检测浏览器是否支持对象
if
(document.images)
{
result = 0;
for
(
var
i = 0; i < document.images.length; i++)
{
result += (document.images[i].width * document.images[i].height);
}
}
//
返回值为一个数字或
null
return
result;
}
function
reportImageArea()
{
//
现在可以判断返回值
var
imgArea = getImgAreas();
var
output;
if
(imgArea ==
null
)
{
//
对于不支持
images
对象的浏览器也要给出相应信息
output = "Unknown";
}
else
{
output = imgArea;
}
document.reportForm.imgData.value = output;
}
这样,不管浏览器是否支持该对象,都能给用户比较合理的信息,而不会跳出突兀的错误信息。
五、检测浏览器对特定属性和方法的支持
问题:
检测一个对象是否含有某个特定的属性或方法。
解决方案:
大多数情况下,可以用类似于下面的代码来判断:
if
(objectTest && objectPropertyTest)
{
// OK to work with property
}
先检测对象是否存在,然后再检测对象的属性是否存在。如果对象确实不存在,该方法有效;如果属性存在,但其值为 null, 0, false , if 语句求值的结果也将是 false !所以这种方法并不安全,最好的方法是这样:
if
(objectReference &&
typeof
(objectReference.propertyName) != "undefined")
{
// OK to work with property
}
对于方法的检测也可用类似的方法:
function
myFunction()
{
if
(document.getElementById)
{
//
这里可以使用
getElementById
方法
}
}
2----------------------
1.document.form.item
问题
(1)
现有问题:
现有代码中存在许多
document.formName.item("itemName")
这样的语句,不能在
MF
下运行
(2)
解决方法:
改用
document.formName.elements["elementName"]
(3)
其它
参见
2
2.
集合类对象问题
(1)
现有问题:
现有代码中许多集合类对象取用时使用
()
,
IE
能接受,
MF
不能。
(2)
解决方法:
改用
[]
作为下标运算。如:
document.forms("formName")
改为
document.forms["formName"]
。
又如:
document.getElementsByName("inputName")(1)
改为
document.getElementsByName("inputName")[1]
(3)
其它
3. window.event
(1)
现有问题:
使用
window.event
无法在
MF
上运行
(2)
解决方法:
MF
的
event
只能在事件发生的现场使用,此问题暂无法解决。可以这样变通:
原代码
(
可在
IE
中运行
)
:
<input type="button" name="someButton" value="
提交
" οnclick="javascript:gotoSubmit()"/>
...
<script language="javascript">
function gotoSubmit() {
...
alert(window.event); // use window.event
...
}
</script>
新代码
(
可在
IE
和
MF
中运行
)
:
<input type="button" name="someButton" value="
提交
" οnclick="javascript:gotoSubmit(event)"/>
...
<script language="javascript">
function gotoSubmit(evt) {
evt = evt ? evt : (window.event ? window.event : null);
...
alert(evt); // use evt
...
}
</script>
此外,如果新代码中第一行不改,与老代码一样的话
(
即
gotoSubmit
调用没有给参数
)
,则仍然只能在
IE
中运行,但不会出错。所以,这种方案
tpl
部分仍与老代码兼容。
<script language="javascript">
function run(evnt) {
if(!document.all) {
alert(evnt.target.tagName);
}
else {
alert("IE
下无法看到效果!
");
}
}
</script>
<body>
<table width="200" border="1" οnclick="run(event);">
<tr>
<td bgcolor="#9900CC" onClick="run(event);">1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>11</td>
<td>22</td>
<td>33</td>
</tr>
<tr οnmοuseοver="run();">
<td>111</td>
<td>222</td>
<td>333</td>
</tr>
</table>
</body>
4. HTML
对象的
id
作为对象名的问题
(1)
现有问题
在
IE
中,
HTML
对象的
ID
可以作为
document
的下属对象变量名直接使用。在
MF
中不能。
(2)
解决方法
用
getElementById("idName")
代替
idName
作为对象变量使用。
5.
用
idName
字符串取得对象的问题
(1)
现有问题
在
IE
中,利用
eval(idName)
可以取得
id
为
idName
的
HTML
对象,在
MF
中不能。
(2)
解决方法
用
getElementById(idName)
代替
eval(idName)
。
6.
变量名与某
HTML
对象
id
相同的问题
(1)
现有问题
在
MF
中,因为对象
id
不作为
HTML
对象的名称,所以可以使用与
HTML
对象
id
相同的变量名,
IE
中不能。
(2)
解决方法
在声明变量时,一律加上
var
,以避免歧义,这样在
IE
中亦可正常运行。
此外,最好不要取与
HTML
对象
id
相同的变量名,以减少错误。
(3)
其它
参见
问题
4
7. event.x
与
event.y
问题
(1)
现有问题
在
IE
中,
event
对象有
x, y
属性,
MF
中没有。
(2)
解决方法
在
MF
中,与
event.x
等效的是
event.pageX
。但
event.pageX IE
中没有。
故采用
event.clientX
代替
event.x
。在
IE
中也有这个变量。
event.clientX
与
event.pageX
有微妙的差别(当整个页面有滚动条的时候),不过大多数时候是等效的。
如果要完全一样,可以稍麻烦些:
mX = event.x ? event.x : event.pageX;
然后用
mX
代替
event.x
(3)
其它
event.layerX
在
IE
与
MF
中都有,具体意义有无差别尚未试验。
8.
关于
frame
(1)
现有问题
在
IE
中
可以用
window.testFrame
取得该
frame
,
mf
中不行
(2)
解决方法
在
frame
的使用方面
mf
和
ie
的最主要的区别是:
如果在
frame
标签中书写了以下属性:
<frame src="xx.htm" id="frameId" name="frameName" />
那么
ie
可以通过
id
或者
name
访问这个
frame
对应的
window
对象
而
mf
只可以通过
name
来访问这个
frame
对应的
window
对象
例如如果上述
frame
标签写在最上层的
window
里面的
htm
里面,那么可以这样访问
ie
:
window.top.frameId
或者
window.top.frameName
来访问这个
window
对象
mf
:
只能这样
window.top.frameName
来访问这个
window
对象
另外,在
mf
和
ie
中都可以使用
window.top.document.getElementById("frameId")
来访问
frame
标签
并且可以通过
window.top.document.getElementById("testFrame").src = 'xx.htm'
来切换
frame
的内容
也都可以通过
window.top.frameName.location = 'xx.htm'
来切换
frame
的内容
关于
frame
和
window
的描述可以参见
bbs
的
‘window
与
frame’
文章
以及
/test/js/test_frame/
目录下面的测试
----adun 2004.12.09
修改
9.
在
mf
中,自己定义的属性必须
getAttribute()
取得
10.
在
mf
中没有
parentElement parement.children
而用
parentNode parentNode.childNodes
childNodes
的下标的含义在
IE
和
MF
中不同,
MF
使用
DOM
规范,
childNodes
中会插入空白文本节点。
一般可以通过
node.getElementsByTagName()
来回避这个问题。
当
html
中节点缺失时,
IE
和
MF
对
parentNode
的解释不同,例如
<form>
<table>
<input/>
</table>
</form>
MF
中
input.parentNode
的值为
form,
而
IE
中
input.parentNode
的值为空节点
MF
中节点没有
removeNode
方法,必须使用如下方法
node.parentNode.removeChild(node)
11.const
问题
(1)
现有问题
:
在
IE
中不能使用
const
关键字。如
const constVar = 32;
在
IE
中这是语法错误。
(2)
解决方法
:
不使用
const
,以
var
代替。
12. body
对象
MF
的
body
在
body
标签没有被浏览器完全读入之前就存在,而
IE
则必须在
body
完全被读入之后才存在
13. url encoding
在
js
中如果书写
url
就直接写
&
不要写
&
例如
var url = 'xx.jsp?objectName=xx&objectEvent=xxx';
frm.action = url
那么很有可能
url
不会被正常显示以至于参数没有正确的传到服务器
一般会服务器报错参数没有找到
当然如果是在
tpl
中例外,因为
tpl
中符合
xml
规范,要求
&
书写为
&
一般
MF
无法识别
js
中的
&
14. nodeName
和
tagName
问题
(1)
现有问题:
在
MF
中,所有节点均有
nodeName
值,但
textNode
没有
tagName
值。在
IE
中,
nodeName
的使用好象
有问题(具体情况没有测试,但我的
IE
已经死了好几次)。
(2)
解决方法:
使用
tagName
,但应检测其是否为空。
15.
元素属性
IE
下
input.type
属性为只读,但是
MF
下可以修改
16. document.getElementsByName()
和
document.all[name]
的问题
(1)
现有问题:
在
IE
中,
getElementsByName()
、
document.all[name]
均不能用来取得
div
元素(是否还有其它不能取的元素还不知道)。
第一:
onload
网页加载完执行的函数,这个代码是从十大常用
javascript
的函数里面摘取的,当然有其他的实现方法,但这个函数写的真的非常巧妙。从效率方面也是一个非常值得使用的函数!
以下就是具体代码:
//--------------------------------------------------------
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload
!
= 'function') {
window.onload = func;
}
else {
window.onload = function() {
oldonload();
func();
}
}
}
//--------------------------------------------------------
我不想一句一句的分析这个
addLoadEvent
,那是高手们很难接受的!我们就来说说它的兼容性!请看函数中
window.onload
,作者为什么不用
document.body.onload
呢?那是因为在
Firefox[
以下全部简称
ff]
下
document.body.onload
是
undefined(
未定义
)
,把一个函数赋值给
undefined
既不会发生什么事情,也不算出错!这个是让人头痛!好的,知道兼容性的厉害了吧?那么,以后在编写代码时注意一下就好了!
第二:
body
这个
body
对象也是困扰我们的东西,叫它对象不知道对不对?我不是学计算机专业的,专业术语还真不大清楚!
以下就是具体代码:
//--------------------------------------------------------
function getPageScroll(){
var yScroll;
if (self.pageYOffset) {
yScroll = self.pageYOffset;
}
// Explorer 6 Strict
else if (document.documentElement && document.documentElement.scrollTop){
yScroll = document.documentElement.scrollTop;
} else if (document.body) {// all other Explorers
yScroll = document.body.scrollTop;
}
arrayPageScroll = new Array(''
,
yScroll)
return arrayPageScroll;
}
//--------------------------------------------------------
这个函数作用是:浏览器滚动条滚动的高度读取。这是从
lightbox
中摘录的,读读这段代码,看到三次判断:
1.if (self.pageYOffset)
对
ff
进行判断处理;
2.if (document.documentElement && document.documentElement.scrollTop)
是对网页标准
(xHTML 1.1 DTD)
的判断,这里要说明一下:在加入
xHTML 1.1 DTD
文件头时
document.body.scrollTop
之类的值往往是
0
,而这个是很难被调试察觉的
(
我曾因此困惑很久,这里吐血传授经验啦!
)
;
3.
则是我们常用
document.body.scrollTop
。
从这三次判断,可以想象作者思维的严密了!当然这是程序员的共性!
第三:
attachEvent/addEventListener
这里是比较直接的区别,可是太多的直接却造成了编程的困扰,这不禁使人想起:到底有多少这样的直接不同函数?他们的差别到底在哪里?毕竟大家的大脑空间有限,这么多怎么记?可是这些是必须记住的!没事,这篇文章里常见的
js
兼容性都提到了,可以作为
“
家居旅行,随身携带
”
的小手册!
以下就是具体代码:
//--------------------------------------------------------
_observeAndCache
:
function(element
,
name
,
observer
,
useCapture) {
if (
!
this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element
,
name
,
observer
,
useCapture]);
element.addEventListener(name
,
observer
,
useCapture);
} else if (element.attachEvent) {
this.observers.push([element
,
name
,
observer
,
useCapture]);
element.attachEvent('on' + name
,
observer);
}
}
//--------------------------------------------------------
意思是给对象添加事件。这是
Sam Stephenson
的
prototype
中类的一部分,举这段代码,不是让你去慢慢分析那个
prototype.js
文件,只是说明在
ie
和
Opera
下就可以使用
obj.attachEvent()
,但在
ff
下却只能使用
obj.addEventListener()
。
类似区别的还有:
detachEvent/removeEventListener
parentElement/parentNode
insertAdjacentElement/appendChild
srcElement/target
onmousewheel/DOMMouseScroll
clientY/pageY
第四:对象引用
1.getElementById
请看以下代码:
<!-- 1 -->
<input id="t1"><input type="button"
value="click me" οnclick="alert(t1.value)">
<!-- 2 -->
<input id="t1"><input type="button"
value="click me" οnclick="alert(document.getElementById('t1').value)">
两个都是获取文本框的值,但后者的兼容性就比前者好!对于
IE
来说,一个
HTML
元素的
ID
可以直接在脚本中当作变量名来使用,而
ff
中不可以。
getElementById
这个函数是非常有用、通用的函数,所以在引用对象时我们要尽量使用它!
2.var
请看以下代码:
//--------------------------------------------------------
echo=function(str){
document.write(str);
}
//--------------------------------------------------------
这个函数在
ie
上运行正常,
ff
下却报错了,而在
echo
前加上
var
就正常了,这个就是我们提到
var
的目的。
3.[]
document.forms(”formName”)
改为
document.forms[”formName”]
目的:现有代码中许多集合类对象取用时使用
()
,
ie
能接受,
ff
却不能。
4.frame
的引用
ie
可以通过
id
或者
name
访问这个
frame
对应的
window
对象,而
mf
只可以通过
name
来访问这个
frame
对应的
window
对象。
第五:脚本执行
让我们分别做个试验,请出
ie
和
ff
分别运行一下下面一段
js
:
//--------------------------------------------------------
o={
foo
:
function(){
alert("fly");
}
};
with (o) {
bar();
function bar(){
alert("fly");
}
foo();
}
//--------------------------------------------------------
IE
下,上面的代码成功输出
fly
,
ff
报错:
bar
未定义!
当然这是一个小小的试验,大家很明显的看出
ie
和
ff
的支持执行的情况:
ie
脚本预解释执行,
ff
脚本顺序执行!这是
javascript
编写和设计时必须注意的东西!
第六:
XMLHttpRequest
对象
请看以下代码
//--------------------------------------------------------
function createRequest(){
if(typeof XMLHttpRequest
!
="undefined")? {
return new XMLHttpRequest();
}else if(typeof ActiveXObject
!
="undefined"){
var xmlHttp_ver? = false;
var xmlHttp_vers = [
"MSXML2.XmlHttp.5.0"
,
"MSXML2.XmlHttp.4.0"
,
"MSXML2.XmlHttp.3.0"
,
"MSXML2.XmlHttp"
,
"Microsoft.XmlHttp"
];
if(
!
xmlHttp_ver){
for(var i=0;i<xmlHttp_vers.length;i++){
try{
new ActiveXObject(xmlHttp_vers[i]);
xmlHttp_ver = xmlHttp_vers[i];
break;
}catch(oError){;}
}
}
if(xmlHttp_ver){
return new ActiveXObject(xmlHttp_ver);
}else{
throw new Error("Could not create XML HTTP Request.");
}
}else{
throw new Error("Your browser doesn't support an XML HTTP Request.");
}
}
//--------------------------------------------------------
意思是:得到
XMLHttpRequest
对象,是喜悦村里的一个兄弟写的。在
ie
下,一句
new ActiveXObject("MSXML2.XMLHTTP")
就可以搞定的东西,但这里我们花了这么多行代码来解决兼容性问题,这个函数作者更从原理入手:
xmlHttp_vers
应该从版本高的往版本低的写,这样建立对象的数据调用的是你机子上安装过的最高版本的
MSXML2.XmlHttp
。十分巧妙和有效地得到了对象!
好了,其实关于
javascript
兼容性的例子还有很多,我们无法罗列所有,这里做了个简单介绍,更多的只能在编程工程中去慢慢体会了!当然本文仅仅讨论了
js
的兼容性,同时
css
的兼容性问题也是不可忽视的!解决兼容性最好的方法就是封装!
3---- 浏览器兼容性
JavaScript编程的最大问题来自不同的浏览器对各种技术和标准的支持。构建一个运行在不同浏览器(如IE和火狐)是一个困难的任务。因此几种AJAX JavaScript框架或者生成基于服务端逻辑或标记库的JavaScript,或者提供符合跨浏览器AJAX开发的客户端JavaScript库。一些流行的框架包括:AJAX.Net, Backbase, Bitkraft, Django, DOJO, DWR, MochiKit, Prototype, Rico, Sajax, Sarissa, and Script.aculo.us.
这些框架给开发人员更多的空间使得他们不需要担心跨浏览器的问题。虽然这些框架提升了开发人员构建应用的能力,但由于厂商已经开发了更细节的用户界面的打包组件解决方案,因此在AJAX组件市场中需要考虑一些其他因素。例如提供通用用户界面的组件如组合框和数据栅格的几个厂商,都可以被用来在应用中创建良好的通过类似电子数据表方式来查看和编辑数据的体验。但这些组件不仅是封装了组件的用户界面而且包括与服务端数据的通讯方式,这些组件通常使用基于标记方式来实现如ASP.Net或JSF控件。
Ajax 在本质上是一个浏览器端的技术,首先面临无可避免的第一个问题即是浏览器的兼容性问题。各家浏览器对于 JavaScript/DOM/CSS 的支持总有部分不太相同或是有 Bug ,甚至同一浏览器的各个版本间对于 JavaScript/DOM/CSS 的支持也有可能部分不一样。这导致程序员在写 Ajax 应用时花大部分的时间在调试浏览器的兼容性而非在应用程序本身。因此,目前大部分的 Ajax 链接库或开发框架大多以 js 链接库的形式存在,以定义更高阶的 JavaScript API 、 JavaScript 对象(模板)、或者 JavaScript Widgets 来解决此问题。如 prototype.js