First do it. Then do it right. Then do it better.
What's a polyfill? Let's take a look at some.
<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!
Thanks to @mathias and @jdalton for this list
More on these very soon!
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 }
function isAPISupported(api, source){ //return (api in source) or.. return !!source[api]; }; // testing with google chrome isAPISupported('geolocation', navigator); // true isAPISupported('pushState', history); // true isAPISupported('localStorage', window); // true isAPISupported('sessionStorage', window); // true
Note: If a third-party library is extending host objects, 'a' in b or b.a testing may provide unreliable results. Keep in mind other scripts on the page!
function isPropSupported(prop){ var el = document.createElement('div'); return prop in el.style; }; isPropSupported('borderRadius'); // true isPropSupported('boxShadow'); // true isPropSupported('textShadow'); // true
function isSelectorSupported(sel){ var el = document.createElement('div'); el.innerHTML = '<style>'+ sel + '{}</style>'; document.body.appendChild(el); return !!el.lastChild.sheet.cssRules[0]; }; isSelectorSupported('::first-child'); // true isSelectorSupported('::after'); // true isSelectorSupported('::before'); // true isSelectorSupported(':nth-child(even)'); // true
function isAttribSupported(prop, el){ var el = document.createElement(el); return prop in el; } // Some simple HTML5 feature-detection tests isAttribSupported('placeholder', 'input'); // true isAttribSupported('play', 'video'); // true isAttribSupported('pause', 'audio'); // true isAttribSupported('getContext', 'canvas'); // true
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
The HTML5 Placeholder attribute is used as follows:
<input type="text" placeholder="Please enter some text"/>
Begin by iterating over all the input elements with a placeholder attribute
$("input[placeholder]").each(function() {
// more logic to come!
});
Get the value of the placeholder attribute, remove default placeholder:
$("input[placeholder]").each(function() { var $e = $(this), placeholder = $e.attr("placeholder"); $e.removeAttr("placeholder").val(placeholder); // A little more left to go });
Polyfill the placeholder text behavior to be cross-browser:
$("input[placeholder]").each(function() { var $e = $(this), placeholder = $e.attr("placeholder"); $e.removeAttr("placeholder").val(placeholder); $e.bind("focus blur", function(e) { if (e.type === "focus" && $e.val() === placeholder) { $e.val(""); } else { if (!$e.val()) { $e.val(placeholder); } } }); });
Before you release your polyfill into the wild..
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.