搜索
首页web前端js教程当JavaScript功能检测失败时

When JavaScript Feature Detection Fails

关键要点

  • JavaScript 的特性检测(测试程序员想要使用的特性)并不总是可靠的。例如,在 Internet Explorer 中测试 ActiveXObject 以进行 Ajax 请求、映射到 DOM 属性的 HTML 属性以及对用户行为的假设(例如检测触摸设备)等。
  • 当特性检测失败时,有时需要采用浏览器检测。但是,建议使用专有对象测试而不是 navigator 信息,并将其用于排除浏览器而不是包含浏览器。
  • 在实现浏览器检测时,务必极其小心。始终首先假设完全符合特性测试,只有在知道某个特性无法按预期工作时才求助于浏览器检测。此外,用于对象和特性测试的语法会影响检测的成功率,因此选择正确的语法至关重要。

曾经,浏览器检测是 JavaScript 程序员的看家本领。如果我们知道某些功能在 IE5 中有效但在 Netscape 4 中无效,我们会测试该浏览器并相应地修改代码。例如:

if (navigator.userAgent.indexOf('MSIE 5') != -1) {
  // 我们认为此浏览器是 IE5
}

但是,当我第一次加入这个行业时,军备竞赛就已经开始了!供应商正在向用户代理字符串添加额外的值,因此它们看起来像是其竞争对手的浏览器,也是它们自己的浏览器。例如,这是 Mac 版 Safari 5:

<code>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.59.10 (KHTML, like Gecko) Version/5.1.9 Safari/534.59.10</code>

这将匹配对“Safari”、“Webkit”以及“KHTML”(Webkit 基于的 Konqueror 代码库)的测试;但它也匹配“Gecko”(这是 Firefox 的渲染引擎),当然还有“Mozilla”(由于历史原因,几乎每个浏览器都声称自己是 Mozilla)。

添加所有这些值的目的是规避浏览器检测。如果脚本假设只有 Firefox 才能处理特定功能,否则可能会排除 Safari,即使它可能也能工作。别忘了用户自己可以更改他们的用户代理——我曾经将我的浏览器设置为识别为“Googlebot/1.0”,这样我就可以访问网站所有者认为仅供抓取的内容!

因此,随着时间的推移,这种浏览器检测已成为一个不可能解开的乱麻,并且在很大程度上已不再使用,取而代之的是更好的东西——特性检测。

特性检测只是测试我们想要使用的特性。例如,如果我们需要 getBoundingClientRect(获取元素相对于视口的位 置),那么重要的是浏览器是否支持它,而不是它是哪个浏览器;因此,与其测试受支持的浏览器,不如测试特性本身:

if (typeof document.documentElement.getBoundingClientRect != "undefined") {
  // 浏览器支持此函数
}

不支持该函数的浏览器将返回“undefined”类型,因此不会通过条件。无需在任何特定浏览器中测试脚本,我们就知道它要么正确工作,要么静默失败。

或者我们……?

但事实是——特性检测也不是完全可靠的——有时它会失败。因此,让我们现在看看一些示例,看看我们可以做些什么来解决每个案例。

ActiveX 对象

也许特性检测失败最著名的例子是测试 ActiveXObject 以在 Internet Explorer 中进行 Ajax 请求。

ActiveX 是后期绑定对象的示例,其实际意义是您无法知道它是否受支持直到您尝试使用它。因此,如果用户禁用了 ActiveX,则以下代码将引发错误:

if (navigator.userAgent.indexOf('MSIE 5') != -1) {
  // 我们认为此浏览器是 IE5
}

要解决此问题,我们需要使用异常处理——尝试实例化对象,捕获任何失败,并相应地处理它:

<code>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.59.10 (KHTML, like Gecko) Version/5.1.9 Safari/534.59.10</code>

映射到 DOM 属性的 HTML 属性

