by Stu Cox / @stucoxmedia
#McrFRED | 27th June 2013 | Manchester, UK
- com•pat•i•bil•i•ty
|kəmˌpatɪˈbɪlɪti| (abbr.: compat.) noun (pl. -i•ties)- a state in which two things are able to exist or occur together without problems or conflict.
In this case:
.btn {
*margin-left: -13px;
}
if (navigator.userAgent.match(/MSIE [67]\./)) {
// Fix for old IE
}
Three sources of compatibility problems:
@font-face
, transitions, animations, flexbox, ...
<audio>
, <video>
, input types, drag & drop, ...
History API, IndexedDB, WebSockets, ...
Flash, Silverlight, Java, ...
PDF, Office documents, ...
Box model, double margin, ...
History API in Android 2.x, ...
"Browser X has the ability to render SVG"
"A browser with the Flash plugin has the ability to render Flash media"
"Browser Y has the ability NOT to fuck up the box model"
“The differences between 2 users' browsers can be described (entirely) by the differences between their capability sets.”
It was going to get mentioned sooner or later.
The core is smaller than you think.
Fewer core capabilities = accessible to more users.
If a user has the required capabilities, they get the enhancement; otherwise they don't.
.module {
box-sizing: border-box;
padding: 1em;
width: 20em;
}
If box-sizing
not supported, layout won't be as expected.
// My module
var modules = document.querySelectorAll('.module');
...
TypeError: Object #<HTMLDocument> has no method 'querySelectorAll'
if (supportsFeature) {
// Use feature!
// Ensure all code depending on this feature is
// contained here... no side effects!
}
else {
// Some fallback (optional)
}
'geolocation' in navigator
var el = createElement('div');
el.style.cssText = 'filter:blur(2px)';
!!el.style.length // true if CSS filters supported
var image = new Image();
image.onload = function() {
image.width == 1 // true if WebP supported
}
image.src = 'data:image/webp;base64,UklGRiwAAABXRUJQVlA4I...';
Yep, they're a kind of feature detection too.
@media (min-width: 800px) {
// Has a large viewport
}
@media not (min-width: 800px) {
// Doesn't have a large viewport
}
if (window.matchMedia('(min-width: 800px)').matches) {
// Has a large viewport
}
else {
// Doesn't have a large viewport
}
Via @supports
@supports (display: flex) {
// Supports flexbox
}
@supports not (display: flex) {
// Doesn't support flexbox
}
if (window.CSS.supports('display', 'flex') {
// Supports flexbox
}
else {
// Doesn't support flexbox
}
Efficient, easy to maintain.
Can be network and processor intensive, rarely an exact match
Usually unnecessary…
Detects for 174+ modern browser capabilities.
It makes feature detection a breeze
if (Modernizr.geolocation) {
// Use feature!
// Ensure all code depending on this feature is
// contained here... no side effects!
}
else {
// Some fallback (optional)
}
.geolocation .module {
/* Styles if geolocation supported */
}
.no-geolocation .module {
/* Styles if geolocation not supported */
}
All killer, no filler
Via Modernizr.addTest()
Modernizr.addTest('yoda', function () {
var yoda = document.createElement('yoda');
return 'theforce' in yoda;
});
Via Modernizr.load()
Avoid heavy loading for browsers which can't use it
Modernizr.load({
test: Modernizr.geolocation,
yep: 'geo.js',
nope: 'geo-polyfill.js'
});
Rarely need nope
– big polyfills are a bad idea!
@supports
under the hoodComing soon, we promise!
Now I'm going to talk a bit about my involvement in Modernizr.
Unfortunately some things fall under our radar
Realistically, most detects make some assumptions – we try to minimise these
Because that's all we can access from JS.
Can't access the pixels on the screen
Best-guess based on the (re)actions of the DOM
Appear on top of the document – invisible to us
Can't tell if events (e.g. DOMContentLoaded
) will be fired at the correct time
contenteditable
We can often give accurate negatives, but not positives
e.g. window.performance
– tells us nothing about older devices
Don't get me started...
Not reliably, anyway
All techniques either use heuristics or rely on new APIs.
Not reliably, anyway
Now I'm going to talk a bit about some hand-wavey idealistic stuff.
Consider it from the start of your project.
RequireJS-like syntax for defining browser dependencies
browserRequire(['svg', 'canvas'], function () {
// Only runs if capabilities available
});
require(['jquery', 'M!svg', 'M!canvas'], function ($) {
// Only runs if software AND capability dependencies
// satisfied
});
That's all I've got.