Implementing feature detection

Feature detection involves working out whether a browser supports a certain block of code, and running different code depending on whether it does (or doesn't), so that the browser can always provide a working experience rather than crashing/erroring in some browsers. This article details how to write your own simple feature detection, how to use a library to speed up implementation, and native features for feature detection such as @supports .

Prerequisites: Familiarity with the core HTML , CSS ,和 JavaScript languages; an idea of the high-level principles of cross-browser testing .
Objective: To understand what the concept of feature detection is, and be able to implement suitable solutions in CSS and JavaScript.

The concept of feature detection

The idea behind feature detection is that you can run a test to determine whether a feature is supported in the current browser, and then conditionally run code to provide an acceptable experience both in browsers that do support the feature, and browsers that don't . If you don't do this, browsers that don't support the features you are using in your code won't display your sites properly and will just fail, creating a bad user experience.

Let's recap and look at the example we touched on in our Handling common JavaScript problems — the 地理位置 API (which exposes available location data for the device the web browser is running on) has the main entry point for its use, a geolocation property available on the global Navigator object. Therefore, you can detect whether the browser supports geolocation or not by using something like the following:

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
}

						

It is probably better to use an established feature detection library however, rather than writing your own all the time. Modernizr is the industry standard for feature detection tests, and we'll look at that later on.

Before we move on, we'd like to say one thing upfront — don't confuse feature detection with browser sniffing (detecting what specific browser is accessing the site) — this is a terrible practice that should be discouraged at all costs. See Using bad browser sniffing code 了解更多细节。

Writing your own feature detection tests

In this section, we'll look at implementing your own feature detection tests, in both CSS and JavaScript.

CSS

You can write tests for CSS features by testing for the existence of element.style.property (如 paragraph.style.transform ) in JavaScript.

A classic example might be to test for Flexbox support in a browser; for browsers that support the newest Flexbox spec, we could use a flexible and robust flex layout. For browsers that don't, we could use a floated layout that works OK, although it is slightly more brittle and hacky, and not as cool-looking.

Let's implement something that demonstrates this, although we'll keep it simple for now.

  1. Start by making local copies of our css-feature-detect.html , flex-layout.css , float-layout.css ,和 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> 元素:
    <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 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> 元素的 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:
    const conditional = document.querySelector('.conditional');
    const 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');
    }
    
    								

Here we are grabbing a reference to the second <link> element, and creating a <div> element as part of our test. In our conditional statement, we test that the flex and flex-flow properties exist in the browser. Note how the JavaScript representations of those properties that are stored inside the HTMLElement.style object use lower camel case, not hyphens, to separate the words.

注意: If you have trouble getting this to work, you can compare it to our css-feature-detect-finished.html code (see also the live version ).

When you save everything and try out your example, you should see the flexbox layout applied to the page if the browser supports modern flexbox, and the float layout if not.

注意: Often such an approach is overkill for a minor feature detection problem — you can often get away with using multiple vendor prefixes and fallback properties, as described in CSS fallback behavior and Handling CSS prefixes .

@supports

In recent times, CSS has had its own native feature detection mechanism introduced — the @supports at-rule. This works in a similar manner to media queries (see also Responsive design problems ) — except that instead of selectively applying CSS depending on a media feature like a resolution, screen width or aspect ratio, it selectively applies CSS depending on whether a CSS feature is supported.

For example, we could rewrite our previous example to use @supports — see supports-feature-detect.html and supports-styling.css . If you look at the latter, you'll see a couple of @supports blocks, for example:

@supports (flex-flow: row) and (flex: 1) {
  main {
    display: flex;
  }
  main div {
    padding-right: 4%;
    flex: 1;
  }
  main div:last-child {
    padding-right: 0;
  }
}

						

This at-rule block applies the CSS rule within only if the current browser supports both the flex-flow: row and flex: 1 declarations. For each condition to work, you need to include a complete declaration (not just a property name) and NOT include the semi-colon on the end.

@supports also has OR and NOT logic available — the other block applies the float layout if the flexbox properties are not available:

@supports not (flex-flow: row) and (flex: 1) {
  /* rules in here */
}

						

This may look a lot more convenient than the previous example — we can do all of our feature detection in CSS, no JavaScript required, and we can handle all the logic in a single CSS file, cutting down on HTTP requests. the problem here is browser support — @supports is not supported at all in IE, and only supported in very recent versions of Safari/iOS WebKit (9+/9.2+), whereas the JavaScript version should work in much older browsers (probably back to IE8 or 9, although older versions of IE will have additional problems, such as not supporting Document.querySelector , and having a messed up box model).

JavaScript

We already saw an example of a JavaScript feature detection test earlier on. Generally, such tests are done via one of the following common patterns:

