$30 off During Our Annual Pro Sale. View Details »

Crafting App Interfaces with jQuery

Crafting App Interfaces with jQuery

Slides from a talk I gave at DrupalCon Chicago in 2011.

Nathan Smith

March 02, 2012
Tweet

More Decks by Nathan Smith

Other Decks in Programming

Transcript

  1. CRAFTING APP
    INTERFACES WITH
    Presented by Nathan Smith

    View Slide

  2. Note: Don’t feel like you need to write all of
    this down. The slides are available here...
    http://www.slideshare.net/nathansmith/drupalcon-jquery
    Carpal Tunnel Relief

    View Slide

  3. “Allow myself to... introduce myself”
    I work (from home) for HP
    as a front-end developer.
    I’m best known for making
    a helpful 5.6 KB CSS file.

    View Slide

  4. Shh... My secret in-house code reviewer
    Note: This was shot hastily with a camera phone. I make no claims of being a decent photographer.

    View Slide

  5. FYI – I don’t especially love CSS
    The reason I create and use CSS
    frameworks is because I hate doing
    mundane tasks repeatedly (yawn).
    I’d rather be working in JavaScript.

    View Slide

  6. Co-author
    JavaScript books I’ve worked on...
    Tech editor Tech editor
    jQueryEnlightenment.com
    oreilly.com/catalog/9780596159788
    JavaScriptEnlightenment.com

    View Slide

  7. Our team at HP uses Sass
    to expedite writing CSS
    http://www.sass-lang.com

    View Slide

  8. Sass compiles down to CSS...
    $blue: #3bbfce
    $margin: 20px
    #foo
    border-color: $blue
    color: darken($blue, 9%)
    .bar
    border-color: $blue
    padding: $margin / 2
    margin: $margin / 2
    #foo {
    border-color: #3bbfce;
    color: #2b9eab;
    }
    #foo .bar {
    border-color: #3bbfce;
    padding: 10px;
    margin: 10px;
    }

    View Slide

  9. Assuming you already know CSS,
    Sass can help you write CSS faster.
    But using Sass doesn’t magically
    create “better” CSS. If you write
    bad code, Sass won’t remedy it.
    But Sass isn’t a magic bullet
    (Because no framework is)

    View Slide

  10. The same principles
    apply to JavaScript
    An important discipline when
    using any framework is striving
    to understand the underlying
    language. In other words, use
    it as a tool – Not a black box

    View Slide

  11. Veteran “ninjas” master a variety of tools – Not just one.
    Use a framework as an extension
    of yourself – Not just as a crutch.
    BY HAND
    FRAMEWORK
    http://imdb.com/title/tt1046173

    View Slide

  12. “JavaScript is the only language
    that I’m aware of that people
    feel they don’t need to learn
    before they start using it.”
    — Douglas Crockford
    http://www.youtube.com/watch?v=hQVTIJBZook
    A day to learn, A lifetime to master

    View Slide

  13. Today, I’ll discuss two examples...
    jQuery Desktop Formalize.Me
    http://desktop.sonspring.com http://formalize.me

    View Slide

  14. jQuery Desktop
    Fun with z-index

    View Slide

  15. jQuery Desktop
    Fun with z-index

    View Slide

  16. Demystifying z-index
    http://sonspring.com/journal/jquery-desktop

    View Slide

  17. Demystifying z-index
    The default z-index value is 0, and it
    only takes effect if an element is
    position: fixed, relative, or absolute.
    The default is position: static.
    You don’t have to go overboard with
    z-index: 99999. Just +1 higher than
    another element will suffice.

    View Slide


  18. ...


    <br/>if (!window.jQuery) {<br/>document.write(unescape('%3Cscript src="assets/javascripts/jquery.js"%3E%3C/script%3E'));<br/>document.write(unescape('%3Cscript src="assets/javascripts/jquery.ui.js"%3E%3C/script%3E'));<br/>}<br/>

    ...

    CDN & Local Fallback
    => Best of both worlds
    http://desktop.sonspring.com

    View Slide

  19. Singleton Pattern = Object-oriented JS
    Nested JS objects versus dot-notation
    => Same result, but different approaches
    var APP = {
    foo: function() {
    // Do stuff.
    },
    bar: function() {
    // Do more stuff.
    }
    }
    var APP = {};
    APP.foo = function() {
    // Do stuff.
    };
    APP.bar = function() {
    // Do more stuff.
    };
    I prefer the one on the left, but you would call methods by
    using the same syntax either way: APP.foo() + APP.bar()

    View Slide

  20. // Module pattern.
    var APP = (function($, window, document, undefined) {
    // For use only inside APP.
    var PRIVATE_CONSTANT_1 = 'foo';
    var PRIVATE_CONSTANT_2 = 'bar';
    // Expose contents of APP.
    return {
    go: function() {
    for (var i in APP.init) {
    APP.init[i]();
    }
    },
    // APP.init
    init: {
    call_automatically: function() {
    // Called when DOM is ready.
    // Can still be called individually, via:
    // APP.init.call_automatically();
    },
    // etc.
    },
    // APP.misc
    misc: {
    call_specifically: function() {
    // Must be called individually, via:
    // APP.misc.call_specifically();
    },
    // etc.
    }
    };
    // Alias window, document.
    })(jQuery, this, this.document);
    // Automatically run all functions in APP.init
    jQuery(document).ready(function() {
    APP.go();
    });
    Module Pattern
    << init example
    Using a module pattern exposes
    only one global variable, in this
    case “APP” – with the rest of
    your methods within an object.
    It gives you the ability to use
    private “constant” variables
    inside your appʼs function scope.
    Nesting functions within an “init”
    object allows them to be called
    automatically on DOM ready.

    View Slide

  21. .live() is Awesome. Use it. Love it.
    $('div.window').live('mousedown', function() {
    // Bring window to front.
    JQD.util.window_flat();
    $(this).addClass('window_stack');
    }).live('mouseenter', function() {
    $(this).die('mouseenter').draggable({
    // Confine to desktop.
    // Movable via top bar only.
    cancel: 'a',
    containment: 'parent',
    handle: 'div.window_top'
    }).resizable({
    containment: 'parent',
    minWidth: 400,
    minHeight: 200
    });
    // etc.
    });

    View Slide

  22. All the event wire-ups are ready, before
    the page even has any content. Once
    the content is loaded remotely via Ajax,
    the elements respond to their respective
    predefined .live() event listeners.
    Live (pun intended) example of .live()
    http://desktop.sonspring.com/ajax_load.html

    View Slide

  23. var JQD = (function($, window, document, undefined) {
    return {
    go: function() {
    for (var i in JQD.init) {
    JQD.init[i]();
    }
    },
    init: {
    frame_breaker: function() {/* ... */},
    clock: function() {/* ... */},
    desktop: function() {/* ... */},
    wallpaper: function() {/* ... */}
    },
    util: {
    clear_active: function() {/* ... */},
    window_flat: function() {/* ... */},
    window_resize: function() {/* ... */}
    }
    })(jQuery, this, this.document);
    Basic structure of jQuery Desktop

    View Slide

  24. // Cancel mousedown, right-click.
    $(document).mousedown(function(ev) {
    var tags = ['a', 'button', 'input', 'select', 'textarea'];
    if (!$(ev.target).closest(tags).length) {
    JQD.util.clear_active();
    ev.preventDefault();
    ev.stopPropagation();
    }
    }).bind('contextmenu', function() {
    return false;
    });
    Event listeners at the document level

    View Slide

  25. // Relative or remote links?
    $('a').live('click', function(ev) {
    var url = $(this).attr('href');
    this.blur();
    if (url.match(/^#/)) {
    ev.preventDefault();
    ev.stopPropagation();
    }
    else {
    $(this).attr('target', '_blank');
    }
    });
    External links open in a new window

    View Slide

  26. // Make top menus active.
    $('a.menu_trigger').live('mousedown', function() {
    if ($(this).next('ul.menu').is(':hidden')) {
    JQD.util.clear_active();
    $(this).addClass('active').next('ul.menu').show();
    }
    else {
    JQD.util.clear_active();
    }
    }).live('mouseenter', function() {
    // Transfer focus, if already open.
    if ($('ul.menu').is(':visible')) {
    JQD.util.clear_active();
    $(this).addClass('active').next('ul.menu').show();
    }
    });
    Top level drop-down menus

    View Slide

  27. // Desktop icons.
    $('a.icon').live('mousedown', function() {
    // Highlight the icon.
    JQD.util.clear_active();
    $(this).addClass('active');
    }).live('dblclick', function() {
    // Get the link's target.
    var x = $(this).attr('href');
    var y = $(x).find('a').attr('href');
    // Show the taskbar button.
    if ($(x).is(':hidden')) {
    $(x).remove().appendTo('#dock');
    $(x).show('fast');
    }
    // Bring window to front.
    JQD.util.window_flat();
    $(y).addClass('window_stack').show();
    }).live('mouseenter', function() {
    $(this).die('mouseenter').draggable({
    revert: true,
    containment: 'parent'
    });
    });
    Making desktop icons interactive

    View Slide

  28. // Taskbar buttons.
    $('#dock a').live('click', function() {
    // Get the link's target.
    var x = $($(this).attr('href'));
    // Hide, if visible.
    if (x.is(':visible')) {
    x.hide();
    }
    else {
    // Bring window to front.
    JQD.util.window_flat();
    x.show().addClass('window_stack');
    }
    });
    Taskbar / Dock buttons

    View Slide

  29. // Minimize the window.
    $('a.window_min').live('click', function() {
    $(this).closest('div.window').hide();
    });
    // Maximize or restore the window.
    $('a.window_resize').live('click', function() {
    JQD.util.window_resize(this);
    });
    // Close the window.
    $('a.window_close').live('click', function() {
    $(this).closest('div.window').hide();
    $($(this).attr('href')).hide('fast');
    });
    Manipulating the “windows”

    View Slide

  30. // Show desktop button, ala Windows OS.
    $('#show_desktop').live('click', function() {
    // If any windows are visible, hide all.
    if ($('div.window:visible').length) {
    $('div.window').hide();
    }
    else {
    // Otherwise, reveal hidden windows that are open.
    $('#dock li:visible a').each(function() {
    $($(this).attr('href')).show();
    });
    }
    });
    Clicking the Show Desktop button

    View Slide

  31. To prevent developers from
    wasting countless hours on
    styling dumb form elements

    View Slide

  32. “Future plans include a tutorial on how
    to use jQuery to add styling hooks to
    form elements, since I know from
    experience that is no cup of tea.”
    — Source = Me when announcing 960.gs in 2008!
    — Excuse = New HTML5 elements set me back :)
    It’s been awhile in the making...

    View Slide

  33. View Slide

  34. select {
    -webkit-appearance: none;
    }
    WebKit’s form styling secret sauce
    This gives you back a bit of control
    for things like drop-downs.

    View Slide

  35. document.write(ad) with position:absolute

    View Slide

  36. document.write(ad) with position:absolute

    View Slide

  37. document.write(ad) with position:absolute
    <br/>document.write(unescape('%3Cscr' + 'ipt src="http://adn.fusionads.net/...' + M<br/>

    Ads by Fusion

    ...

    View Slide

  38. header[role="banner"] h1 {
    background-repeat: no-repeat;
    background-position: center 20px;
    background-image: url(../images/logo.png);
    background-image: url(data:imagepng;base64,
    iVBORw0KGgoAAA...);
    color: transparent;
    font-size: 0;
    overflow: hidden;
    text-indent: -99999px;
    height: 130px;
    }
    Base64 encode background images
    http://formalize.me http://www.motobit.com/util/base64-decoder-encoder.asp

    View Slide


  39. ...














    Pre-load JavaScript files
    http://formalize.me/demo.html

    View Slide


  40. ...














    Pre-load JavaScript files
    http://formalize.me/demo.html

    View Slide

  41. var IE6 = IE(6);
    var IE7 = IE(7);
    // Internet Explorer detection.
    function IE(version) {
    var b = document.createElement('b');
    b.innerHTML = '';
    return !!b.getElementsByTagName('br').length;
    }
    IE detection for Prototype.js
    http://formalize.me/assets/javascripts/prototype.formalize.js
    Prevents false positives since Opera can be made to impersonate IE.
    Note: I did this because Prototype doesn’t detect IE version number.

    View Slide

  42. var ie = (function() {
    var undef,
    v = 3,
    div = document.createElement('div'),
    all = div.getElementsByTagName('i');
    while (
    div.innerHTML = '',
    all[0]
    );
    return v > 4 ? v : undef;
    }());
    James Padolsey's IE detection (whoa!)
    https://gist.github.com/527683

    View Slide

  43. Line number counts in Formalize JS
    Dojo = 174 lines ExtJS = 168 lines
    jQuery = 158 lines MooTools = 163 lines
    Prototype = 171 lines YUI = 168 lines
    “Write Less, Do More” FTW! :)

    View Slide

  44. var FORMALIZE = (function($, window, document, undefined) {
    // Private constants.
    ! var PLACEHOLDER_SUPPORTED = 'placeholder' in document.createElement('input');
    ! var AUTOFOCUS_SUPPORTED = 'autofocus' in document.createElement('input');
    ! var WEBKIT = 'webkitAppearance' in document.createElement('select').style;
    ! var IE6 = !!($.browser.msie && parseInt($.browser.version, 10) === 6);
    ! var IE7 = !!($.browser.msie && parseInt($.browser.version, 10) === 7);
    // Expose innards of FORMALIZE.
    return {
    go: function() {/* ... */},
    init: {
    detect_webkit: function() {/* ... */},
    full_input_size: function() {/* ... */},
    ie6_skin_inputs: function() {/* ... */},
    autofocus: function() {/* ... */},
    placeholder: function() {/* ... */}
    },
    misc: {
    add_placeholder: function() {/* ... */}
    }
    };
    })(jQuery, this, this.document);
    Basic structure of Formalize JS

    View Slide

  45. // FORMALIZE.init.detect_webkit
    detect_webkit: function() {
    ! if (!WEBKIT) {
    ! ! return;
    ! }
    ! // Tweaks for Safari + Chrome.
    ! $('html').addClass('is_webkit');
    },
    ...
    Detecting WebKit

    View Slide

  46. // FORMALIZE.init.full_input_size
    full_input_size: function() {
    ! if (!IE7 || !$('textarea, input.input_full').length) {
    ! ! return;
    ! }
    ! // This fixes width: 100% on and class="input_full".
    ! // It ensures that form elements don't go wider than container.
    ! $('textarea, input.input_full')
    .wrap('');
    },
    ...
    Basic structure of Formalize JS

    View Slide

  47. // FORMALIZE.init.ie6_skin_inputs
    ie6_skin_inputs: function() {
    // Test for Internet Explorer 6.
    if (!IE6 || !$('input, select, textarea').length) {
    // Exit if the browser is not IE6,
    // or if no form elements exist.
    return;
    }
    // For , etc.
    var button_regex = /button|submit|reset/;
    // For , etc.
    var type_regex = /date|datetime|datetime-local|email|month|number|password|range|search|tel|text|time|url|week/;
    $('input').each(function() {
    var el = $(this);
    // Is it a button?
    if (this.getAttribute('type').match(button_regex)) {
    el.addClass('ie6_button');
    /* Is it disabled? */
    if (this.disabled) {
    el.addClass('ie6_button_disabled');
    }
    }
    // Or is it a textual input?
    else if (this.getAttribute('type').match(type_regex)) {
    el.addClass('ie6_input');
    /* Is it disabled? */
    if (this.disabled) {
    el.addClass('ie6_input_disabled');
    }
    }
    });
    $('textarea, select').each(function() {
    /* Is it disabled? */
    if (this.disabled) {
    $(this).addClass('ie6_input_disabled');
    }
    });
    },
    Adding styling
    hooks for IE6

    View Slide

  48. // FORMALIZE.init.autofocus
    autofocus: function() {
    ! if (AUTOFOCUS_SUPPORTED || !$(':input[autofocus]').length) {
    ! ! return;
    ! }
    ! $(':input[autofocus]:visible:first').focus();
    },
    ...
    Adding HTML5 autofocus support

    View Slide

  49. // FORMALIZE.init.placeholder
    placeholder: function() {
    ! if (PLACEHOLDER_SUPPORTED || !$(':input[placeholder]').length) {
    ! ! // Exit if placeholder is supported natively,
    ! ! // or if page does not have any placeholder.
    ! ! return;
    ! }
    ! FORMALIZE.misc.add_placeholder();
    ! $(':input[placeholder]').each(function() {
    ! ! var el = $(this);
    ! ! var text = el.attr('placeholder');
    ! ! el.focus(function() {
    ! ! ! if (el.val() === text) {
    ! ! ! ! el.val('').removeClass('placeholder_text');
    ! ! ! }
    ! ! }).blur(function() {
    ! ! ! FORMALIZE.misc.add_placeholder();
    ! ! });
    ! ! // Prevent from accidentally
    ! ! // submitting the placeholder text.
    ! ! el.closest('form').submit(function() {
    ! ! ! if (el.val() === text) {
    ! ! ! ! el.val('').removeClass('placeholder_text');
    ! ! ! }
    ! ! }).bind('reset', function() {
    ! ! ! setTimeout(FORMALIZE.misc.add_placeholder, 50);
    ! ! });
    ! });
    }
    Adding HTML5 placeholder support

    View Slide

  50. // FORMALIZE.misc
    misc: {
    ! // FORMALIZE.misc.add_placeholder
    ! add_placeholder: function() {
    ! ! if (PLACEHOLDER_SUPPORTED || !$(':input[placeholder]').length)
    {
    ! ! ! // Exit if placeholder is supported natively,
    ! ! ! // or if page does not have any placeholder.
    ! ! ! return;
    ! ! }
    ! ! $(':input[placeholder]').each(function() {
    ! ! ! var el = $(this);
    ! ! ! var text = el.attr('placeholder');
    ! ! ! if (!el.val() || el.val() === text) {
    ! ! ! ! el.val(text).addClass('placeholder_text');
    ! ! ! }
    ! ! });
    ! }
    }
    Moved outside of init, for easier reuse

    View Slide

  51. http://jeromeetienne.github.com/jquery-mobile-960

    View Slide

  52. jQuery Companion Libraries
    jQuery UI – http://jqueryui.com
    jQuery Mobile – http://jquerymobile.com
    Amplify – http://amplifyjs.com
    Underscore – http://documentcloud.github.com/underscore
    Backbone – http://documentcloud.github.com/backbone
    JS Testing
    Jasmine – http://pivotal.github.com/jasmine
    QUnit – http://docs.jquery.com/Qunit
    If you want write Ruby-esque code that compiles to JS...
    CoffeeScript – http://jashkenas.github.com/coffee-script
    Additional Resources

    View Slide

  53. Learning jQuery – https://www.packtpub.com/learning-jquery-1.3
    jQuery Enlightenment – http://jqueryenlightenment.com
    DOM Scripting – http://domscripting.com
    JavaScript: The Good Parts – http://oreilly.com/catalog/9780596517748
    JavaScript: The Definitive Guide – http://oreilly.com/catalog/9780596101992
    High Performance JavaScript – http://oreilly.com/catalog/9780596802806
    Pro JavaScript Design Patterns – http://jsdesignpatterns.com
    Object-Oriented JavaScript – https://www.packtpub.com/object-oriented-javascript/book
    The Art and Science of JavaScript – http://www.sitepoint.com/books/jsdesign1
    Test-Driven JavaScript Development – http://tddjs.com
    Recommended Books

    View Slide

  54. For questions – Or just to say hi :)
    Contact – http://sonspring.com/contact
    Twitter – http://twitter.com/nathansmith
    http://www.slideshare.net/nathansmith/drupalcon-jquery

    View Slide

  55. What did you think?
    Locate this session on the DCC website:
    http://chicago2011.drupal.org/sessions
    Click the “Take the Survey” link.
    Thanks!

    View Slide