codecamp

Implementing feature detection

先决条件: 熟悉核心 HTML CSS JavaScript 语言; 高级跨浏览器测试原则的概念。
目的: 理解特征检测的概念,并能够在CSS和JavaScript中实现合适的解决方案。

特征检测的概念

功能检测背后的理念是,您可以运行测试,以确定当前浏览器中是否支持某个功能,然后有条件地运行代码,以便在支持该功能的浏览器中提供可接受的体验。 不要的浏览器 如果不这样做,那么不支持您在代码中使用的功能的浏览器将无法正常显示您的网站,只会导致失败,从而造成糟糕的用户体验。

让我们回顾一下我们在处理常见JavaScript问题( org / zh-CN / docs / Web / API / Geolocation / Using_geolocation">地理位置API (暴露运行Web浏览器的设备的可用位置数据)具有其使用的主入口点, 代码>地理位置属性在全局导航器对象上可用。 因此,您可以通过使用类似以下内容来检测浏览器是否支持地理位置:

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
}

这可能是更好地使用已建立的特征检测库,而不是写你自己的所有时间。 Modernizr是特征检测测试的行业标准,稍后我们将介绍。

在我们继续之前,我们先说一说 - 不要将特征检测与浏览器嗅探(检测特定浏览器访问网站)混淆 - 这是一个可怕的做法,应该 不惜一切代价。 有关详情,请参见使用错误的浏览器嗅探代码

编写自己的特性检测测试

在本节中,我们将介绍如何在CSS和JavaScript中实现自己的特性检测测试。

CSS

您可以通过测试 element.style的存在性来编写CSS功能的测试。 属性 (例如 paragraph.style.transform )。

一个典型的示例可能是在浏览器中测试 Flexbox 支持; 对于支持最新Flexbox规范的浏览器,我们可以使用灵活和强大的flex布局。 对于没有的浏览器,我们可以使用一个浮动的布局工作正常,虽然它稍微更脆弱和hacky,而不是那么酷。

让我们实现一些演示这一点,虽然我们现在保持简单。

  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.\">这里我们抓住第二个< link> 元素的引用,并创建一个< div> 元素作为测试的一部分。在我们的条件语句中,我们测试flex\">Flex项目可以被拉伸以使用与它们的flex增长因子或它们的flex收缩因子成比例的可用空间以防止溢出。"> flex and >和flex-flow properties exist in the browser.\">-wrap individual properties。"> flex-flow 属性存在于浏览器中。请注意如何存储在HTMLElement.style\">返回一个CSSStyleDeclaration对象,它只表示元素的内联样式属性,忽略任何应用的样式规则。请参阅CSS属性参考以获取通过样式可访问的CSS属性列表"> HTMLElement.style object use lower camel case, not hyphens, to separate the words.\">>对象使用下骆驼的情况,不是连字符,来分隔词。

当您保存所有内容并尝试您的示例时,如果浏览器支持现代flexbox,则应该看到flexbox布局应用于页面,如果没有,则应该看到float布局。

注意:对于次要功能检测问题,这种方法通常是过度使用的 - 您可以使用多个供应商前缀和备用属性,如 #CSS_fallback_behaviour"> CSS后备行为处理CSS前缀

@supports

最近,CSS已经引入了自己的本机功能检测机制 - CSS规则将由大括号分隔的CSS块中的一组嵌套语句与由CSS声明的测试组成的条件(即属性值对,与任意连接,析取和否定 这样的条件称为支持条件。"> @supports at-rule。 此操作方式与媒体查询(另请参阅 webstart / Cross_browser_testing / HTML_and_CSS#Responsive_design_problems">响应式设计问题) - 除了根据媒体功能(如分辨率,屏幕宽度或宽高比)选择性应用CSS之外,它还会根据CSS功能 支持。

例如,我们可以重写我们前面的例子来使用 @supports - 参见 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> 。 如果你看看后者,你会看到一些 @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;
  }

}

只有当前浏览器同时支持 flex-flow:row flex:1 声明时,此规则块才应用CSS规则。 对于每个条件的工作,你需要包括一个完整的声明(不只是一个属性名称)和不包括分号在末尾。

@supports 也有 OR NOT 逻辑可用 - 如果flexbox属性不可用,其他块应用float布局:

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

  /* rules in here */

}

