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

JavaScript For Non-Web Apps

JavaScript For Non-Web Apps

Trevor Davis

April 18, 2013
Tweet

More Decks by Trevor Davis

Other Decks in Programming

Transcript

  1. by Trevor Davis
    at DC jQuery Users Group
    on April , 
    © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    JAVASCRIPT FOR
    NON-WEB APPS

    View Slide

  2. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    @trevor_davis
    HI! I’M
    I work as a Senior Front-End Developer at Viget.
    TREVOR DAVIS

    View Slide

  3. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    I. JavaScript “Basics” & “Best Practices”
    II. Anatomy of a jQuery Plugin
    III. jQuery Plugin Patterns
    IV. JavaScript Execution Patterns
    WHAT WE WILL COVER

    View Slide

  4. JAVASCRIPT “BASICS”
    &
    “BEST PRACTICES”

    View Slide

  5. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    VARIABLE NAMING

    View Slide

  6. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ All JavaScript variables are camelCase
    ‎ Except for a class/constructor which are
    TitleCased
    ‎ jQuery objects prefixed with a $
    ‎ Variables are declared at the start of the scope,
    on their own line, and with their own var
    keyword
    VARIABLE NAMING

    View Slide

  7. var isReady = true;
    var clickHandler = function() {};
    var $timerContainer = $('.timer');
    var Timer = function() {
    this.init();
    };
    var timer = new Timer();

    View Slide

  8. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ANONYMOUS
    FUNCTIONS

    View Slide

  9. // Meh
    $('.row').each(function(){
    var $cols = $(this).find('.col');
    var maxHeight = 0;
    $cols.each(function(){
    maxHeight = Math.max($(this).height(), maxHeight);
    });
    $cols.height(maxHeight);
    });
    // Better
    var equalizeHeight = function(){
    var $cols = $(this).find('.col');
    var maxHeight = 0;
    var setMaxHeight = function(){
    maxHeight = Math.max($(this).height(), max_height);
    };
    $cols.each(setMaxHeight).height(maxHeight);
    };
    $('.row').each(equalizeHeight);

    View Slide

  10. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    FUNCTIONS

    View Slide

  11. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ Keep functions short and specific
    ‎ Use functions that return values
    FUNCTIONS

    View Slide

  12. note 1
    note 2

    View Slide

  13. var footnotes = {};
    var getHash = function($link) {
    return $link.attr('href').split('#')[1];
    };
    var getText = function($link) {
    return $link.data('footnote');
    };
    $('.footnote').each(function() {
    var $this = $(this);
    var key = getHash($this);
    var val = getText($this);
    footnotes[key] = val;
    });

    View Slide

  14. {
    footnote1: 'Here is the 1st footnote',
    footnote2: 'Here is the 2nd footnote'
    }

    View Slide

  15. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    OBJECT LITERAL

    View Slide

  16. var myObject = {
    stringProp: 'here is a string',
    numProp: 11,
    boolProp: true,
    myFunction: function() {
    // Do this thing
    }
    };

    View Slide

  17. myObject.numProp; // 11
    myObject['numProp']; // 11
    myObject.myFunction(); // Runs function

    View Slide

  18. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    OBJECT PROTOTYPE

    View Slide

  19. // Constructor
    var Person = function(name) {
    this.name = name;
    };
    // Prototype
    Person.prototype = {
    greet: function() {
    console.log('Hello ' + this.name);
    }
    };
    var bob = new Person('Robert');
    bob.greet(); // Hello Robert

    View Slide

  20. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    NAMESPACING

    View Slide

  21. window.SITE || (SITE = {});
    SITE = {
    init: function() {
    },
    anotherMethod: function() {
    }
    };

    View Slide

  22. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    CLOSURE

    View Slide

  23. ;(function($, window, document, undefined) {
    ...
    })(jQuery, window, document);

    View Slide

  24. ANATOMY OF A
    JQUERY PLUGIN

    View Slide

  25. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    EFFIN’

    View Slide

  26. $('.something').pluginName();

    View Slide

  27. $.fn.pluginName = function() {
    ...
    };

    View Slide

  28. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    CHAINABILITY

    View Slide

  29. $('.something').css('color', 'red');
    $('.something').show();
    $('.something').pluginName();

    View Slide

  30. $('.something').css('color', 'red').show().pluginName();

    View Slide

  31. $.fn.pluginName = function() {
    ...
    return this;
    };

    View Slide

  32. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    OPTIONS

    View Slide

  33. $('.something').pluginName({
    one: 'some thing',
    two: 'another thing'
    });

    View Slide

  34. $.fn.pluginName = function(options) {
    ...
    };

    View Slide

  35. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    DEFAULTS

    View Slide

  36. $.fn.pluginName.defaults = {
    one: 'some thing',
    two: 'another thing'
    };

    View Slide

  37. $.fn.pluginName = function(options) {
    var opts = $.extend({}, $.fn.pluginName.defaults, options);
    ...
    };

    View Slide

  38. // Defaults
    {
    one: 'some thing',
    two: 'another thing'
    }
    // Options
    {
    one: 'this new thing'
    }

    View Slide

  39. // Result after extend
    {
    one: 'this new thing',
    two: 'another thing'
    }

    View Slide

  40. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    MULTIPLE INSTANCES

    View Slide

  41. this.each(function() {
    ...
    });

    View Slide

  42. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    PUT IT ALL TOGETHER

    View Slide

  43. ;(function($, window, document, undefined) {
    $.fn.pluginName = function(options) {
    var opts = $.extend({}, $.fn.pluginName.defaults, options);
    this.each(function() {
    // Plugin code goes here
    });
    return this;
    };
    // Default options
    $.fn.pluginName.defaults = {
    one: 'some thing',
    two: 'another thing'
    };
    })(jQuery, window, document);

    View Slide

  44. JQUERY PLUGIN
    PATTERNS

    View Slide

  45. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    THE OLD WAY

    View Slide

  46. ;(function($, window, document, undefined) {
    $.fn.pluginName = function(options) {
    var opts = $.extend({}, $.fn.pluginName.defaults, options);
    this.each(function() {
    // Plugin code goes here
    });
    return this;
    };
    // Default options
    $.fn.pluginName.defaults = {
    one: 'some thing',
    two: 'another thing'
    };
    })(jQuery, window, document);

    View Slide

  47. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    A BETTER WAY

    View Slide

  48. // Plugin constructor
    var Plugin = function(elem, options){
    this.elem = elem;
    this.$elem = $(elem);
    this.options = options;
    this.metadata = this.$elem.data('plugin-options');
    };

    View Slide

  49. // Plugin prototype
    Plugin.prototype = {
    defaults: {
    one: 'some thing',
    two: 'another thing'
    },
    init: function() {
    this.config = $.extend({}, this.defaults, this.options, this.metadata);
    return this;
    }
    };

    View Slide

  50. $.fn.pluginName = function(options) {
    return this.each(function() {
    new Plugin(this, options).init();
    });
    };

    View Slide

  51. ;(function($, window, document, undefined) {
    // Plugin constructor
    var Plugin = function(elem, options) {
    this.elem = elem;
    this.$elem = $(elem);
    this.options = options;
    this.metadata = this.$elem.data('plugin-options');
    };
    // Plugin prototype
    Plugin.prototype = {
    defaults: {
    one: 'some thing',
    two: 'another thing'
    },
    init: function() {
    this.config = $.extend({}, this.defaults, this.options, this.metadata);
    return this;
    }
    // Plugin methods go here
    };
    Plugin.defaults = Plugin.prototype.defaults;
    $.fn.pluginName = function(options) {
    return this.each(function() {
    new Plugin(this, options).init();
    });
    };
    })(jQuery, window, document);

    View Slide

  52. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    WHY?

    View Slide

  53. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ More portable
    ‎ Easily make the object available on the window
    window.Plugin = Plugin;
    ‎ Better organization
    ‎ Logic is outside of the jQuery plugin
    ‎ Better customization
    ‎ Per element options
    WHY?

    View Slide

  54. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    AN EXAMPLE

    View Slide


  55. Tab 1
    Tab 2
    Tab 3
    Tab 4

    View Slide


  56. Tab 1
    Tab 2
    Tab 3
    Tab 4

    View Slide

  57. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    NO JQUERY PLUGIN

    View Slide

  58. ;(function($, window, document, undefined) {
    window.ListSelect = function(elem, options) {
    this.elem = elem;
    this.$elem = $(elem);
    this.options = options;
    this.$links = this.$elem.find('a');
    this.selectHTML = '';
    };
    ListSelect.prototype = {
    init: function() {
    this.$links.each($.proxy(this.buildSelect, this));
    this.selectHTML += '';
    this.$select = $(this.selectHTML);
    if(this.options['class']) {
    this.$select.addClass(this.options['class']);
    }
    this.$select.insertAfter(this.$elem);
    this.bindEvents();
    return this;
    },
    bindEvents: function() {
    this.$select.on('change.listSelect', this.redirect);
    },

    View Slide

  59. this.$select = $(this.selectHTML);
    if(this.options['class']) {
    this.$select.addClass(this.options['class']);
    }
    this.$select.insertAfter(this.$elem);
    this.bindEvents();
    return this;
    },
    bindEvents: function() {
    this.$select.on('change.listSelect', this.redirect);
    },
    buildSelect: function(index, el) {
    var $el = $(el);
    this.selectHTML += '' +
    $el.text() + '';
    },
    redirect: function() {
    window.location = $(this).val();
    }
    };
    })(jQuery, window, document);

    View Slide

  60. var tabSelect = new ListSelect($('.tabs'), {
    'class': 'tabs-select'
    }).init();

    View Slide

  61. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ Need a loop to handle multiple .tabs elements on
    a page
    ‎ Cluttering the global namespace
    ‎ You don’t have to make it a jQuery plugin to fix
    this
    SHORTCOMINGS

    View Slide

  62. $('.tabs').each(function() {
    var tabSelect = new ListSelect(this, {
    'class': 'tabs-select'
    }).init();
    });

    View Slide

  63. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    LET’S MAKE
    IT A PLUGIN

    View Slide

  64. ;(function($, window, document, undefined) {
    // Plugin constructor
    var ListSelect = function(elem, options) {
    this.elem = elem;
    this.$elem = $(elem);
    this.options = options;
    this.metadata = this.$elem.data('listSelect-options');
    this.$links = this.$elem.find('a');
    this.selectHTML = '';
    };
    // Plugin prototype
    ListSelect.prototype = {
    defaults: {
    'class': ''
    },
    init: function() {
    this.config = $.extend({}, this.defaults, this.options, this.metadata);
    this.$links.each($.proxy(this.buildSelect, this));
    this.selectHTML += '';
    this.$select = $(this.selectHTML);
    if(this.config['class']) {
    this.$select.addClass(this.config['class']);
    }
    this.$select.insertAfter(this.$elem);
    this.bindEvents();
    return this;
    },

    View Slide

  65. if(this.config['class']) {
    this.$select.addClass(this.config['class']);
    }
    this.$select.insertAfter(this.$elem);
    this.bindEvents();
    return this;
    },
    bindEvents: function() {
    this.$select.on('change.listSelect', this.redirect);
    },
    buildSelect: function(index, el) {
    var $el = $(el);
    this.selectHTML += '' + $el.text() +
    '';
    },
    redirect: function() {
    window.location = $(this).val();
    }
    };
    ListSelect.defaults = ListSelect.prototype.defaults;
    $.fn.listSelect = function(options) {
    return this.each(function() {
    new ListSelect(this, options).init();
    });
    };
    })(jQuery, window, document);

    View Slide

  66. $('.tabs').listSelect({
    'class': 'tabs-select'
    });

    View Slide

  67. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    PER ELEMENT
    OPTIONS

    View Slide


  68. Tab 1
    Tab 2
    Tab 3
    Tab 4

    View Slide

  69. $('.tabs').listSelect({
    'class': 'tabs-select'
    });

    View Slide


  70. Tab 1
    Tab 2
    Tab 3
    Tab 4

    View Slide

  71. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ Keep plugin functionality outside of jQuery
    instantiation
    ‎ Don’t be scared of using a lot of methods
    ‎ Namespace events
    ‎ Easy to make functionality available to jQuery
    plugins and for vanilla JS use
    SO WHAT HAVE WE LEARNED?

    View Slide

  72. JAVASCRIPT
    EXECUTION PATTERNS

    View Slide

  73. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    EVERYTHING IN
    DOCUMENT.READY

    View Slide

  74. $(document).ready(function() {
    $('.tabs').tabs();
    $('.carousel').carousel();
    $('.slider').slider();
    });

    View Slide

  75. URL or Source
    ARRESTEDSTILLS.TUMBLR.COM

    View Slide

  76. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    DOM BASED ROUTING

    View Slide


  77. View Slide

  78. SITE = {
    common: {
    init: function(){ ... },
    finalize: function(){ ... }
    },
    shopping: {
    init: function(){ ... },
    cart: function(){ ... },
    category: function(){ ... }
    }
    };

    View Slide

  79. SITE.common.init()
    SITE.shopping.init()
    SITE.shopping.cart()
    SITE.common.finalize()

    View Slide

  80. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    GARBER-IRISH
    IMPLEMENTATION

    View Slide


  81. View Slide

  82. SITE = {
    common: {
    init: function() {}
    },
    users: {
    init: function() {},
    index: function() {},
    show: function() {}
    }
    };

    View Slide

  83. SITE.common.init();
    SITE.users.init();
    SITE.users.show();

    View Slide

  84. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    FEATURE BASED
    EXECUTION

    View Slide

  85. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ More modular
    ‎ Feature specific instead of page specific
    ‎ Easier to add existing features to pages without
    having to touch JS
    FEATURE BASED EXECUTION

    View Slide


  86. View Slide

  87. SITE.features = {
    filters: {
    init: function() {}
    },
    modal: {
    init: function() {}
    },
    tabs: {
    init: function() {}
    },
    timeline: {
    init: function() {}
    }
    };

    View Slide

  88. SITE.features = {
    init: function() {
    var features = $('body').data('features');
    var featuresArray = [];
    if(features) {
    featuresArray = features.split(' ');
    for(var i = 0, length = featuresArray.length; i < length; i++) {
    var func = featuresArray[i];
    if(this[func] && typeof this[func].init === 'function') {
    this[func].init();
    }
    }
    }
    },
    ...
    };

    View Slide

  89. SITE = {
    init: function() {
    SITE.features.init();
    }
    };
    $(document).ready(SITE.init);

    View Slide

  90. SITE.features.timeline.init()
    SITE.features.tabs.init()
    SITE.features.filters.init()
    SITE.features.modal.init()

    View Slide

  91. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    FEATURE BASED
    EXECUTION +
    SCRIPT LOADER

    View Slide

  92. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ Break features into separate files
    ‎ Load each feature JS file with script loader
    ‎ Doesn’t load unused JS
    FEATURE BASED EXECUTION + SCRIPT LOADER

    View Slide

  93. yepnope([{
    load: 'scripts/features/' + func + '.js',
    complete: function () {
    if(this[func] && typeof this[func].init === 'function') {
    this[func].init();
    }
    }
    }]);

    View Slide

  94. SITE.features = {
    init: function() {
    var features = $('body').data('features');
    var featuresArray = [];
    if (features) {
    featuresArray = features.split(' ');
    for(var i = 0, length = featuresArray.length; i < length; i++) {
    var func = featuresArray[i];
    yepnope([{
    load: 'scripts/features/' + func + '.js',
    complete: function () {
    if(this[func] && typeof this[func].init === 'function') {
    this[func].init();
    }
    }
    }]);
    }
    }
    },
    ...
    };

    View Slide

  95. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    ‎ Don’t just haphazardly fire a bunch of JS
    functions that may or may not be used
    ‎ Come up with a pattern for triggering JS on each
    page that makes sense for your site
    WHAT DID WE LEARN?

    View Slide

  96. © Viget Labs, LLC • This presentation is CONFIDENTIAL and should not be shared without permission.
    jQuery Plugin Authoring
    Essential jQuery Plugin Patterns
    Markup-based Unobtrusive Comprehensive
    DOM-ready Execution
    Extending Paul Irish’s comprehensive DOM-
    ready execution
    : viget.com
    : trevordavis.net
    : [email protected]
    : @trevor_davis
    Thanks!
    Let’s Connect
    Resources

    View Slide