Successfully reported this slideshow.
Your SlideShare is downloading. ×

JavaScript Code Organizations, Patterns Slides - Zach Dennis

Ad

JAVASCRIPT CODE
              ORGANIZATION, PATTERNS
                               ZACH DENNIS
                         M...

Ad

<div class="presentation">
                           <a onclick="playPresentation()">
                             Play P...

Ad

POOR MODULARITY / REUSE


                    CODE MAINTENANCE PROBLEMS


                    LACK OF CACHING


          ...

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Upcoming SlideShare
Action bar
Action bar
Loading in …3
×

Check these out next

1 of 55 Ad
1 of 55 Ad

JavaScript Code Organizations, Patterns Slides - Zach Dennis

Download to read offline

Slides from the "JavaScript Code Organizations, Pattern" talk by Zach Dennis from Mutually Human Software at the
GRWebDev meeting on March 28th, 2011

Slides from the "JavaScript Code Organizations, Pattern" talk by Zach Dennis from Mutually Human Software at the
GRWebDev meeting on March 28th, 2011

More Related Content

JavaScript Code Organizations, Patterns Slides - Zach Dennis

  1. 1. JAVASCRIPT CODE ORGANIZATION, PATTERNS ZACH DENNIS MUTUALLY HUMAN SOFTWARE Monday, March 28, 2011 1
  2. 2. <div class="presentation"> <a onclick="playPresentation()"> Play Presentation </a> </div> INLINE JAVASCRIPT IS NOT SUSTAINABLE Monday, March 28, 2011 2
  3. 3. POOR MODULARITY / REUSE CODE MAINTENANCE PROBLEMS LACK OF CACHING DIFFICULTY SCALING TO LARGER APPS Monday, March 28, 2011 3
  4. 4. UNOBTRUSIVE JAVASCRIPT PATHWAY TO THE GREAT VALLEY Monday, March 28, 2011 4
  5. 5. 2 TECHNIQUES SEPARATE CONTENT FROM BEHAVIOR, REGISTER EVENT HANDLERS PROGRAMMATICALLY Monday, March 28, 2011 5
  6. 6. SEPARATE BEHAVIOR FROM CONTENT <div class="presentation"> <a class="play"> Play Presentation </a> </div> HTML $(“.presentation .play”).click(function(){ // ... }); JS Monday, March 28, 2011 6
  7. 7. REGISTER HANDLERS PROGRAMMATICALLY $(“.presentation .play”).click(function(){ // ... }); JQUERY $(“.presentation .play”).addEvent(“click”, function(){ // ... }); MOOTOOLS var el = dojo.query(“.presentation .play”); el.connect(“click”, function(){ // ... }); DOJO Monday, March 28, 2011 7
  8. 8. THE GREAT VALLEY EFFECT Monday, March 28, 2011 8
  9. 9. A BETTER VANTAGE POINT SEE PATTERNS CODE DECOMPOSITION CODE ORGANIZATION APPLY PATTERNS Monday, March 28, 2011 9
  10. 10. DRIVES SEMANTIC SELECTOR DRIVEN CODE Monday, March 28, 2011 10
  11. 11. REINFORCE SEMANTIC MARKUP <div class="presentation"> <a class="play"> Play Presentation </a> </div> HTML $(“.presentation .play”).click(function(){ // ... }); JS .presentation .play { background-image: url(...); font-size: 1.em; } CSS Monday, March 28, 2011 11
  12. 12. SELECTOR DRIVEN CODE IT CAN BE SCANNED AND RE-ORGANIZED MORE EFFECTIVELY. IT’S EASIER TO VISUALIZE AND MANIPULATE. Monday, March 28, 2011 12
  13. 13. BUT THEN Monday, March 28, 2011 13
  14. 14. $(".presentation .play").click(function(){ var presentation = $(this).parents('.presentation:first'); presentation.addClass('playing'); selectSlide(presentation.find(".slides:first")); }); $(".presentation .stop").click(function(){ $(this).parents('.deck:first').addClass('stopping'); }); $('.presentation a.destroy').live('ajax:success', function(data){ var deck = $(this).parents('.deck:first'); deck.fadeOut('fast', deck.remove); }); $('#grid .slide a:last').click(function(){ selectSlide($(this).parents(".slide:first")); return false; }); $('img.slide').live("slide:loaded", function(){ resizeSlide($(this)); }); // any time the window resizes, resize the main slide $(window).resize(function(){ resizeSlide($(".slide_viewer img.slide")); }); function resizeSlide(img) { var viewer_width = $('.slide_viewer').width(); var viewer_height = $('.slide_viewer').height(); // Use original width and height since the image may be scaled // down to a smaller size, and we want to use the original size to scale // the image rather than the size it is currently scaled to var slide_width = img.data('original_width'); var slide_height = img.data('original_height'); if(slide_width > viewer_width){ ratio = viewer_width / slide_width; $('.slide_viewer img.slide').css({width: viewer_width, height: slide_height * ratio}); } } page 1 of 22 Monday, March 28, 2011 14
  15. 15. MONOLITHIC JAVASCRIPT FORMED OF A SINGLE LARGE FILE. Monday, March 28, 2011 15
  16. 16. CODE MONOLITHS GREAT FOR DEPLOYMENT BAD FOR DEVELOPMENT LOSES CONTEXT, HIERARCHY, SCOPE VISUALLY HARD TO SCAN CODE WITH DIFFERENT BEHAVIORS OR REASONS TO EXIST, CO-EXIST EXCEPT VERY SMALL SITES / APPS Monday, March 28, 2011 16
  17. 17. PATTERNS FOR AVOIDING MONOLITHS TRADITIONAL CLASS-BASED OO FUNCTIONS, CLOSURES EVENT-DRIVEN JAVASCRIPT Monday, March 28, 2011 17
  18. 18. TRADITIONAL CLASS-BASED OO APPLYING TRIED AND TRUE TECHNIQUES WITH JAVASCRIPT Monday, March 28, 2011 18
  19. 19. function Presentation(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }; Presentation.prototype.play = function() { this._element.addClass(“playing”); // ... }; Presentation.prototype.stop = function(hours) { // ... }; JS Monday, March 28, 2011 19
  20. 20. var Presentation = $.Class.create({ initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... }, }); JQUERY Monday, March 28, 2011 20
  21. 21. USING THE CLASS var el = $(“.presentation:first”); var prez = new Presentation(prez); prez.play(); prez.stop(); JQUERY Monday, March 28, 2011 21
  22. 22. BENEFITS DATA/BEHAVIOR ENCAPSULATION CLEAR BOUNDARIES OF RESPONSIBILITIES API DEFINITION MODULAR / REUSABLE CODE Monday, March 28, 2011 22
  23. 23. ASSIGN PSEUDO-PRIVATE VARIABLE var Presentation = $.Class.create({ initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... }, }); JQUERY Monday, March 28, 2011 23
  24. 24. FIND AND ASSIGN ELEMENTS WE NEED ACCESS TO var Presentation = $.Class.create({ initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... }, }); JQUERY Monday, March 28, 2011 24
  25. 25. REGISTER EVENT HANDLERS var Presentation = $.Class.create({ initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... JQUERY Monday, March 28, 2011 25
  26. 26. KEEP REFERENCE TO THIS var Presentation = $.Class.create({ initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”); this.play.bind(“click”, $.proxy(this.play, this); ); this.stop.bind(“click”, $.proxy(this.stop, this) ); }, play: function(){ this ._element.addClass(“playing”); // ... }, stop: function(){ // ... JQUERY Monday, March 28, 2011 26
  27. 27. FEELS A LITTLE HEAVY FEELS A LITTLE AWKWARD Monday, March 28, 2011 27
  28. 28. SAME EXAMPLE, DIFFERENT PATTERN Monday, March 28, 2011 28
  29. 29. FUNCTIONS, CLOSURES TAKING ADVANTAGE OF JAVASCRIPT Monday, March 28, 2011 29
  30. 30. FUNCTIONS, SCOPE, CLOSURES var Presentation = function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop); function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: play, stop: stop } }; JQUERY Monday, March 28, 2011 30
  31. 31. ONLY DIFFERENCE, NO “NEW” var el = $(“.presentation:first”); var prez = Presentation(prez); prez.play(); prez.stop(); JQUERY Monday, March 28, 2011 31
  32. 32. ACCESSIBLE PRIVATE VARIABLES var Presentation = function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop); function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: play, stop: stop } }; JQUERY Monday, March 28, 2011 32
  33. 33. STRAIGHT FORWARD EVENT DELEGATION var Presentation = function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop); function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: play, stop: stop } }; JQUERY Monday, March 28, 2011 33
  34. 34. API DEFINITION var Presentation = function(element){ var presentation = element; presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop); function play(){ presentation.addClass(“playing”); // ... }; function stop(){ // ... }; return { play: play, stop: stop }; }; JQUERY Monday, March 28, 2011 34
  35. 35. FEELS MORE LIKE HOME Monday, March 28, 2011 35
  36. 36. WHAT IF WE DIDN’T CARE ABOUT A PUBLIC API? Monday, March 28, 2011 36
  37. 37. SHORTER. SIMPLER. SWEETER. (function(){ $(".presentation").delegate(".play", "click", play); $(".presentation").delegate(".stop", "click", stop); function play(){ $(this).parents(“.presentation:first”).addClass(“playing”); // ... } function stop(){ // ... } })(); JQUERY Monday, March 28, 2011 37
  38. 38. WHAT JUST HAPPENED? CREATED A FUNCTION, EXECUTED IT REMOVE UNNECESSARY VARS RELY ON SELECTOR-DRIVEN EVENT HANDLERS NO NEED TO MANUALLY BIND TO ELEMENTS Monday, March 28, 2011 38
  39. 39. CLASS-BASED FUNCTION, CLOSURES EVENT-DRIVEN var Presentation = $.Class.create({ var Presentation = (function(element){ function(){ var presentation = element; $(".presentation").delegate(".play", "click", play); initialize: function(element) { presentation.delegate(".play", "click", play); $(".presentation").delegate(".stop", "click", stop); this._element = element; presentation.delegate(".stop", "click", stop); this.play = element.find(“.play”); function play(){ this.stop = element.find(“.stop”); function play(){ $(this).parents(“.presentation:first”).addClass(“playing”); this.play.bind(“click”, $.proxy(this.play, this)); presentation.addClass(“playing”); // ... this.stop.bind(“click”, $.proxy(this.stop, this)); // ... } }, } function stop(){ play: function(){ function stop(){ // ... this._element.addClass(“playing”); // ... } // ... } }(); }, return { stop: function(){ play: playPresentation, // ... stop: stopPresentation } } }); }); Monday, March 28, 2011 39
  40. 40. FUNCTIONS, CLOSURES RECAP USE FUNCTIONS AND CLOSURES TO CREATE SCOPE PRESERVE PRIVATE METHODS AND PROPERTIES WITH VAR STATEMENTS RETURN PUBLIC METHODS, PROPERTIES (OPTIONAL) AND WE DON’T POLLUTE GLOBAL NAMESPACE Monday, March 28, 2011 40
  41. 41. ONE STEP FURTHER Monday, March 28, 2011 41
  42. 42. EVENT-DRIVEN JAVASCRIPT DECLARATIVELY READABLE SELECTOR DRIVEN CODE Monday, March 28, 2011 42
  43. 43. 6 GUIDELINES Monday, March 28, 2011 43
  44. 44. MEANINGFUL FILE NAMES TO ORGANIZE BEHAVIOR PRESENTER.JS PRESENTER/ GRID.JS REMOTE-CONTROLS.JS VIEWER.JS VIEWER/ REMOTE-CONTROLS.JS SLIDES/ RESIZING.JS *USE COMBINATOR, COMPRESSOR, MINIFIER FOR DEPLOYMENT Monday, March 28, 2011 44
  45. 45. STRUCTURE RELIES ON FUNCTION SCOPE EXECUTES IMMEDIATELY (function(){ })(); JS MODULE EXECUTES AFTER DOM READY $(function(){ }); JQUERY MODULE Monday, March 28, 2011 45
  46. 46. DECLARE PAGE CHECKS FIRST $(function(){ if(!$("html.presenter").length) return; PRESENTER.JS }); JQUERY Monday, March 28, 2011 46
  47. 47. DECLARE HANDLERS AND VARS SECOND $(function(){ if(!$("html.presenter").length) return; var presentation = $(“.presentation”), attendee_count = 0; presentation.delegate(“.play”, “click”, play); presentation.delegate(“.stop”, “click”, stop); presentation.delegate(“.slide .next”, “click”, nextSlide); presentation.delegate(“.slide .prev”, “click”, prevSlide); }); PRESENTER.JS JQUERY Monday, March 28, 2011 47
  48. 48. DECLARE FUNCTIONS LAST $(function(){ if(!$("html.presenter").length) return; var presentation = $(“.presentation”), attendee_count = 0; presentation.delegate(“.play”, “click”, play); presentation.delegate(“.stop”, “click”, stop); presentation.delegate(“.slide .next”, “click”, nextSlide); presentation.delegate(“.slide .prev”, “click”, prevSlide); function play(){ // ... }; function stop(){ PRESENTER.JS // ... JQUERY Monday, March 28, 2011 48
  49. 49. QUICKER TO SCAN. KEEPING DECLARATIONS ABOVE FUNCTION DEFINITIONS CREATES MORE READABLE CODE. Monday, March 28, 2011 49
  50. 50. EVENTS DRIVE CROSS-CLOSURE COMMUNICATION PRESENTER.JS function nextSlide(){ var prez = $(this).parents('.presentation:first'); _ // ... _ _ current_slide.removeClass('current'); next_slide.addClass('current'); prez.trigger('slide:changed', { slide: next_slide }); }; THUMBNAIL-CONTROLS.JS $("body").delegate(".presentation", "slide:changed", transitionToSlide); REMOTE-VIEWER-CONTROLS.JS $("body").delegate(".presentation", "slide:changed", changeSlideOnRemoteViewers); Monday, March 28, 2011 50
  51. 51. BENEFITS FUNCTIONS AND CLOSURES ALLOW GROUPING OF COMMON BEHAVIOR AND DATA CUSTOM EVENTS ARE AWESOME NO NEED TO HAVE REFERENCES TO EXTERNAL OBJECTS THROUGHOUT OUR APP LOOSER COUPLING EASIER TO HOOK IN NEW PARTS OF OUR APP WITH MINIMAL IMPACT TO EXISTING CODE Monday, March 28, 2011 51
  52. 52. Monday, March 28, 2011 52
  53. 53. EVENT-DRIVEN HOW WE GOT THERE MEANINGFUL DIRECTORY AND FILE NAMES FOLLOW MODULE OR SIMILAR CLOSURE-PATTERN GENERAL GUIDELINES FOR READABILITY: DECLARE PAGE CHECKS FIRST DECLARE HANDLERS AND VARS SECOND DECLARE FUNCTIONS LAST USE EVENTS TO DRIVE CROSS-CLOSURE COMMUNICATION Monday, March 28, 2011 53
  54. 54. Monday, March 28, 2011 54
  55. 55. IN SUMMARY INLINE JAVASCRIPT IS NOT A SUSTAINABLE APPROACH. UNOBTRUSIVE JAVASCRIPT IS A PATH TO “THE GREAT VALLEY” MONOLITHIC UJS WORKS FINE FOR SMALL APPS, BUT DOESN’T SCALE WELL. FORTUNATELY SELECTOR-DRIVEN JS IS EASIER TO MANIPULATE AND REORGANIZE. TRADITIONAL CLASS-BASED OO + UJS WORKS WELL, BUT CAN AT TIMES BE A BIT HEAVY FUNCTION/CLOSURES ARE A LIGHTER WEIGHT APPROACH THAN TRADITIONAL OO. FEELS MORE JAVASCRIPTY. EVENT-DRIVEN APPROACH WITH EMPHASIS ON FUNCTION/CLOSURES FOR SCOPE AND DECLARATIVE SELECTOR-DRIVEN CODE IS LEANER, SCALABLE AND PROMOTES LOOSE COOUPLING Monday, March 28, 2011 55

×