Implementing feature detection

2018-05-15 17:26 更新
先決條件: 熟悉核心 HTML , CSS JavaScript 語(yǔ)言; 高級(jí)跨瀏覽器測(cè)試原則的概念。
目的: 理解特征檢測(cè)的概念,并能夠在CSS和JavaScript中實(shí)現(xiàn)合適的解決方案。

特征檢測(cè)的概念

功能檢測(cè)背后的理念是,您可以運(yùn)行測(cè)試,以確定當(dāng)前瀏覽器中是否支持某個(gè)功能,然后有條件地運(yùn)行代碼,以便在支持該功能的瀏覽器中提供可接受的體驗(yàn)。 不要的瀏覽器。 如果不這樣做,那么不支持您在代碼中使用的功能的瀏覽器將無(wú)法正常顯示您的網(wǎng)站,只會(huì)導(dǎo)致失敗,從而造成糟糕的用戶體驗(yàn)。

讓我們回顧一下我們?cè)?a href="/webstart/Cross_browser_testing/JavaScript#Feature_detection">處理常見(jiàn)JavaScript問(wèn)題( org / zh-CN / docs / Web / API / Geolocation / Using_geolocation">地理位置API (暴露運(yùn)行Web瀏覽器的設(shè)備的可用位置數(shù)據(jù))具有其使用的主入口點(diǎn), 代碼>地理位置屬性在全局導(dǎo)航器對(duì)象上可用。 因此,您可以通過(guò)使用類似以下內(nèi)容來(lái)檢測(cè)瀏覽器是否支持地理位置:

if("geolocation" in navigator) {
  navigator.geolocation.getCurrentPosition(function(position) {
    // show the location on a map, perhaps using the Google Maps API
  });
} else {
  // Give the user a choice of static maps instead perhaps
}

這可能是更好地使用已建立的特征檢測(cè)庫(kù),而不是寫(xiě)你自己的所有時(shí)間。 Modernizr是特征檢測(cè)測(cè)試的行業(yè)標(biāo)準(zhǔn),稍后我們將介紹。

在我們繼續(xù)之前,我們先說(shuō)一說(shuō) - 不要將特征檢測(cè)與瀏覽器嗅探(檢測(cè)特定瀏覽器訪問(wèn)網(wǎng)站)混淆 - 這是一個(gè)可怕的做法,應(yīng)該 不惜一切代價(jià)。 有關(guān)詳情,請(qǐng)參見(jiàn)使用錯(cuò)誤的瀏覽器嗅探代碼。

編寫(xiě)自己的特性檢測(cè)測(cè)試

在本節(jié)中,我們將介紹如何在CSS和JavaScript中實(shí)現(xiàn)自己的特性檢測(cè)測(cè)試。

CSS

您可以通過(guò)測(cè)試 element.style的存在性來(lái)編寫(xiě)CSS功能的測(cè)試。 屬性 (例如 paragraph.style.transform )。

一個(gè)典型的示例可能是在瀏覽器中測(cè)試 Flexbox 支持; 對(duì)于支持最新Flexbox規(guī)范的瀏覽器,我們可以使用靈活和強(qiáng)大的flex布局。 對(duì)于沒(méi)有的瀏覽器,我們可以使用一個(gè)浮動(dòng)的布局工作正常,雖然它稍微更脆弱和hacky,而不是那么酷。

讓我們實(shí)現(xiàn)一些演示這一點(diǎn),雖然我們現(xiàn)在保持簡(jiǎn)單。

  1. Start by making local copies of our css-feature-detect.html, flex-layout.css, float-layout-css, and basic-styling.css files. Save them in a new directory.
  2. We will add the HTML5 Shiv to our example too, so that the HTML5 semantic elements will style properly in older versions of IE. Download the latest version? (see Manual installation), unzip the ZIP file, copy the html5shiv-printshiv.min.js and html5shiv.min.js files into your example directory, and link to one of the files by putting the following under your <title> element:
    <script src="html5shiv.min.js"></script>
  3. Have a look at your example CSS files — you'll see that?basic-styling.css handles all the styling that we want to give to every browser, whereas the other two CSS files contain the CSS we want to selectively apply to browser depending on their support levels. You can look at the different effects these two files they have by manually changing the CSS file referred to by the second <link> element, but let's instead implement some JavaScript to automatically swap them as needed.
  4. First, remove the contents of the second <link> element's href attribute. We will fill this in dynamically later on.
  5. Next, add a <script></script> element at the bottom of your body (just before the closing </body> tag).
  6. Give it the following contents:
    var conditional = document.querySelector('.conditional');
    var testElem = document.createElement('div');
    if(testElem.style.flex !== undefined && testElem.style.flexFlow !== undefined) {
      conditional.setAttribute('href', 'flex-layout.css');
    } else {
      conditional.setAttribute('href', 'float-layout.css');
    }

<link> element, and creating a <div> element as part of our test.\">這里我們抓住第二個(gè)< link> 元素的引用,并創(chuàng)建一個(gè)< div> 元素作為測(cè)試的一部分。在我們的條件語(yǔ)句中,我們測(cè)試flex\">Flex項(xiàng)目可以被拉伸以使用與它們的flex增長(zhǎng)因子或它們的flex收縮因子成比例的可用空間以防止溢出。"> flex and >和flex-flow properties exist in the browser.\">-wrap individual properties。"> flex-flow 屬性存在于瀏覽器中。請(qǐng)注意如何存儲(chǔ)在HTMLElement.style\">返回一個(gè)CSSStyleDeclaration對(duì)象,它只表示元素的內(nèi)聯(lián)樣式屬性,忽略任何應(yīng)用的樣式規(guī)則。請(qǐng)參閱CSS屬性參考以獲取通過(guò)樣式可訪問(wèn)的CSS屬性列表"> HTMLElement.style object use lower camel case, not hyphens, to separate the words.\">>對(duì)象使用下駱駝的情況,不是連字符,來(lái)分隔詞。

當(dāng)您保存所有內(nèi)容并嘗試您的示例時(shí),如果瀏覽器支持現(xiàn)代flexbox,則應(yīng)該看到flexbox布局應(yīng)用于頁(yè)面,如果沒(méi)有,則應(yīng)該看到float布局。

注意:對(duì)于次要功能檢測(cè)問(wèn)題,這種方法通常是過(guò)度使用的 - 您可以使用多個(gè)供應(yīng)商前綴和備用屬性,如 #CSS_fallback_behaviour"> CSS后備行為處理CSS前綴。

@supports

最近,CSS已經(jīng)引入了自己的本機(jī)功能檢測(cè)機(jī)制 - CSS規(guī)則將由大括號(hào)分隔的CSS塊中的一組嵌套語(yǔ)句與由CSS聲明的測(cè)試組成的條件(即屬性值對(duì),與任意連接,析取和否定 這樣的條件稱為支持條件。"> @supports at-rule。 此操作方式與媒體查詢(另請(qǐng)參閱 webstart / Cross_browser_testing / HTML_and_CSS#Responsive_design_problems">響應(yīng)式設(shè)計(jì)問(wèn)題) - 除了根據(jù)媒體功能(如分辨率,屏幕寬度或?qū)捀弑?選擇性應(yīng)用CSS之外,它還會(huì)根據(jù)CSS功能 支持。

例如,我們可以重寫(xiě)我們前面的例子來(lái)使用 @supports - 參見(jiàn) testing / cross-browser-testing / feature-detection / supports-feature-detect.html"class ="external"> supports-feature-detect.html https://github.com/mdn/learning-area/blob/master/tools-testing/cross-browser-testing/feature-detection/supports-styling.css"class ="external"> supports-styling.css / a> 。 如果你看看后者,你會(huì)看到一些 @supports 塊,例如:

@supports (flex-flow: row) and (flex: 1) {

  main {
    display: flex;
  }

  main div {
    padding-right: 4%;
    flex: 1;
  }

  main div:last-child {
    padding-right: 0;
  }

}

只有當(dāng)前瀏覽器同時(shí)支持 flex-flow:row flex:1 聲明時(shí),此規(guī)則塊才應(yīng)用CSS規(guī)則。 對(duì)于每個(gè)條件的工作,你需要包括一個(gè)完整的聲明(不只是一個(gè)屬性名稱)和不包括分號(hào)在末尾。

@supports 也有 OR NOT 邏輯可用 - 如果flexbox屬性不可用,其他塊應(yīng)用float布局:

@supports not (flex-flow: row) and (flex: 1) {

  /* rules in here */

}

這可能看起來(lái)比以前的例子更方便 - 我們可以在CSS中執(zhí)行所有的特性檢測(cè),不需要JavaScript,我們可以處理單個(gè)CSS文件中的所有邏輯,減少HTTP請(qǐng)求。 這里的問(wèn)題是瀏覽器支持 - 在IE中不支持 @supports ,并且僅在最新版本的Safari / iOS WebKit(9 + / 9.2 +)中支持,而JavaScript版本應(yīng)該工作 在較舊的瀏覽器(可能回到IE8或9,雖然舊版本的IE將有額外的問(wèn)題,如不支持 / Document / querySelector"title ="返回文檔中的第一個(gè)元素(使用文檔標(biāo)記中的第一個(gè)元素的深度優(yōu)先預(yù)先遍歷文檔的節(jié)點(diǎn),并按照子節(jié)點(diǎn)數(shù)量的順序迭代順序節(jié)點(diǎn))匹配 指定的選擇器組"> Document.querySelector ,并且具有混亂的框模型)。

JavaScript

我們?cè)缇鸵呀?jīng)看到了一個(gè)JavaScript特性檢測(cè)測(cè)試的例子。 通常,這種測(cè)試通過(guò)以下通用模式之一來(lái)完成:

