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

JavaScript For Non-Web Apps

JavaScript For Non-Web Apps

Avatar for Trevor Davis

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