属性映射通常用于测试与 HTML5 属性一起使用的 API 的支持。例如,通过查找可拖动属性来检查具有 [draggable="true"] 的元素是否支持拖放 API:

if (typeof document.documentElement.getBoundingClientRect != "undefined") {
  // 浏览器支持此函数
}

这里的问题是 IE8 或更早版本会自动将所有HTML 属性映射到 DOM 属性。这就是为什么 getAttribute 在这些旧版本中如此混乱的原因,因为它根本不返回属性,而是返回 DOM 属性。

这意味着如果我们使用已经具有属性的元素:

if (typeof window.ActiveXObject != "undefined") {
  var request = new ActiveXObject("Microsoft.XMLHTTP");
}

那么即使它们不支持,IE8 或更早版本也会返回 true 用于 ("draggable" in element)

属性可以是任何内容:

if (typeof window.ActiveXObject != "undefined") {
  try {
    var request = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (ex) {
    request = null;
  }
  if (request !== null) {
    //... 我们有一个请求对象
  }
}

但结果将相同——IE8 或更早版本将返回 true 用于 ("nonsense" in element)

在这种情况下,解决方案是使用不具有该属性的元素进行测试,最安全的方法是使用创建的元素:

if ("draggable" in element) {
  // 浏览器支持拖放
}

对用户行为的假设

您可能已经看到使用以下代码来检测触摸设备:

<div draggable="true"> ... </div>

大多数触摸设备在触发点击事件之前会实现人工延迟(通常约为 300 毫秒),这是为了避免在双击元素的同时也点击它们。但这会使应用程序感觉迟缓且无响应,因此开发人员有时会使用该特性测试来分叉事件:

<div nonsense="true"> ... </div>

但是,此条件源于一个错误的假设——因为设备支持触摸,因此将使用触摸。但是触摸屏笔记本电脑呢?用户可能正在触摸屏幕,也可能正在使用鼠标或触控板;上面的代码无法处理这种情况,因此用鼠标单击将不会执行任何操作。

在这种情况下,解决方案根本不是测试事件支持——而是同时绑定两个事件,然后使用 preventDefault 来阻止触摸生成点击:

if (navigator.userAgent.indexOf('MSIE 5') != -1) {
  // 我们认为此浏览器是 IE5
}

完全不起作用的东西

承认这一点很痛苦,但有时我们不需要测试的不是特性——而是浏览器——因为特定浏览器声称支持某些不起作用的东西。最近的一个例子是 Opera 12 中的 setDragImage()(这是拖放 dataTransfer 对象的一种方法)。

特性测试在这里失败是因为 Opera 12 声称支持它;异常处理也无济于事,因为它不会引发任何错误。它只是不起作用:

<code>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.59.10 (KHTML, like Gecko) Version/5.1.9 Safari/534.59.10</code>

现在,如果您只想尝试添加自定义拖动图像,并且乐于在不支持的情况下保留默认值(这将发生),那么这可能很好。但是,如果您的应用程序确实需要自定义图像,以至于不支持它的浏览器应该使用完全不同的实现(即使用自定义 JavaScript 来实现所有拖动行为)呢?

或者,如果浏览器实现了某些功能,但存在无法避免的渲染错误呢?有时我们别无选择,只能明确检测有问题的浏览器,并将其排除在使用它本来会尝试支持的功能之外。

因此,问题变成了——实现浏览器检测最安全的方法是什么?

我有两点建议:

  1. 优先使用专有对象测试而不是 navigator 信息。
  2. 将其用于排除浏览器而不是包含浏览器。

例如,可以使用 window.opera 对象检测 Opera 12 或更早版本,因此我们可以使用该排除来测试可拖动支持:

if (typeof document.documentElement.getBoundingClientRect != "undefined") {
  // 浏览器支持此函数
}

最好使用专有对象而不是标准对象,因为当发布新浏览器时,测试结果不太可能发生变化。以下是一些我最喜欢的示例:

if (typeof window.ActiveXObject != "undefined") {
  var request = new ActiveXObject("Microsoft.XMLHTTP");
}

对象测试也可以与特性测试结合使用,以确定特定浏览器中特定特性的支持,或者在紧急情况下,定义更精确的浏览器条件:

if (typeof window.ActiveXObject != "undefined") {
  try {
    var request = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (ex) {
    request = null;
  }
  if (request !== null) {
    //... 我们有一个请求对象
  }
}

我们已经注意到用户代理字符串是一个不可靠的混乱,但供应商字符串实际上相当可预测,并且可以用来可靠地测试 Chrome 或 Safari:

if ("draggable" in element) {
  // 浏览器支持拖放
}

所有这一切的黄金法则是要极其小心。确保您在尽可能多的浏览器中测试条件,并仔细考虑它们的向前兼容性——目标是使用浏览器条件来排除浏览器,因为存在已知的错误,而不是因为已知的特性而包含它们(这就是特性测试的目的)

从根本上说,始终首先假设完全符合特性测试——除非您知道情况并非如此,否则假设特性将按预期工作。

选择测试语法

在结束之前,我想检查一下我们可以用于对象和特性测试的不同类型的语法。例如,近年来,以下语法已变得很常见:

if (navigator.userAgent.indexOf('MSIE 5') != -1) {
  // 我们认为此浏览器是 IE5
}

过去我们无法使用它,因为 IE5 及其同类产品会因语法而引发错误;但现在我们不必支持这些浏览器,这已不再是问题。

从本质上讲,它与以下内容完全相同,但编写起来更短:

<code>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.59.10 (KHTML, like Gecko) Version/5.1.9 Safari/534.59.10</code>

但是,测试条件通常依赖于自动类型转换:

if (typeof document.documentElement.getBoundingClientRect != "undefined") {
  // 浏览器支持此函数
}

我们在某些浏览器对象测试(例如 window.opera 测试)中早些时候使用了该语法,这是安全的,因为对象如何评估——任何已定义的对象或函数都将始终评估为 true,而如果它未定义,则将评估为 false。

但是我们可能正在测试有效返回 null 或空字符串的东西,这两者都评估为 false。例如,style.maxWidth 属性有时用于排除 IE6:

if (typeof window.ActiveXObject != "undefined") {
  var request = new ActiveXObject("Microsoft.XMLHTTP");
}

只有在支持 maxWidth 属性并且具有作者定义的值时,它才会评估为 true,因此如果我们这样编写测试,它可能会失败:

if (typeof window.ActiveXObject != "undefined") {
  try {
    var request = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (ex) {
    request = null;
  }
  if (request !== null) {
    //... 我们有一个请求对象
  }
}

一般规则是这样的:依赖于自动类型转换对于对象和函数是安全的,但对于字符串和数字或可能为 null 的值并不一定安全。

话虽如此——如果您能安全地使用它,那就这样做,因为它在现代浏览器中通常要快得多(可能是因为它们针对这种类型的条件进行了优化)。

有关此内容的更多信息,请参阅:现实世界中的自动类型转换。

关于 JavaScript 特性检测的常见问题

什么是 JavaScript 特性检测,为什么它很重要?

JavaScript 特性检测是开发人员用来确定用户浏览器是否支持特定特性或 API 的一种技术。这至关重要,因为并非所有浏览器都支持 JavaScript 的所有特性。通过使用特性检测,开发人员可以为不受支持的特性提供替代解决方案或后备方案,确保网站或应用程序在不同浏览器上都能正确运行。这增强了用户体验并确保了兼容性。

JavaScript 特性检测是如何失败的?

JavaScript 特性检测可能会由于多种原因而失败。一个常见的原因是特性检测代码的实现不正确。例如,如果代码检查对象中不存在的属性,它将返回 undefined,导致假阴性。另一个原因可能是浏览器的怪癖或错误,这可能会导致特性检测给出不准确的结果。

特性检测和浏览器检测有什么区别?

特性检测涉及检查用户浏览器是否支持特定特性或 API,而浏览器检测则识别用户的浏览器和版本。虽然这两种技术都旨在确保兼容性和功能性,但特性检测通常被认为是一种更好的实践,因为它直接检查特性,而不是根据浏览器类型或版本来假设其支持。

如何使用 JavaScript 检测移动设备?

您可以使用 JavaScript 中的 navigator.userAgent 属性来检测移动设备。此属性返回一个字符串,表示浏览器的用户代理标头。通过检查此字符串中的特定关键字(例如“Android”、“iPhone”或“iPad”),您可以确定用户是否在移动设备上。

什么是 Feature.js,它如何帮助进行特性检测?

Feature.js 是一个轻量级、快速且简单的 JavaScript 实用程序,用于特性检测。它提供易于使用的 API,允许开发人员测试浏览器是否支持特定特性。这有助于为不受支持的特性提供后备方案或替代解决方案,从而增强网站或应用程序的兼容性和功能性。

什么是 Modernizr,它如何帮助进行特性检测?

Modernizr 是一个 JavaScript 库,可帮助开发人员利用 HTML5 和 CSS3 特性,同时保持与旧版浏览器的兼容性。它使用特性检测来检查浏览器是否支持特定特性,并将类添加到 HTML 元素,允许您在样式表或 JavaScript 中定位特定浏览器功能。

如何使用 device-detector-js 包进行特性检测?

device-detector-js 包是用于设备检测的强大工具。它解析用户代理字符串并检测智能手机、平板电脑、台式机、电视机等设备。它还检测浏览器、引擎、操作系统和其他有用信息。您可以使用此包根据检测到的设备调整网站或应用程序的行为。

实施特性检测的一些最佳实践是什么?

实施特性检测的一些最佳实践包括:使用可靠且经过测试的库(如 Modernizr 或 Feature.js)、在不同的浏览器和设备上彻底测试您的特性检测代码、为不受支持的特性提供替代解决方案或后备方案以及避免根据浏览器类型或版本来假设特性支持。

特性检测能否帮助提高网站性能?

是的,特性检测可以帮助提高网站性能。通过检测不受支持的特性并提供替代解决方案或后备方案,您可以防止不必要的代码在浏览器中运行。这可以减少加载时间并提高网站的整体性能。

如何了解不同浏览器支持的最新特性?

由于 Web 开发的快速发展,了解不同浏览器支持的最新特性可能具有挑战性。但是,Mozilla 开发者网络 (MDN)、Can I Use 和 JavaScript 文档等资源可以提供有关不同浏览器中特性支持的最新信息。

以上是当JavaScript功能检测失败时的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

node.js流带打字稿node.js流带打字稿Apr 30, 2025 am 08:22 AM

Node.js擅长于高效I/O,这在很大程度上要归功于流。 流媒体汇总处理数据,避免内存过载 - 大型文件,网络任务和实时应用程序的理想。将流与打字稿的类型安全结合起来创建POWE

Python vs. JavaScript:性能和效率注意事项Python vs. JavaScript:性能和效率注意事项Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差异主要体现在:1)Python作为解释型语言,运行速度较慢,但开发效率高,适合快速原型开发;2)JavaScript在浏览器中受限于单线程,但在Node.js中可利用多线程和异步I/O提升性能,两者在实际项目中各有优势。

JavaScript的起源:探索其实施语言JavaScript的起源:探索其实施语言Apr 29, 2025 am 12:51 AM

JavaScript起源于1995年,由布兰登·艾克创造,实现语言为C语言。1.C语言为JavaScript提供了高性能和系统级编程能力。2.JavaScript的内存管理和性能优化依赖于C语言。3.C语言的跨平台特性帮助JavaScript在不同操作系统上高效运行。

幕后:什么语言能力JavaScript?幕后:什么语言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境