目錄
或者我們……?
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工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Laravel 教程
1601
29
PHP教程
1502
276
如何在node.js中提出HTTP請求? 如何在node.js中提出HTTP請求? Jul 13, 2025 am 02:18 AM

在Node.js中發起HTTP請求有三種常用方式:使用內置模塊、axios和node-fetch。 1.使用內置的http/https模塊無需依賴,適合基礎場景,但需手動處理數據拼接和錯誤監聽,例如用https.get()獲取數據或通過.write()發送POST請求;2.axios是基於Promise的第三方庫,語法簡潔且功能強大,支持async/await、自動JSON轉換、攔截器等,推薦用於簡化異步請求操作;3.node-fetch提供類似瀏覽器fetch的風格,基於Promise且語法簡單

JavaScript數據類型:原始與參考 JavaScript數據類型:原始與參考 Jul 13, 2025 am 02:43 AM

JavaScript的數據類型分為原始類型和引用類型。原始類型包括string、number、boolean、null、undefined和symbol,其值不可變且賦值時復制副本,因此互不影響;引用類型如對象、數組和函數存儲的是內存地址,指向同一對象的變量會相互影響。判斷類型可用typeof和instanceof,但需注意typeofnull的歷史問題。理解這兩類差異有助於編寫更穩定可靠的代碼。

JavaScript時間對象,某人構建了一個eactexe,在Google Chrome上更快的網站等等 JavaScript時間對象,某人構建了一個eactexe,在Google Chrome上更快的網站等等 Jul 08, 2025 pm 02:27 PM

JavaScript開發者們,大家好!歡迎閱讀本週的JavaScript新聞!本週我們將重點關注:Oracle與Deno的商標糾紛、新的JavaScript時間對象獲得瀏覽器支持、GoogleChrome的更新以及一些強大的開發者工具。讓我們開始吧! Oracle與Deno的商標之爭Oracle試圖註冊“JavaScript”商標的舉動引發爭議。 Node.js和Deno的創建者RyanDahl已提交請願書,要求取消該商標,他認為JavaScript是一個開放標準,不應由Oracle

處理諾言:鏈接,錯誤處理和承諾在JavaScript中 處理諾言:鏈接,錯誤處理和承諾在JavaScript中 Jul 08, 2025 am 02:40 AM

Promise是JavaScript中處理異步操作的核心機制,理解鍊式調用、錯誤處理和組合器是掌握其應用的關鍵。 1.鍊式調用通過.then()返回新Promise實現異步流程串聯,每個.then()接收上一步結果並可返回值或Promise;2.錯誤處理應統一使用.catch()捕獲異常,避免靜默失敗,並可在catch中返回默認值繼續流程;3.組合器如Promise.all()(全成功才成功)、Promise.race()(首個完成即返回)和Promise.allSettled()(等待所有完成)

什麼是緩存API?如何與服務人員使用? 什麼是緩存API?如何與服務人員使用? Jul 08, 2025 am 02:43 AM

CacheAPI是瀏覽器提供的一種緩存網絡請求的工具,常與ServiceWorker配合使用,以提升網站性能和離線體驗。 1.它允許開發者手動存儲如腳本、樣式表、圖片等資源;2.可根據請求匹配緩存響應;3.支持刪除特定緩存或清空整個緩存;4.通過ServiceWorker監聽fetch事件實現緩存優先或網絡優先等策略;5.常用於離線支持、加快重複訪問速度、預加載關鍵資源及後台更新內容;6.使用時需注意緩存版本控制、存儲限制及與HTTP緩存機制的區別。

JS綜述:深入研究JavaScript事件循環 JS綜述:深入研究JavaScript事件循環 Jul 08, 2025 am 02:24 AM

JavaScript的事件循環通過協調調用棧、WebAPI和任務隊列來管理異步操作。 1.調用棧執行同步代碼,遇到異步任務時交由WebAPI處理;2.WebAPI在後台完成任務後將回調放入相應的隊列(宏任務或微任務);3.事件循環檢查調用棧是否為空,若為空則從隊列中取出回調推入調用棧執行;4.微任務(如Promise.then)優先於宏任務(如setTimeout)執行;5.理解事件循環有助於避免阻塞主線程並優化代碼執行順序。

了解事件在JavaScript DOM事件中冒泡和捕獲 了解事件在JavaScript DOM事件中冒泡和捕獲 Jul 08, 2025 am 02:36 AM

事件冒泡是從目標元素向外傳播到祖先節點,事件捕獲則是從外層向內傳播到目標元素。 1.事件冒泡:點擊子元素後,事件依次向上觸發父級元素的監聽器,例如點擊按鈕後先輸出Childclicked,再輸出Parentclicked。 2.事件捕獲:設置第三個參數為true,使監聽器在捕獲階段執行,如點擊按鈕前先觸發父元素的捕獲監聽器。 3.實際用途包括統一管理子元素事件、攔截預處理和性能優化。 4.DOM事件流分為捕獲、目標和冒泡三個階段,默認監聽器在冒泡階段執行。

超越地圖和過濾器的高階功能的JS綜述 超越地圖和過濾器的高階功能的JS綜述 Jul 10, 2025 am 11:41 AM

JavaScript數組中,除了map和filter,還有其他強大且不常用的方法。 1.reduce不僅能求和,還可計數、分組、展平數組、構建新結構;2.find和findIndex用於查找單個元素或索引;3.some和every用於判斷是否存在或全部滿足條件;4.sort可排序但會改變原數組;5.使用時注意複製數組避免副作用。這些方法使代碼更簡潔高效。

See all articles