Summary of JavaScript feature detection techniques
Feature detection type 解释 范例
If member in object Check whether a certain method or property (typically an entry point into using the API or other feature you are detecting for) exists in its parent Object. if("geolocation" in navigator) { ... }
Property on element 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 支持。 function supports_canvas() {
return !!document.createElement('canvas').getContext;
}

if(supports_canvas()) { ... }
Method on element return value 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. Dive Into HTML5 Video Formats detection test.
Property on element retains value Create an element in memory using Document.createElement() , set a property to a certain value, then check to see if the value is retained. Dive into HTML5 <input> types detection test.

注意: The double NOT in the above example ( !! ) is a way to force a return value to become a "proper" boolean value, rather than a Truthy / Falsy value that may skew the results.

Dive into HTML5 Detecting HTML5 Features page has a lot more useful feature detection tests besides the ones listed above, and you can generally find a feature detection test for most things by searching for "detect support for YOUR-FEATURE-HERE" in your favorite search engine. Bear in mind though that some features, however, are known to be undetectable — see Modernizr's list of Undetectables .

matchMedia

We also wanted to mention the Window.matchMedia JavaScript feature at this point too. This is a property that allows you to run media query tests inside JavaScript. It looks like this:

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

						

As an example, our Snapshot demo makes use of it to selectively apply the Brick JavaScript library and use it to handle the UI layout, but only for the small screen layout (480px wide or less). We first use the media attribute to only apply the Brick CSS to the page if the page width is 480px or less:

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

						