Summary of JavaScript feature detection techniques
特征檢測(cè)類型 說(shuō)明 例子
如果對(duì)象中的成員 檢查某個(gè)方法或?qū)傩?通常是使用API或要檢測(cè)的其他功能的入口點(diǎn))是否存在于其父對(duì)象中。

if("geolocation"in navigator){...}

元素的屬性 Create an element in memory using Document.createElement() and then check if a property exists on it. The example shown is a way of detecting HTML5 Canvas support. function supports_canvas() {
return?!!document.createElement('canvas').getContext;
}

if(supports_canvas()) { ... }
元素返回值的方法 Create an element in memory using Document.createElement() and then check if a method exists on it. If it does, check what value it returns. 請(qǐng)參見(jiàn)深入HTML5視頻格式檢測(cè)測(cè)試。
屬性屬性保留值 Create an element in memory using Document.createElement(), set a property to a certain value, then check to see if the value is retained. See Dive into HTML5 <input> types detection test.

深入HTML5檢測(cè)HTML5功能頁(yè)面除了上面列出的功能外,還有更多有用的功能檢測(cè)測(cè)試 ,您通??梢酝ㄟ^(guò)在您最喜歡的搜索引擎中搜索"檢測(cè)對(duì)您的特性的支持"來(lái)查找大多數(shù)功能的功能檢測(cè)測(cè)試。 請(qǐng)記住,某些功能被認(rèn)為是無(wú)法檢測(cè)的 - 請(qǐng)參閱Modernizr的 Undetectables 列表。

