First do it. Then do it right. Then do it better.
<canvas>
in IE7 & 8 with FlashCanvas (Demo)border-radius
,box-shadow
and gradients in IE7Imagine if we could polyfill things like..
oldIE: Internet Explorer 6, 7 & 8. aka, the three browsers often getting the low-res experience.
New features will never be available in oldIE.
Bleeding-edge features aren't available in all modern browsers at the same time.
Polyfills are a type of shim that retrofit legacy browsers with modern HTML5/CSS3 features
Shims refer to any code that intercepts API calls and provides a layer of abstraction
Polyfills help us use today's modern features in yesterday's browsers
html
tag based on browser capabilities<html class="no-cssgradients boxshadow no-cssanimations ...">
.no-cssgradients .shinybutton { background: url("shinybutton.jpg"); } .cssgradients .shinybutton { background-image: linear-gradient(top, #555, #333); }
if (Modernizr.webgl){ loadAllWebGLScripts(); // webgl assets can easily be > 300k } else { var msg = 'WebGL is not available in your browser'; document.getElementById( '#notice' ).innerHTML = msg; }
We'll look at more Modernizr feature detection tests later.
WebAudio API
Modernizr.addTest('webaudio', !!(window.webkitAudioContext || window.AudioContext));
Track API
Modernizr.addTest('trackapi', typeof document.createElement('video').addTextTrack === 'function');
<!DOCTYPE html> <html> <head> <style>section { border:1px solid red; display:block}</style> <script>document.createElement('section');</script> </head> <body> <section> <p>Hello FITC!</p> </section> ...
We can now style a HTML5 element in IE6 and above
<section>
instead of <div>
in older browsers<section> <p>I can now be styled in all browsers!</p> </section>
Remember, polyfills still needed for other features
There are some great script loaders that can help with conditional loading
Example: conditionally load a geolocation polyfill and stylesheet
yepnope({ test: Modernizr.geolocation, yep: 'regular-styles.css', nope: ['modified-styles.css', 'geolocation-polyfill.js'], callback: function (url, result, key) { if (url === 'modified-styles.css') { alert("woohoo! it's loaded"); } } });
Supports similarly loading up a geolocation polyfill depending on support
Modernizr.load({ test: Modernizr.geolocation, yep : 'geo.js', nope: 'geo-polyfill.js' });
Example: conditionally load a JSON polyfill if it isn't natively supported
$LAB.script(function(){ if (typeof JSON == "undefined") return "json2.js"; }) .wait() .script("myotherscript.js");
An alternative:
$LAB.script(typeof JSON == "undefined" ? "json2.js" : false).wait() .script("myotherscript.js");
Example: conditionally load a geolocation polyfill and stylesheet
yepnope({ test: Modernizr.geolocation, yep: 'regular-styles.css', nope: ['modified-styles.css', 'geolocation-polyfill.js'], callback: function (url, result, key) { if (url === 'modified-styles.css') { alert('The Styles loaded!'); } } });
Why should you give writing polyfills a go?
You can't detect 'HTML5 Support', but you can detect support for individual features
There are some useful tips to keep in mind
window
and document
are your friendwindow //explore me!
Detecting finalized and unfinalized features. Test if:
window
or navigator
)
Testing for geolocation
support
function isGeolocationSupported(){ return !!navigator.geolocation; }
and with Modernizr it's as simple as..
if(Modernizr.geolocation){ // supported }else{ // not supported }
Testing for <canvas>
support
function isCanvasSupported(){ return !!document.createElement('canvas').getContext; }
and with Modernizr its..
if(Modernizr.canvas){ // supported }else{ // not supported }
Testing for <audio>
support
function isAudioSupported(){ return !!document.createElement('audio').canPlayType; }
and with Modernizr its..
if(Modernizr.audio){ // supported }else{ // not supported }
Testing for <audio>
format support
if(isAudioSupported()){ var audio = document.createElement('audio'); if(audio.canPlayType('audio/mpeg')=='probably'){ //supports MP3 audio audio.src = 'music.mp3'; } else if(audio.canPlayType('video/ogg; codecs="theora"')=='probably'){ //supports Ogg/Vorbis audio audio.src = 'music.ogg'; } }else{ //load a flash fallback }
and with Modernizr its..
if(Modernizr.audio){ var audio = new Audio(); //If Ogg is supported, load 'music.ogg' //Otherwise the MP3 or M4A fallback depending //on browser support audio.src = Modernizr.audio.ogg ? 'music.ogg' : Modernizr.audio.mp3 ? 'music.mp3' : 'music.m4a'; }else{ //use a flash fallback }
Testing for <input type="color">
support
function isColorPickerSupported(){ var input = document.createElement('input'); input.setAttribute('type','color'); return input.type !== 'text'; }
and with Modernizr its..
if(Modernizr.inputtypes.color){ // supported }else{ // not supported }
Allow vendors to implement experimental features before they've been finalized
// From css3please.com: .box_transition { -webkit-transition: all 0.3s ease-out; /* Saf3.2+, Chrome */ -moz-transition: all 0.3s ease-out; /* FF4+ */ -ms-transition: all 0.3s ease-out; /* IE10? */ -o-transition: all 0.3s ease-out; /* Opera 10.5+ */ transition: all 0.3s ease-out; /*fast-forward compatible*/ }
Edge-features occasionally need to be tested prepending a vendor prefix to the feature name.
function getPrefix(prop){ var prefixes = ['Moz','Khtml','Webkit','O','ms'], elem = document.createElement('div'), upper = prop.charAt(0).toUpperCase() + prop.slice(1); if (prop in elem.style) return prop; for (var len = prefixes.length; len--; ){ if ((prefixes[len] + upper) in elem.style) return (prefixes[len] + upper); } return false; } console.log(getPrefix('transform'));//WebkitTransform
console.log(Modernizr.prefixed('transform'));//WebkitTransform
As browser vendors implement new specs and features, the need for specific polyfills will decrease.
What did we learn today?
For more on me:
Big thanks to @paul_irish, @mathias, @peol, @rem and others for their previous work in this area and technical reviews.