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. View Slide

  2. 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 Slide

  3. Why Do Polyfills Matter?

    View Slide

  4. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  8. CSS3Pie - A polyfill for CSS3 decoration
    features

    View Slide

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

    View Slide

  10. CSS3 patterns gallery for modern browsers
    (Demo)

    View Slide

  11. Which we can polyfill for IE9 (Demo)

    View Slide

  12. 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 Slide

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

    View Slide

  14. Features & Support

    View Slide

  15. 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 Slide

  16. HTML5 & CSS3 Readiness (View)

    View Slide

  17. Can I Use? (View)

    View Slide

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


    View Slide

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

  20. Unless you're using something like
    Chrome Frame

    View Slide

  21. 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 Slide

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

    View Slide

  23. Use Polyfills & Shims

    View Slide

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


    View Slide

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


    View Slide

  26. 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 Slide

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

    View Slide

  28. View Slide

  29. View Slide

  30. 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 Slide

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

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

    View Slide

  33. 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 Slide

  34. 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 Slide

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

  36. 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 Slide

  37. 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 Slide

  38. Issues & Solutions

    View Slide

  39. 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 Slide

  40. Take the 'hardboiled' approach

    View Slide

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

    View Slide

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

    View Slide

  43. Be a champion of performance

    View Slide

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

    View Slide

  45. 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 Slide

  46. View Slide

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

  48. 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 Slide

  49. View Slide

  50. 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 Slide

  51. 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 Slide

  52. Writing Polyfills

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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


    View Slide

  59. 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 Slide

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

  61. 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 Slide

  62. 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 Slide

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

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

  65. 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 Slide

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

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

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

    View Slide

  69. 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 Slide

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

  71. 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 Slide

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

  73. 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 Slide

  74. 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 Slide

  75. 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 Slide

  76. 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 Slide

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

    View Slide

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

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

    View Slide

  80. Polyfills with jQuery

    View Slide

  81. 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 Slide

  82. 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 Slide

  83. 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 Slide

  84. 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 Slide

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

    View Slide

  86. Performance

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  90. Benchmark performance with jsPerf.com
    (View)

    View Slide

  91. Case study: forEach() (ES5)

    View Slide

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

  93. View Slide

  94. Getting the polyfill out there

    View Slide

  95. 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 Slide

  96. Offer minified versions of your polyfill

    View Slide

  97. Host on GitHub

    View Slide

  98. Post To The Modernizr Polyfills Wiki

    View Slide

  99. The Future

    View Slide

  100. Polyfills will probably still exist in the future.

    View Slide

  101. 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 Slide

  102. Firefox nightlies

    View Slide

  103. Chromium (Stable, Beta, Canary builds)

    View Slide

  104. Opera.next

    View Slide

  105. Internet Explorer Preview

    View Slide

  106. 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 Slide

  107. 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 Slide