JavaScript For Non-Web Apps

JavaScript For Non-Web Apps

98e7524451cd8ec1bf8f644d2c2f01c9?s=128

Trevor Davis

April 18, 2013
Tweet

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
  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
  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
  4. JAVASCRIPT “BASICS” & “BEST PRACTICES”

  5. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. VARIABLE NAMING
  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
  7. var isReady = true; var clickHandler = function() {}; var

    $timerContainer = $('.timer'); var Timer = function() { this.init(); }; var timer = new Timer();
  8. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. ANONYMOUS FUNCTIONS
  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);
  10. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. FUNCTIONS
  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
  12. <a href="#footnote1" class="footnote" data-footnote="Here is the 1st footnote">note 1</a> <a

    href="#footnote2" class="footnote" data-footnote="Here is the 2nd footnote">note 2</a>
  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; });
  14. { footnote1: 'Here is the 1st footnote', footnote2: 'Here is

    the 2nd footnote' }
  15. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. OBJECT LITERAL
  16. var myObject = { stringProp: 'here is a string', numProp:

    11, boolProp: true, myFunction: function() { // Do this thing } };
  17. myObject.numProp; // 11 myObject['numProp']; // 11 myObject.myFunction(); // Runs function

  18. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. OBJECT PROTOTYPE
  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
  20. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. NAMESPACING
  21. window.SITE || (SITE = {}); SITE = { init: function()

    { }, anotherMethod: function() { } };
  22. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. CLOSURE
  23. ;(function($, window, document, undefined) { ... })(jQuery, window, document);

  24. ANATOMY OF A JQUERY PLUGIN

  25. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. EFFIN’
  26. $('.something').pluginName();

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

  28. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. CHAINABILITY
  29. $('.something').css('color', 'red'); $('.something').show(); $('.something').pluginName();

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

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

  32. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. OPTIONS
  33. $('.something').pluginName({ one: 'some thing', two: 'another thing' });

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

  35. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. DEFAULTS
  36. $.fn.pluginName.defaults = { one: 'some thing', two: 'another thing' };

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

    ... };
  38. // Defaults { one: 'some thing', two: 'another thing' }

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

    'another thing' }
  40. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. MULTIPLE INSTANCES
  41. this.each(function() { ... });

  42. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. PUT IT ALL TOGETHER
  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);
  44. JQUERY PLUGIN PATTERNS

  45. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. THE OLD WAY
  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);
  47. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. A BETTER WAY
  48. // Plugin constructor var Plugin = function(elem, options){ this.elem =

    elem; this.$elem = $(elem); this.options = options; this.metadata = this.$elem.data('plugin-options'); };
  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; } };
  50. $.fn.pluginName = function(options) { return this.each(function() { new Plugin(this, options).init();

    }); };
  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);
  52. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. WHY?
  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?
  54. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. AN EXAMPLE
  55. <ul class="tabs"> <li><a href="/about">Tab 1</a></li> <li><a href="/work">Tab 2</a></li> <li><a href="/blog">Tab

    3</a></li> <li><a href="/contact">Tab 4</a></li> </ul>
  56. <select class="tabs-select"> <option value="/about">Tab 1</option> <option value="/work">Tab 2</option> <option value="/blog">Tab

    3</option> <option value="/contact">Tab 4</option> </select>
  57. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. NO JQUERY PLUGIN
  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 = '<select>'; }; ListSelect.prototype = { init: function() { this.$links.each($.proxy(this.buildSelect, this)); this.selectHTML += '</select>'; 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); },
  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 += '<option value="' + $el.attr('href') + '">' + $el.text() + '</option>'; }, redirect: function() { window.location = $(this).val(); } }; })(jQuery, window, document);
  60. var tabSelect = new ListSelect($('.tabs'), { 'class': 'tabs-select' }).init();

  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
  62. $('.tabs').each(function() { var tabSelect = new ListSelect(this, { 'class': 'tabs-select'

    }).init(); });
  63. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. LET’S MAKE IT A PLUGIN
  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 = '<select>'; }; // 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 += '</select>'; this.$select = $(this.selectHTML); if(this.config['class']) { this.$select.addClass(this.config['class']); } this.$select.insertAfter(this.$elem); this.bindEvents(); return this; },
  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 += '<option value="' + $el.attr('href') + '">' + $el.text() + '</option>'; }, 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);
  66. $('.tabs').listSelect({ 'class': 'tabs-select' });

  67. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. PER ELEMENT OPTIONS
  68. <ul class="tabs" data-listSelect-options='{"class":"new-class"}'> <li><a href="/about">Tab 1</a></li> <li><a href="/work">Tab 2</a></li> <li><a

    href="/blog">Tab 3</a></li> <li><a href="/contact">Tab 4</a></li> </ul>
  69. $('.tabs').listSelect({ 'class': 'tabs-select' });

  70. <select class="new-class"> <option value="/about">Tab 1</option> <option value="/work">Tab 2</option> <option value="/blog">Tab

    3</option> <option value="/contact">Tab 4</option> </select>
  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?
  72. JAVASCRIPT EXECUTION PATTERNS

  73. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. EVERYTHING IN DOCUMENT.READY
  74. $(document).ready(function() { $('.tabs').tabs(); $('.carousel').carousel(); $('.slider').slider(); });

  75. URL or Source ARRESTEDSTILLS.TUMBLR.COM

  76. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. DOM BASED ROUTING
  77. <body class="shopping" id="cart">

  78. SITE = { common: { init: function(){ ... }, finalize:

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

  80. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. GARBER-IRISH IMPLEMENTATION
  81. <body data-controller="users" data-action="show">

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

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

  84. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. FEATURE BASED EXECUTION
  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
  86. <body data-features="timeline tabs filters modal">

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

    { init: function() {} }, tabs: { init: function() {} }, timeline: { init: function() {} } };
  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(); } } } }, ... };
  89. SITE = { init: function() { SITE.features.init(); } }; $(document).ready(SITE.init);

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

  91. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. FEATURE BASED EXECUTION + SCRIPT LOADER
  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
  93. yepnope([{ load: 'scripts/features/' + func + '.js', complete: function ()

    { if(this[func] && typeof this[func].init === 'function') { this[func].init(); } } }]);
  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(); } } }]); } } }, ... };
  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?
  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 : trevor.davis@viget.com : @trevor_davis Thanks! Let’s Connect Resources