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
    "
    "
    "
    "
    "

    View full-size slide

  2. Why Do Polyfills Matter?

    View full-size slide

  3. WebGL in IE6, 7 & 8 using JebGL (Video)

    View full-size slide

  4. Canvas animation in IE6, 7 & 8 with
    FlashCanvas (Demo)

    View full-size slide

  5. WebSocket API support with instant
    fallbacks to long polling using socket.io
    (Demo)

    View full-size slide

  6. CSS3Pie - A polyfill for CSS3 decoration
    features

    View full-size slide

  7. CSS3 border-radius,box-shadow and
    gradients in IE7

    View full-size slide

  8. CSS3 patterns gallery for modern browsers
    (Demo)

    View full-size slide

  9. Which we can polyfill for IE9 (Demo)

    View full-size slide

  10. 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...
    !
    !
    !

    View full-size slide

  11. WebCam access in special Opera/Chrome
    builds using getUserMedia() (Demo)

    View full-size slide

  12. Features & Support

    View full-size slide

  13. 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
    !
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  14. HTML5 & CSS3 Readiness (View)

    View full-size slide

  15. Can I Use? (View)

    View full-size slide

  16. oldIE: Internet Explorer 6,
    7 & 8. aka, the three
    browsers often getting
    the low-res experience.
    — Paul Irish


    View full-size slide

  17. 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
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  18. Unless you're using something like
    Chrome Frame

    View full-size slide

  19. 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
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  20. Can we use modern features now? Let's
    wait...or

    View full-size slide

  21. Use Polyfills & Shims

    View full-size slide

  22. Polyfills are a type of
    shim that retrofit legacy
    browsers with modern
    HTML5/CSS3 features
    — Remy Sharp


    View full-size slide

  23. Shims refer to any code
    that intercepts API calls
    and provides a layer of
    abstraction
    — Paul Irish


    View full-size slide

  24. 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!
    !
    !
    !
    !

    View full-size slide

  25. What polyfills are widely used today?
    HTML5Shim/HTML5Shiv
    respond.js
    mediaelement.js
    socket.io
    flashcanvas
    store.js / AmplifyJS
    CSS3Pie
    history.js/pjax
    !
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  26. Modernizr - feature detection made simple
    Adds classes to the html tag based on
    browser capabilities

    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);
    }
    !
    !

    View full-size slide

  27. 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.
    !
    !

    View full-size slide

  28. READ THE DOCS FOR MORE ON BUILT-IN
    TESTS

    View full-size slide

  29. 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');

    View full-size slide

  30. 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?
    !
    !
    !

    View full-size slide

  31. 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
    !
    !
    ! !
    !

    View full-size slide

  32. The solution
    Create a dummy element in the page before
    using it
    Don't even need to insert it into the DOM!



    article { border:1px solid red; display:block}
    document.createElement('article');



    Hello FITC!

    ...
    !
    !

    View full-size slide

  33. So, HTML5Shim..
    Runs a little loop in JavaScript to 'enable' all the
    different elements in HTML5
    Allows us to style tags like instead
    of in older browsers

    I can now be styled in all browsers!

    Remember, polyfills still needed for other
    features
    !
    !

    View full-size slide

  34. Issues & Solutions

    View full-size slide

  35. 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
    !
    !
    !
    !
    !

    View full-size slide

  36. Take the 'hardboiled' approach

    View full-size slide

  37. It's okay for users to get different
    experiences

    View full-size slide

  38. Lo-res - users are still delivered content.
    Default stylesheet used instead.

    View full-size slide

  39. Be a champion of performance

    View full-size slide

  40. Optimize to make best use of the capabilities
    a user's browser supports

    View full-size slide

  41. 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
    !
    !
    !

    View full-size slide

  42. 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");
    }
    }
    });

    View full-size slide

  43. 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'
    });

    View full-size slide

  44. 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");

    View full-size slide

  45. 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!');
    }
    }
    });

    View full-size slide

  46. Writing Polyfills

    View full-size slide

  47. 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
    !
    !
    !
    !

    View full-size slide

  48. Test what features your current browser
    supports (View)

    View full-size slide

  49. Does Browser-x natively support the
    feature? Browser.next?

    View full-size slide

  50. Specifications - What does the API look
    Like? Check standards groups like the W3C
    for specs

    View full-size slide

  51. Quirks - What quirks do older browser
    implementations suffer from?
    QuirksMode.org

    View full-size slide

  52. You can't detect 'HTML5
    Support', but you can
    detect support for
    individual features
    — Mark Pilgrim


    View full-size slide

  53. 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
    !
    !
    !
    !

    View full-size slide

  54. 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!
    !
    !
    !
    !

    View full-size slide

  55. 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
    !
    !
    !
    !

    View full-size slide

  56. 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
    }

    View full-size slide

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

    View full-size slide

  58. 3. Method of the feature exists on a
    specific element where the value it returns
    can be tested
    Testing for support
    function isAudioSupported(){
    return !!document.createElement('audio').canPlayType;
    }
    and with Modernizr its..
    if(Modernizr.audio){
    // supported
    }else{
    // not supported
    }

    View full-size slide

  59. But we can take this further
    Testing for 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
    }

    View full-size slide

  60. 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
    }

    View full-size slide

  61. 4. Property of the feature can be set and
    it's value retained
    Testing for 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

    View full-size slide

  62. More Feature-Detection Tests
    Very, very simple JavaScript API support

    View full-size slide

  63. 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

    View full-size slide

  64. 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

    View full-size slide

  65. Simple CSS selector support testing
    function isSelectorSupported(sel){
    var el = document.createElement('div');
    el.innerHTML = '​'+ sel + '{}';
    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

    View full-size slide

  66. 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

    View full-size slide

  67. 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

    View full-size slide

  68. 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==';

    View full-size slide

  69. 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..';
    }());

    View full-size slide

  70. 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.

    View full-size slide

  71. CSS3 Please - The Cross-Browser Rule
    Generator (View)

    View full-size slide

  72. 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

    View full-size slide

  73. Getting the vendor prefix with Modernizr
    console.log(Modernizr.prefixed('transform'));//WebkitTransform

    View full-size slide

  74. Polyfills with jQuery

    View full-size slide

  75. A very very simple jQuery placeholder
    polyfill
    The HTML5 Placeholder attribute is used as
    follows:

    Begin by iterating over all the input elements with
    a placeholder attribute
    $("input[placeholder]").each(function() {
    // more logic to come!
    });
    Please enter some text

    View full-size slide

  76. 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
    });

    View full-size slide

  77. 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); } }
    });
    });

    View full-size slide

  78. 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
    !
    !
    !

    View full-size slide

  79. Watch out! Not everything can be detected
    (View)

    View full-size slide

  80. Benchmark performance of the complete
    polyfill
    Are there other scripts loading on the
    page?
    Polyfills relying on other polyfills?
    !
    !
    !

    View full-size slide

  81. Break your polyfill into smaller parts. Can
    those be tested or optimized further?

    View full-size slide

  82. Is the polyfill visibly slow in some browsers?
    Use vs. lose

    View full-size slide

  83. Benchmark performance with jsPerf.com
    (View)

    View full-size slide

  84. Case study: forEach() (ES5)

    View full-size slide

  85. 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
    !
    !
    !

    View full-size slide

  86. Getting the polyfill out there

    View full-size slide

  87. 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
    !
    !
    !
    !
    !

    View full-size slide

  88. Offer minified versions of your polyfill

    View full-size slide

  89. Host on GitHub

    View full-size slide

  90. Post To The Modernizr Polyfills Wiki

    View full-size slide

  91. Polyfills will probably still exist in the future.

    View full-size slide

  92. 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
    !
    !

    View full-size slide

  93. Firefox nightlies

    View full-size slide

  94. Chromium (Stable, Beta, Canary builds)

    View full-size slide

  95. Internet Explorer Preview

    View full-size slide

  96. 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
    !
    !
    !
    !

    View full-size slide

  97. 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.
    !
    !
    !
    !

    View full-size slide