matchMedia

我們還想提及 媒體查詢字符串"> Window.matchMedia 此時(shí)的JavaScript功能。 這是一個(gè)屬性,允許您在JavaScript中運(yùn)行媒體查詢測(cè)試。 它看起來(lái)像這樣:

if (window.matchMedia("(max-width: 480px)").matches) {
  // run JavaScript in here. 
}

例如,我們的 Snapshot 演示使用它來(lái)選擇性地應(yīng)用Brick JavaScript庫(kù)并使用它處理 UI布局,但只適用于小屏幕布局(480像素寬或更小)。 如果頁(yè)面寬度為480像素或更小,我們首先使用 media 屬性僅將Brick CSS應(yīng)用于頁(yè)面:

<link href="dist/brick.css" type="text/css" rel="stylesheet" media="all and (max-width: 480px)">

然后,我們?cè)贘avaScript中使用 matchMedia()幾次,以便只在小屏幕布局上運(yùn)行Brick導(dǎo)航功能(在更寬的屏幕布局中,一切都可以一次看到, 需要在不同視圖之間導(dǎo)航)。

if (window.matchMedia("(max-width: 480px)").matches) {
  deck.shuffleTo(1);  
}

使用Modernizr實(shí)現(xiàn)功能檢測(cè)

可以使用類似上面詳述的技術(shù)來(lái)實(shí)現(xiàn)自己的特征檢測(cè)測(cè)試。 你可以使用一個(gè)專用的特征檢測(cè)庫(kù),因?yàn)樗故虑樽兊酶菀住?所有功能檢測(cè)庫(kù)的母親是 Modernizr ,它可以檢測(cè)您所需要的一切。 讓我們來(lái)看看如何使用它。