这可能看起来比以前的例子更方便 - 我们可以在CSS中执行所有的特性检测,不需要JavaScript,我们可以处理单个CSS文件中的所有逻辑,减少HTTP请求。 这里的问题是浏览器支持 - 在IE中不支持 @supports ,并且仅在最新版本的Safari / iOS WebKit(9 + / 9.2 +)中支持,而JavaScript版本应该工作 在较旧的浏览器(可能回到IE8或9,虽然旧版本的IE将有额外的问题,如不支持 / Document / querySelector"title ="返回文档中的第一个元素(使用文档标记中的第一个元素的深度优先预先遍历文档的节点,并按照子节点数量的顺序迭代顺序节点)匹配 指定的选择器组"> Document.querySelector ,并且具有混乱的框模型)。

JavaScript

我们早就已经看到了一个JavaScript特性检测测试的例子。 通常,这种测试通过以下通用模式之一来完成:

Summary of JavaScript feature detection techniques
特征检测类型 说明 例子
如果对象中的成员 检查某个方法或属性(通常是使用API或要检测的其他功能的入口点)是否存在于其父对象中。

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. 请参见深入HTML5视频格式检测测试。
属性属性保留值 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检测HTML5功能页面除了上面列出的功能外,还有更多有用的功能检测测试 ,您通常可以通过在您最喜欢的搜索引擎中搜索"检测对您的特性的支持"来查找大多数功能的功能检测测试。 请记住,某些功能被认为是无法检测的 - 请参阅Modernizr的 Undetectables 列表。

matchMedia

我们还想提及 媒体查询字符串"> Window.matchMedia 此时的JavaScript功能。 这是一个属性,允许您在JavaScript中运行媒体查询测试。 它看起来像这样:

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

例如,我们的 Snapshot 演示使用它来选择性地应用Brick JavaScript库并使用它处理 UI布局,但只适用于小屏幕布局(480像素宽或更小)。 如果页面宽度为480像素或更小,我们首先使用 media 属性仅将Brick CSS应用于页面:

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

然后,我们在JavaScript中使用 matchMedia()几次,以便只在小屏幕布局上运行Brick导航功能(在更宽的屏幕布局中,一切都可以一次看到, 需要在不同视图之间导航)。

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

使用Modernizr实现功能检测

可以使用类似上面详述的技术来实现自己的特征检测测试。 你可以使用一个专用的特征检测库,因为它使事情变得更容易。 所有功能检测库的母亲是 Modernizr ,它可以检测您所需要的一切。 让我们来看看如何使用它。

当你使用Modernizr进行实验时,你可以使用开发版本,包括每一个可能的特性检测测试。 立即下载:

  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.

将它保存在某个明智的地方,就像您在本文中创建其他示例的目录一样。

在生产环境中使用Modernizr时,您可以转到您已访问过的下载页面,然后点击加号按钮 仅用于需要功能检测的功能。 然后,当您点击生成按钮时,您将下载一个仅包含这些功能检测的自定义版本,使文件大小更小。

CSS

让我们看看Modernizr如何工作在选择性地应用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">

此时,尝试加载您的页面,您将了解Modernizr如何工作的CSS功能。 如果您查看浏览器开发人员工具的DOM检查器,您会看到Modernizr已更新您的< html> 类值如下:

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

它现在包含大量的类,指示不同技术功能的支持状态。 作为示例,如果浏览器根本不支持flexbox,则< html> 将被给予类名称 no-flexbox 。 如果它支持现代flexbox,它会得到一个类名称 flexbox 。 如果您搜索类列表,您还会看到与flexbox相关的其他内容,例如:

  • 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.

注意:您可以找到所有类名的含义的列表 - 请参阅 Modernizr检测到的功能

继续,让我们更新我们的CSS使用Modernizr,而不是 @supports 。 进入 modernizr-css.css ,并用以下代码替换两个 @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;
}

那么这是如何工作的? 因为所有这些类名已放在< html> 元素中,所以您可以使用特定的后代选择器来定向支持或不支持某个功能的浏览器。 因此,这里我们只将顶层规则应用于支持flexbox的浏览器,而将底层规则仅应用于不包含( no-flexbox )的浏览器。

注意:请记住,Modernizr的所有HTML和JavaScript功能测试也会在这些类名称中报告,因此您可以根据浏览器是否支持HTML或JavaScript功能(如果需要)选择性地应用CSS。 。

JavaScript

Modernizr也同样准备好实现JavaScript功能检测。 它通过使全局 Modernizr 对象可用于其应用的页面来实现,其包含特征的检测结果检测为 true / false 属性。

例如,加载我们的 html"class ="external"> modernizr-css.html 示例,然后尝试转到您的JavaScript控制台并在 Modernizr。中键入其中一些 类名(它们在这里也是相同的)。 例如:

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

控制台将返回 true / false 值,以指示您的浏览器是否支持这些功能。

让我们来看一个例子来展示如何使用这些属性。

  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 测试来检查当前浏览器是否支持地理位置。 如果是,我们会运行一些代码,以获取您设备的当前位置,并将其绘制在Google地图上。

概要

本文涵盖了在一个合理的细节,特征检测,通过主要概念,并显示如何实现自己的特性检测测试和使用Modernizr库更容易实现测试。

接下来,我们将开始关注自动化测试。

Handling common HTML and CSS problems
Introduction to automated testing
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录
CSS

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }