Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Polyfilling The HTML5 Gaps With JavaScript

Addy Osmani
December 14, 2011

Polyfilling The HTML5 Gaps With JavaScript

Note: (Better) HTML version of these slides are available are at http://addyosmani.com/blog/polyfilling-the-html5-gaps/

We all want to use the hot new features being implemented in modern browsers right away, but there's a small problem holding us back: how can we use these great new capabilities whilst ensuring older browsers still render pages and features as expected? Polyfills are here to help.

Polyfills are JavaScript shims that replicate the standard API found in native features of new browsers for those without such features and in this talk I'll take you through how to both use specially written JavaScript to emulate HTML5 features and even pick up pro-tips about writing your own polyfills along the way.

Addy Osmani

December 14, 2011
Tweet

More Decks by Addy Osmani

Other Decks in Programming

Transcript

  1. A little about me @addyosmani JavaScript & UI Developer @

    AOL jQuery [Bug Triage/Docs/Learning] teams Written polyfills for Page Visibility API, WebKit speech input and more. I ! making the web better " " " " "
  2. This is only the tip of the iceberg Imagine if

    we could polyfill things like.. In-browser speech detection (WebKit/Chrome) CSS Shaders (Chrome Canary) or even... ! ! !
  3. All of these features are now possible.. Media queries WebSockets

    Web Storage HTML5 video Web Workers Application cache WebGL and more!! but support is fragmented ! ! ! ! ! ! ! !
  4. oldIE: Internet Explorer 6, 7 & 8. aka, the three

    browsers often getting the low-res experience. — Paul Irish “ ”
  5. Our biggest issue - browser support New features will never

    be available in oldIE. Media queries - IE9, 10 WebSockets - IE10 HTML5 video - IE9, 10 Web Workers - IE10 HTML5 History API - IE10 Application cache - IE10 WebGL - Unknown ! ! ! ! ! ! !
  6. Hold on! Support isn't just an IE issue! Bleeding-edge features

    aren't available in all modern browsers at the same time. CSS Regions CSS Grid Layout CSS Filter Effects Web Audio API Page Visibility API ContextMenu API Download API ! ! ! ! ! ! !
  7. Polyfills are a type of shim that retrofit legacy browsers

    with modern HTML5/CSS3 features — Remy Sharp “ ”
  8. Shims refer to any code that intercepts API calls and

    provides a layer of abstraction — Paul Irish “ ”
  9. What can they do? Polyfills help us use today's modern

    features in yesterday's browsers Use native capabilities if a browser does support a feature Use fallbacks (Java applets, Flash, JS) for browsers that don't Provide as consistent an experience as is possible* *More on this later! ! ! ! !
  10. What polyfills are widely used today? HTML5Shim/HTML5Shiv respond.js mediaelement.js socket.io

    flashcanvas store.js / AmplifyJS CSS3Pie history.js/pjax ! ! ! ! ! ! ! !
  11. Modernizr - feature detection made simple Adds classes to the

    html tag based on browser capabilities <html class="no-cssgradients boxshadow no-cssanimations ..."> This enables styling rules that can only be rendered in capable browsers .no-cssgradients .shinybutton { background: url("shinybutton.jpg"); } .cssgradients .shinybutton { background-image: linear-gradient(top, #555, #333); } ! !
  12. Modernizr feature-detection tests Tests for many features come readily built-in

    (hot) Does the browser support WebGL? 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. ! !
  13. Modernizr also supports custom feature detection plug-ins WebAudio API Modernizr.addTest('webaudio',

    !!(window.webkitAudioContext || window.AudioContext)); Track API Modernizr.addTest('trackapi', typeof document.createElement('video').addTextTrack === 'function');
  14. Include HTML5Shim & we can also enable styling and using

    semantic HTML5 elements in oldIE All browsers have a master list of elements supported If an element isn't on this list it's considered unknown How should these elements be styled? ! ! !
  15. More issues oldIE doesn't allow these elements to be styled

    at all What does the DOM for an unknown element look like? 1. IE inserts it as a empty node without any children 2. Children of the element are treated as siblings ! ! ! ! !
  16. The solution Create a dummy element in the page before

    using it Don't even need to insert it into the DOM! <!DOCTYPE html> <html> <head> <style>article { border:1px solid red; display:block}</style> <script>document.createElement('article');</script> </head> <body> <article> <p>Hello FITC!</p> </article> ... ! !
  17. So, HTML5Shim.. Runs a little loop in JavaScript to 'enable'

    all the different elements in HTML5 Allows us to style tags like <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 ! !
  18. It's important to remember.. Just because you can load a

    polyfill doesn't mean you should The fastest loading page on the planet is this Each thing added to this page means more time the browser has to spend loading or processing Consider performance - developers don't benchmark polyfills enough (more on this soon!) Look at speed, value added, loading mechanism ! ! ! ! !
  19. Only load polyfills if they're absolutely needed There are some

    great script loaders that can help with conditional loading yepnope.js (standalone, Modernizr) - Alex Sexton LabJS - Kyle Simpson YeahNo.js - also by Kyle ! ! !
  20. yepnope.js - an asynchronous conditional resource loader 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"); } } });
  21. Modernizr includes yepnope.js in special builds Supports similarly loading up

    a geolocation polyfill depending on support Modernizr.load({ test: Modernizr.geolocation, yep : 'geo.js', nope: 'geo-polyfill.js' });
  22. LabJS 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");
  23. YeahNo.js - a yepnope API wrapper around LabJS 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!'); } } });
  24. Writing Polyfills Why should you give writing polyfills a go?

    Use modern/bleeding-edge features in today's browsers Fantastic opportunity to learn subtleties in vendor implementations It'll force you to read specifications and understand them If it's reliable, other developers may also be able to use it ! ! ! !
  25. You can't detect 'HTML5 Support', but you can detect support

    for individual features — Mark Pilgrim “ ”
  26. Feature detection has some friends There are some useful tips

    to keep in mind Objects like window and document are your friend Fire up the console and look at this: window //explore me! BOOM. Often the first place to check for support of new features Careful. Not all browsers have the same naming conventions for experimental features ! ! ! !
  27. Testing techniques in order of preference: 1. Feature testing -

    check for the existence of a method and if it returns the correct output 2. Feature detection - check for the existence of a method 3. Weak inference - check for the existence of an unrelated method 4. UA sniffing - check the browser's user agent string Thanks to @mathias and @jdalton for this list More on these very soon! ! ! ! !
  28. Support Detecting finalized and unfinalized features. Test if: 1. Property

    of the feature exists on a global object (window or navigator) 2. Property of the feature exists on a specific element 3. Method of the feature exists on a specific element where the value it returns can be tested 4. Property of the feature can be set and its value retained ! ! ! !
  29. 1. Property exists on a global object Testing for geolocation

    support function isGeolocationSupported(){ return !!navigator.geolocation; } and with Modernizr it's as simple as.. if(Modernizr.geolocation){ // supported }else{ // not supported }
  30. 2. Property of the feature exists on a specific element

    Testing for <canvas> support function isCanvasSupported(){ return !!document.createElement('canvas').getContext; } and with Modernizr its.. if(Modernizr.canvas){ // supported }else{ // not supported }
  31. 3. Method of the feature exists on a specific element

    where the value it returns can be tested Testing for <audio> support function isAudioSupported(){ return !!document.createElement('audio').canPlayType; } and with Modernizr its.. if(Modernizr.audio){ // supported }else{ // not supported }
  32. But we can take this further 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 }
  33. 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 }
  34. 4. Property of the feature can be set and it's

    value retained 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
  35. Very, very simple JavaScript API support testing 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 Simple CSS property support testing
  36. Simple CSS property support testing function isPropSupported(prop){ var el =

    document.createElement('div'); return prop in el.style; }; isPropSupported('borderRadius'); // true isPropSupported('boxShadow'); // true isPropSupported('textShadow'); // true Simple CSS selector support testing
  37. Simple CSS selector support testing 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 Basic HTML Attribute support testing
  38. Basic HTML Attribute support testing 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 Going further: snippets from has.js
  39. Going further: snippets from has.js HTML5 audio feature test for

    WAV var audio = document.createElement('audio'); audio.canPlayType("audio/wav; codecs=1"); //probably CSS3 support detection (border radius) var elStyle = document.createElement('div').style; if(typeof elStyle['borderRadius']=="string"){ //supported } Modernizr snippets
  40. Modernizr snippets File API: Where a spec includes multiple features

    !!(window.File && window.FileList && window.FileReader) Data URI Support var dataURI = new Image(); datauri.onload = function(){ return (datauri.width == 1 && datauri.height == 1); // true or false } datauri.src='data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
  41. Asynchronous Example: WebP (lossless image format) (function(){ // full version:

    http://bit.ly/qLJbFj var image = new Image(); image.onerror = function() { Modernizr.addTest('webp', false); }; image.onload = function() { Modernizr.addTest('webp', function() { return image.width == 1; }); }; image.src = 'data:image/webp;base64,UklGRj..'; }());
  42. Vendor prefixes 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.
  43. Getting the vendor prefix 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
  44. A very very simple jQuery placeholder polyfill 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! }); Please enter some text
  45. Step 2 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 });
  46. Step 3 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); } } }); });
  47. Some quick notes This was a quick example and the

    code didn't account for password inputs, textareas or values of 'placeholder' entered It will also submit the placeholder values if no value is entered. Ideally use this awesome plugin by @mathias instead ! ! !
  48. Benchmark performance of the complete polyfill Are there other scripts

    loading on the page? Polyfills relying on other polyfills? ! ! !
  49. Performance testing involved.. Discovering how well the native feature performed

    Benchmarking how well various polyfills for it performed Deciding which browsers to use it in based on this information ! ! !
  50. Distribution check-list Before you release your polyfill into the wild..

    Unit tests - do your best to include them if possible Documentation - what does and doesn't it do? Support - level of future-support and maintenance Alternatives - acknowledge, describe differences. help users (Optional, but nice) Performance tests ! ! ! ! !
  51. Will they be around forever? As browser vendors implement new

    specs and features, the need for specific polyfills will decrease. There will be a day when oldIE is dead but polyfills will still come in useful for filling missing gaps in other browsers Until then, regularly test latest browsers in between releases to ensure the polyfill isn't loading unless needed ! !
  52. SUMMARY What did we learn today? Polyfills are amazing when

    used responsibly Writing your own polyfills or feature detection tests isn't as hard as it looks Don't be afraid to give users a lower-res experience where it makes sense Benchmark what you do to ensure users are given the fastest experience possible ! ! ! !
  53. That's a wrap! For more on me: @addyosmani http://addyosmani.com http://github.com/addyosmani

    G+:Addy Osmani Big thanks to @paul_irish, @mathias, @peol, @rem and others for their previous work in this area and technical reviews. ! ! ! !