當(dāng)你使用Modernizr進(jìn)行實(shí)驗(yàn)時(shí),你可以使用開(kāi)發(fā)版本,包括每一個(gè)可能的特性檢測(cè)測(cè)試。 立即下載:

  1. Clicking on the Development build link.
  2. Clicking the big pink Build button in the page that comes up.
  3. Clicking the top Download link in the dialog box that appears.

將它保存在某個(gè)明智的地方,就像您在本文中創(chuàng)建其他示例的目錄一樣。

在生產(chǎn)環(huán)境中使用Modernizr時(shí),您可以轉(zhuǎn)到您已訪問(wèn)過(guò)的下載頁(yè)面,然后點(diǎn)擊加號(hào)按鈕 僅用于需要功能檢測(cè)的功能。 然后,當(dāng)您點(diǎn)擊生成按鈕時(shí),您將下載一個(gè)僅包含這些功能檢測(cè)的自定義版本,使文件大小更小。

CSS

讓我們看看Modernizr如何工作在選擇性地應(yīng)用CSS。

  1. First, make a copy of supports-feature-detect.html and supports-styling.css. Save them as modernizr-css.html and modernizr-css.css.
  2. Update your <link> element in your HTML so it points to the correct CSS file (you should also update you <title> element to something more suitable!):
    <link href="modernizr-css.css" rel="stylesheet">
  3. Above this <link> element, add a <script> element to apply the Modernizr library to the page, as shown below. This needs to be applied to the page before any CSS (or JavaScript) that might make use of it.
    <script src="modernizr-custom.js"></script>
  4. Now edit your opening <html> tag, so that it looks like this:
    <html class="no-js">

此時(shí),嘗試加載您的頁(yè)面,您將了解Modernizr如何工作的CSS功能。 如果您查看瀏覽器開(kāi)發(fā)人員工具的DOM檢查器,您會(huì)看到Modernizr已更新您的< html> 類值如下:

<html class="js no-htmlimports sizes flash transferables applicationcache blobconstructor
blob-constructor cookies cors ...AND LOADS MORE VALUES!>

它現(xiàn)在包含大量的類,指示不同技術(shù)功能的支持狀態(tài)。 作為示例,如果瀏覽器根本不支持flexbox,則< html> 將被給予類名稱 no-flexbox 。 如果它支持現(xiàn)代flexbox,它會(huì)得到一個(gè)類名稱 flexbox 。 如果您搜索類列表,您還會(huì)看到與flexbox相關(guān)的其他內(nèi)容,例如:

  • flexboxlegacy for the old flexbox spec (2009).
  • flexboxtweener for the 2011 inbetween syntax supported by IE10
  • flexwrap for the flex-wrap property, which isn't present in some implementations.

注意:您可以找到所有類名的含義的列表 - 請(qǐng)參閱 Modernizr檢測(cè)到的功能 。

繼續(xù),讓我們更新我們的CSS使用Modernizr,而不是 @supports 。 進(jìn)入 modernizr-css.css ,并用以下代碼替換兩個(gè) @supports 塊:

