携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
如今,IE浏览器的溃败,让浏览器之间的差异已经好了很多,但浏览器之间的一致性依旧是Web开发中的常见主题,现在要检测浏览器的方法有很多,但每种都有各自的长处和不足,问题的关键在于知道客户端检测 应该是解决问题的最后一个举措。任何时候,只要有更普适的方案可选,都应该毫不犹豫地选择。首先 要设计最常用的方案,然后再考虑为特定的浏览器进行补救。
能力检测
能力检测(又称特性检测)即在 JavaScript 运行时中使用一套简单的检测逻辑,测试浏览器是否支持某种特性。这种方式不需要知道当前是什么浏览器,只需要知道这个浏览器有没有这个能力。
例如:
在IE5之前没有document.getElementByid()这个方法,但是当时有document.all属性实现同样的功能。这时候我们就可以进能力检测。
function getElement(id){
if(document.getElementById){
return document.getElementById(id);
} else if(document.all){
return document.all[id];
} else {
throw new Error("无法获取Element")
}
}
getElement()函数的目的获取DOM元素。先通过if判断浏览器是否拥有document.getElementById()这个方法,如果有就使用它获取DOM元素,如果这函数不存在(不是undefined),那就再检测document.all这个方法,有就使用,如果这两个函数都不存在(基本上没有种情况),就会抛出错误,无法获取DOM元素。
注意:这里的判断顺序应该将最常用的方式最先检测。
安全能力检测
安全能力检测是是检测是为了检测能力是否存在,而且有我们预期的行为。这样说可能大家不太懂,下面我们通过一个例子来理解这句话。
大家常常用来检测某个对象是否拥有某个方法是通过什么方式来检测的?是像下面这样吗:
function isSort(object){
return !!object.sort;
}
我们通过上面的方法来判断对象是否拥有排序方法,但是这样有一个问题,如果对象中拥有一个sort属性,这个函数还是回会返回true。所以这里我们值完成了我们刚刚那句话的前半部分,检测能力是否存在,但没有检测能力是否符合我们的预期行为。
所以更好的测试是检测sort是否是函数:
function isSort(object){
return typeof object.sort===='function';
}
通过这样的方式,我们就检测的对象是否可以调用它对属性进行排序。
基于能力检测进行浏览器分析
这时有人可能就会觉得,就这呀,这也不很吊呀,常规的代码里也会使用到这些。那这里再教你们一些不一样的。
检测特性
如果你的网站需要使用浏览器的特定能力,那么最好放在一起集中检测,而不是要用的时候再来重复检测
// 检测浏览器是否支持 Netscape 式的插件
let hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);
// 检测浏览器是否具有 DOM Level 1 能力
let hasDOM1 = !!(document.getElementById&&document.createElement&&document.getElementsByTagName);
这里我们检测了两项,并且存在变量中,当我们需要使用的时候,就可以将变量用在条件语句中,这样比重复检测省事多了。
检测浏览器
我们可通过浏览器的一些特性检测,确定用户使用的是什么浏览器。但这套方案未来不一定适用。 我们可以根据浏览器的行为推断出用户使用的浏览器。
class BrowserDetector {
constructor() { // 测试条件编译 // IE6~10 支持
this.isIE_Gte6Lte10 = /*@cc_on!@*/false;
// 测试 documentMode // IE7~11 支持
this.isIE_Gte7Lte11 = !!document.documentMode;
//测试 StyleMedia 构造函数 // Edge 20 及以上版本支持
this.isEdge_Gte20 = !!window.StyleMedia;
// 测试 Firefox 专有扩展安装 API
// 所有版本的 Firefox 都支持
this.isFirefox_Gte1 = typeof InstallTrigger !== 'undefined';
// 测试 chrome 对象及其 webstore 属性
// Opera 的某些版本有 window.chrome,但没有 window.chrome.webstore
// 所有版本的 Chrome 都支持
this.isChrome_Gte1 = !!window.chrome && !!window.chrome.webstore;
// Safari 早期版本会给构造函数的标签符追加"Constructor"字样,如:
// window.Element.toString(); // [object ElementConstructor]
// Safari 3~9.1 支持
this.isSafari_Gte3Lte9_1 = /constructor/i.test(window.Element);
// 推送通知 API 暴露在 window 对象上
// 使用默认参数值以避免对 undefined 调用 toString()
// Safari 7.1 及以上版本支持
this.isSafari_Gte7_1 = (({ pushNotification = {} } = {}) => pushNotification.toString() == '[object SafariRemoteNotification]')(window.safari);
// 测试 addons 属性
// Opera 20 及以上版本支持
this.isOpera_Gte20 = !!window.opr && !!window.opr.addons;
}
isIE() { return this.isIE_Gte6Lte10 || this.isIE_Gte7Lte11; }
isEdge() { return this.isEdge_Gte20 && !this.isIE(); }
isFirefox() { return this.isFirefox_Gte1; }
isChrome() { return this.isChrome_Gte1; }
isSafari() { return this.isSafari_Gte3Lte9_1 || this.isSafari_Gte7_1; }
isOpera() { return this.isOpera_Gte20; }
}
这个类暴露的通用浏览器检测方法使用了检测浏览器范围的能力测试。随着浏览器的变迁及发展, 可以不断调整底层检测逻辑,但主要的 API 可以保持不变。
能力检测的局限性
上面检测浏览器的方法不是一直适用,在未来说不定其他浏览器也支持这些特性,当特性重复的时候,我们就没办法判断用户具体使用的是什么浏览器了。