目录
或者我们……?
ActiveX 对象
映射到 DOM 属性的 HTML 属性
对用户行为的假设
完全不起作用的东西
选择测试语法
关于 JavaScript 特性检测的常见问题
什么是 JavaScript 特性检测,为什么它很重要?
JavaScript 特性检测是如何失败的?
特性检测和浏览器检测有什么区别?
如何使用 JavaScript 检测移动设备?
什么是 Feature.js,它如何帮助进行特性检测?
什么是 Modernizr,它如何帮助进行特性检测?
如何使用 device-detector-js 包进行特性检测?
实施特性检测的一些最佳实践是什么?
特性检测能否帮助提高网站性能?
如何了解不同浏览器支持的最新特性?
首页 web前端 js教程 当JavaScript功能检测失败时

当JavaScript功能检测失败时

Feb 22, 2025 am 09:57 AM

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

热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

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

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1672
14
CakePHP 教程
1428
52
Laravel 教程
1332
25
PHP教程
1276
29
C# 教程
1256
24
Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在行动中:现实世界中的示例和项目 JavaScript在行动中:现实世界中的示例和项目 Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

了解JavaScript引擎:实施详细信息 了解JavaScript引擎:实施详细信息 Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:社区,图书馆和资源 Python vs. JavaScript:社区,图书馆和资源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python vs. JavaScript:开发环境和工具 Python vs. JavaScript:开发环境和工具 Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

C/C在JavaScript口译员和编译器中的作用 C/C在JavaScript口译员和编译器中的作用 Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

从网站到应用程序:JavaScript的不同应用 从网站到应用程序:JavaScript的不同应用 Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

See all articles