/* Properties for browsers with modern flexbox */

.flexbox main {
  display: flex;
}

.flexbox main div {
  padding-right: 4%;
  flex: 1;
}

.flexbox main div:last-child {
  padding-right: 0;
}

/* Fallbacks for browsers that don't support modern flexbox */

.no-flexbox main div {
  width: 22%;
  float: left;
  padding-right: 4%;
}

.no-flexbox main div:last-child {
  padding-right: 0;
}

.no-flexbox footer {
  clear: left;
}

那么這是如何工作的? 因?yàn)樗羞@些類名已放在< html> 元素中,所以您可以使用特定的后代選擇器來(lái)定向支持或不支持某個(gè)功能的瀏覽器。 因此,這里我們只將頂層規(guī)則應(yīng)用于支持flexbox的瀏覽器,而將底層規(guī)則僅應(yīng)用于不包含( no-flexbox )的瀏覽器。

注意:請(qǐng)記住,Modernizr的所有HTML和JavaScript功能測(cè)試也會(huì)在這些類名稱中報(bào)告,因此您可以根據(jù)瀏覽器是否支持HTML或JavaScript功能(如果需要)選擇性地應(yīng)用CSS。 。

JavaScript

Modernizr也同樣準(zhǔn)備好實(shí)現(xiàn)JavaScript功能檢測(cè)。 它通過(guò)使全局 Modernizr 對(duì)象可用于其應(yīng)用的頁(yè)面來(lái)實(shí)現(xiàn),其包含特征的檢測(cè)結(jié)果檢測(cè)為 true / false 屬性。

例如,加載我們的 html"class ="external"> modernizr-css.html 示例,然后嘗試轉(zhuǎn)到您的JavaScript控制臺(tái)并在 Modernizr。中鍵入其中一些 類名(它們?cè)谶@里也是相同的)。 例如:

Modernizr.flexbox
Modernizr.websqldatabase
Modernizr.xhr2
Modernizr.fetch

控制臺(tái)將返回 true / false 值,以指示您的瀏覽器是否支持這些功能。

讓我們來(lái)看一個(gè)例子來(lái)展示如何使用這些屬性。

  1. First of all, make a local copy of the modernizr-js.html example file.
  2. Attach the Modernizr library to the HTML using a <script> element, as we have done in previous demos. Put it above the existing <script> element, which is attaching the Google Maps API to the page.
  3. Next, fill in the YOUR-API-KEY placeholder text in the second <script> element (as it is now) with a valid Google Maps API key. To get a key, sign in to a Google account, go to the Get a Key/Authentication page, then click the blue Get a Key button and follow the instructions.
  4. Finally, add another <script> element at the bottom of the HTML body (just before the </body> tag), and put the following script inside the tags:
    if(Modernizr.geolocation) {
    
      navigator.geolocation.getCurrentPosition(function(position) {
    
        var latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
        var myOptions = {
          zoom: 8,
          center: latlng,
          mapTypeId: google.maps.MapTypeId.TERRAIN,
          disableDefaultUI: true
        }
        var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
      });
    
    } else {
      var para = document.createElement('p');
      para.textContent = 'Argh, no geolocation!';
      document.body.appendChild(para);
    }

試試你的例子! 這里我們使用 Modernizr.geolocation 測(cè)試來(lái)檢查當(dāng)前瀏覽器是否支持地理位置。 如果是,我們會(huì)運(yùn)行一些代碼,以獲取您設(shè)備的當(dāng)前位置,并將其繪制在Google地圖上。

概要

本文涵蓋了在一個(gè)合理的細(xì)節(jié),特征檢測(cè),通過(guò)主要概念,并顯示如何實(shí)現(xiàn)自己的特性檢測(cè)測(cè)試和使用Modernizr庫(kù)更容易實(shí)現(xiàn)測(cè)試。

接下來(lái),我們將開(kāi)始關(guān)注自動(dòng)化測(cè)試。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)