We then use matchMedia() in the JavaScript several times, to only run Brick navigation functions if we are on the small screen layout (in wider screen layouts, everything can be seen at once, so we don't need to navigate between different views).

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

						

Using Modernizr to implement feature detection

It is possible to implement your own feature detection tests using techniques like the ones detailed above. You might as well use a dedicated feature detection library however, as it makes things much easier. The mother of all feature detection libraries is Modernizr , and it can detect just about everything you'll ever need. Let's look at how to use it now.

When you are experimenting with Modernizr you might as well use the development build, which includes every possible feature detection test. Download this now by:

  1. 点击 Development build link.
  2. Clicking the big pink 构建 button on the page that comes up.
  3. Clicking the top 下载 link in the dialog box that appears.

Save it somewhere sensible, like the directory you've been creating your other examples for in this article.

When you are using Modernizr in production, you can go to the Download page you've already visited and click the plus buttons for only the features you need feature detects for. Then when you click the 构建 button, you'll download a custom build containing only those feature detects, making for a much smaller file size.

CSS

Let's have a look at how Modernizr works in terms of selectively applying 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 your <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">
    
    								

At this point, try loading your page, and you'll get an idea of how Modernizr works for CSS features. If you look at the DOM inspector of your browser's developer tools, you'll see that Modernizr has updated your <html> class value like so:

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

						

It now contains a large number of classes that indicate the support status of different technology features. As an example, if the browser didn't support flexbox at all, <html> would be given a class name of no-flexbox . If it did support modern flexbox, it would get a class name of flexbox . If you search through the class list, you'll also see others relating to flexbox, like:

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

注意: You can find a list of what all the class names mean — see Features detected by Modernizr .

Moving on, let's update our CSS to use Modernizr rather than @supports . Go into modernizr-css.css , and replace the two @supports blocks with the following:

/* 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;
}

						

So how does this work? Because all those class names have been put on the <html> element, you can target browsers that do or don't support a feature using specific descendant selectors. So here we're applying the top set of rules only to browsers that do support flexbox, and the bottom set of rules only to browsers that don't ( no-flexbox ).

注意: Bear in mind that all of Modernizr's HTML and JavaScript feature tests are also reported in these class names, so you can quite happily apply CSS selectively based on whether the browser supports HTML or JavaScript features, if needed.

注意: If you have trouble getting this to work, check your code against our modernizr-css.html and modernizr-css.css files (see this running live also).

JavaScript

Modernizr is also equally well-prepared for implementing JavaScript feature detects too. It does this by making the global Modernizr object available to the page it is applied to, which contains results of the feature detects as true / false 特性。

For example, load up our modernizr-css.html example in your browser, then try going to your JavaScript console and typing in Modernizr. followed by some of those class names (they are the same here too). For example:

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

The console will return true / false values to indicate whether your browser supports those features or not.

Let's look at an example to show how you'd use those properties.

  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) {
        let latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
        let myOptions = {
          zoom: 8,
          center: latlng,
          mapTypeId: google.maps.MapTypeId.TERRAIN,
          disableDefaultUI: true
        }
        let map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
      });
    } else {
      const para = document.createElement('p');
      para.textContent = 'Argh, no geolocation!';
      document.body.appendChild(para);
    }
    
    								

Try your example out! Here we use the Modernizr.geolocation test to check whether geolocation is supported by the current browser. If it is, we run some code that gets your device's current location, and plots it on a Google Map.

摘要

This article covered feature detection in a reasonable amount of detail, going through the main concepts and showing you how to both implement your own feature detection tests and use the Modernizr library to implement tests more easily.

Next up, we'll start looking at automated testing.

In this module

发现此页面有问题吗?

最后修改: , 由 MDN 贡献者

  1. Complete beginners start here!
  2. Web 快速入门
    1. Getting started with the Web overview
    2. 安装基本软件
    3. What will your website look like?
    4. 处理文件
    5. HTML 基础
    6. CSS 基础
    7. JavaScript 基础
    8. 发布您的网站
    9. How the Web works
  3. HTML — Structuring the Web
  4. HTML 介绍
    1. Introduction to HTML overview
    2. Getting started with HTML
    3. What's in the head? Metadata in HTML
    4. HTML text fundamentals
    5. Creating hyperlinks
    6. Advanced text formatting
    7. Document and website structure
    8. Debugging HTML
    9. Assessment: Marking up a letter
    10. Assessment: Structuring a page of content
  5. 多媒体和嵌入
    1. Multimedia and embedding overview
    2. Images in HTML
    3. Video and audio content
    4. From object to iframe — other embedding technologies
    5. Adding vector graphics to the Web
    6. Responsive images
    7. Assessment: Mozilla splash page
  6. HTML 表格
    1. HTML tables overview
    2. HTML table basics
    3. HTML Table advanced features and accessibility
    4. Assessment: Structuring planet data
  7. CSS — Styling the Web
  8. CSS 第一步
    1. CSS first steps overview
    2. What is CSS?
    3. Getting started with CSS
    4. How CSS is structured
    5. How CSS works
    6. Using your new knowledge
  9. CSS 构建块
    1. CSS building blocks overview
    2. Cascade and inheritance
    3. CSS 选择器
    4. The box model
    5. Backgrounds and borders
    6. Handling different text directions
    7. Overflowing content
    8. Values and units
    9. Sizing items in CSS
    10. Images, media, and form elements
    11. Styling tables
    12. Debugging CSS
    13. Organizing your CSS
  10. 样式化文本
    1. Styling text overview
    2. Fundamental text and font styling
    3. Styling lists
    4. Styling links
    5. Web fonts
    6. Assessment: Typesetting a community school homepage
  11. CSS 布局
    1. CSS layout overview
    2. Introduction to CSS layout
    3. Normal Flow
    4. Flexbox
    5. Grids
    6. Floats
    7. 位置
    8. Multiple-column Layout
    9. Responsive design
    10. Beginner's guide to media queries
    11. Legacy Layout Methods
    12. Supporting Older Browsers
    13. Fundamental Layout Comprehension
  12. JavaScript — Dynamic client-side scripting
  13. JavaScript 第一步
    1. JavaScript first steps overview
    2. What is JavaScript?
    3. A first splash into JavaScript
    4. What went wrong? Troubleshooting JavaScript
    5. Storing the information you need — Variables
    6. Basic math in JavaScript — Numbers and operators
    7. Handling text — Strings in JavaScript
    8. Useful string methods
    9. 数组
    10. Assessment: Silly story generator
  14. JavaScript 构建块
    1. JavaScript building blocks overview
    2. Making decisions in your code — Conditionals
    3. Looping code
    4. Functions — Reusable blocks of code
    5. Build your own function
    6. Function return values
    7. 事件介绍
    8. Assessment: Image gallery
  15. 引入 JavaScript 对象
    1. Introducing JavaScript objects overview
    2. Object basics
    3. 对象原型
    4. Object-oriented programming concepts
    5. Classes in JavaScript
    6. Working with JSON data
    7. Object building practice
    8. Assessment: Adding features to our bouncing balls demo
  16. 异步 JavaScript
    1. Asynchronous JavaScript overview
    2. General asynchronous programming concepts
    3. Introducing asynchronous JavaScript
    4. Cooperative asynchronous Java​Script: Timeouts and intervals
    5. Graceful asynchronous programming with Promises
    6. Making asynchronous programming easier with async and await
    7. Choosing the right approach
  17. 客户端侧 Web API
    1. 客户端侧 Web API
    2. Introduction to web APIs
    3. Manipulating documents
    4. Fetching data from the server
    5. Third party APIs
    6. Drawing graphics
    7. Video and audio APIs
    8. Client-side storage
  18. Web forms — Working with user data
  19. Core forms learning pathway
    1. Web forms overview
    2. Your first form
    3. How to structure a web form
    4. Basic native form controls
    5. The HTML5 input types
    6. Other form controls
    7. Styling web forms
    8. Advanced form styling
    9. UI pseudo-classes
    10. Client-side form validation
    11. Sending form data
  20. Advanced forms articles
    1. How to build custom form controls
    2. Sending forms through JavaScript
    3. CSS property compatibility table for form controls
  21. Accessibility — Make the web usable by everyone
  22. Accessibility guides
    1. Accessibility overview
    2. What is accessibility?
    3. HTML: A good basis for accessibility
    4. CSS and JavaScript accessibility best practices
    5. WAI-ARIA basics
    6. Accessible multimedia
    7. Mobile accessibility
  23. Accessibility assessment
    1. Assessment: Accessibility troubleshooting
  24. Tools and testing
  25. Client-side web development tools
    1. Client-side web development tools index
    2. Client-side tooling overview
    3. Command line crash course
    4. Package management basics
    5. Introducing a complete toolchain
    6. Deploying our app
  26. Introduction to client-side frameworks
    1. Client-side frameworks overview
    2. Framework main features
  27. React
    1. Getting started with React
    2. Beginning our React todo list
    3. Componentizing our React app
    4. React interactivity: Events and state
    5. React interactivity: Editing, filtering, conditional rendering
    6. Accessibility in React
    7. React resources
  28. Ember
    1. Getting started with Ember
    2. Ember app structure and componentization
    3. Ember interactivity: Events, classes and state
    4. Ember Interactivity: Footer functionality, conditional rendering
    5. Routing in Ember
    6. Ember resources and troubleshooting
  29. Vue
    1. Getting started with Vue
    2. Creating our first Vue component
    3. Rendering a list of Vue components
    4. Adding a new todo form: Vue events, methods, and models
    5. Styling Vue components with CSS
    6. Using Vue computed properties
    7. Vue conditional rendering: editing existing todos
    8. Focus management with Vue refs
    9. Vue resources
  30. Svelte
    1. Getting started with Svelte
    2. Starting our Svelte Todo list app
    3. Dynamic behavior in Svelte: working with variables and props
    4. Componentizing our Svelte app
    5. Advanced Svelte: Reactivity, lifecycle, accessibility
    6. Working with Svelte stores
    7. TypeScript support in Svelte
    8. Deployment and next steps
  31. Angular
    1. Getting started with Angular
    2. Beginning our Angular todo list app
    3. Styling our Angular app
    4. Creating an item component
    5. Filtering our to-do items
    6. Building Angular applications and further resources
  32. Git and GitHub
    1. Git and GitHub overview
    2. Hello World
    3. Git Handbook
    4. Forking Projects
    5. About pull requests
    6. Mastering Issues
  33. Cross browser testing
    1. Cross browser testing overview
    2. Introduction to cross browser testing
    3. Strategies for carrying out testing
    4. Handling common HTML and CSS problems
    5. Handling common JavaScript problems
    6. Handling common accessibility problems
    7. Implementing feature detection
    8. Introduction to automated testing
    9. Setting up your own test automation environment
  34. Server-side website programming
  35. 第一步
    1. First steps overview
    2. Introduction to the server-side
    3. Client-Server overview
    4. Server-side web frameworks
    5. Website security
  36. Django Web 框架 (Python)
    1. Django web framework (Python) overview
    2. 介绍
    3. 设置开发环境
    4. Tutorial: The Local Library website
    5. Tutorial Part 2: Creating a skeleton website
    6. Tutorial Part 3: Using models
    7. Tutorial Part 4: Django admin site
    8. Tutorial Part 5: Creating our home page
    9. Tutorial Part 6: Generic list and detail views
    10. Tutorial Part 7: Sessions framework
    11. Tutorial Part 8: User authentication and permissions
    12. Tutorial Part 9: Working with forms
    13. Tutorial Part 10: Testing a Django web application
    14. Tutorial Part 11: Deploying Django to production
    15. Web application security
    16. Assessment: DIY mini blog
  37. Express Web Framework (node.js/JavaScript)
    1. Express Web Framework (Node.js/JavaScript) overview
    2. Express/Node introduction
    3. Setting up a Node (Express) development environment
    4. Express tutorial: The Local Library website
    5. Express Tutorial Part 2: Creating a skeleton website
    6. Express Tutorial Part 3: Using a database (with Mongoose)
    7. Express Tutorial Part 4: Routes and controllers
    8. Express Tutorial Part 5: Displaying library data
    9. Express Tutorial Part 6: Working with forms
    10. Express Tutorial Part 7: Deploying to production
  38. Further resources
  39. Common questions
    1. HTML questions
    2. CSS questions
    3. JavaScript questions
    4. Web mechanics
    5. Tools and setup
    6. Design and accessibility