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

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

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

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

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

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

    11, boolProp: true, myFunction: function() { // Do this thing } };
  15. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

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

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

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

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

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

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

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

    should not be shared without permission. DEFAULTS
  24. // Defaults { one: 'some thing', two: 'another thing' }

    // Options { one: 'this new thing' }
  25. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

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

    should not be shared without permission. PUT IT ALL TOGETHER
  27. ;(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);
  28. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

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

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

    elem; this.$elem = $(elem); this.options = options; this.metadata = this.$elem.data('plugin-options'); };
  32. // Plugin prototype Plugin.prototype = { defaults: { one: 'some

    thing', two: 'another thing' }, init: function() { this.config = $.extend({}, this.defaults, this.options, this.metadata); return this; } };
  33. ;(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);
  34. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

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

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

    should not be shared without permission. NO JQUERY PLUGIN
  38. ;(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); },
  39. 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);
  40. © 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
  41. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. LET’S MAKE IT A PLUGIN
  42. ;(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; },
  43. 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);
  44. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. PER ELEMENT OPTIONS
  45. © 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?
  46. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

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

    should not be shared without permission. DOM BASED ROUTING
  48. SITE = { common: { init: function(){ ... }, finalize:

    function(){ ... } }, shopping: { init: function(){ ... }, cart: function(){ ... }, category: function(){ ... } } };
  49. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. GARBER-IRISH IMPLEMENTATION
  50. SITE = { common: { init: function() {} }, users:

    { init: function() {}, index: function() {}, show: function() {} } };
  51. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

    should not be shared without permission. FEATURE BASED EXECUTION
  52. © 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
  53. SITE.features = { filters: { init: function() {} }, modal:

    { init: function() {} }, tabs: { init: function() {} }, timeline: { init: function() {} } };
  54. 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(); } } } }, ... };
  55. © Viget Labs, LLC • This presentation is CONFIDENTIAL and

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

    { if(this[func] && typeof this[func].init === 'function') { this[func].init(); } } }]);
  58. 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(); } } }]); } } }, ... };
  59. © 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?
  60. © 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