Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

© 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

Slide 3

Slide 3 text

© 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

Slide 4

Slide 4 text

JAVASCRIPT “BASICS” & “BEST PRACTICES”

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

© 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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

// 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);

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

© 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

Slide 12

Slide 12 text

Slide 13

Slide 13 text

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; });

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

// 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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

ANATOMY OF A JQUERY PLUGIN

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

;(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);

Slide 44

Slide 44 text

JQUERY PLUGIN PATTERNS

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

;(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);

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

;(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);

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

© 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?

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

Slide 56

Slide 56 text

Tab 1 Tab 2 Tab 3 Tab 4

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

;(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); },

Slide 59

Slide 59 text

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);

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

© 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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

;(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; },

Slide 65

Slide 65 text

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);

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Tab 1 Tab 2 Tab 3 Tab 4

Slide 71

Slide 71 text

© 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?

Slide 72

Slide 72 text

JAVASCRIPT EXECUTION PATTERNS

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

URL or Source ARRESTEDSTILLS.TUMBLR.COM

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

© 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

Slide 86

Slide 86 text

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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(); } } } }, ... };

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

© 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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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(); } } }]); } } }, ... };

Slide 95

Slide 95 text

© 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?

Slide 96

Slide 96 text

© 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