[2024]Digital Global Overview Report 2024 Meltwater.pdf
JavaScript Code Organizations, Patterns Slides - Zach Dennis
1. JAVASCRIPT CODE
ORGANIZATION, PATTERNS
ZACH DENNIS
MUTUALLY HUMAN SOFTWARE
Monday, March 28, 2011 1
2. <div class="presentation">
<a onclick="playPresentation()">
Play Presentation
</a>
</div>
INLINE JAVASCRIPT
IS NOT SUSTAINABLE
Monday, March 28, 2011 2
3. POOR MODULARITY / REUSE
CODE MAINTENANCE PROBLEMS
LACK OF CACHING
DIFFICULTY SCALING TO LARGER APPS
Monday, March 28, 2011 3
5. 2 TECHNIQUES
SEPARATE CONTENT FROM BEHAVIOR,
REGISTER EVENT HANDLERS PROGRAMMATICALLY
Monday, March 28, 2011 5
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
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
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. PATTERNS FOR AVOIDING
MONOLITHS
TRADITIONAL CLASS-BASED OO
FUNCTIONS, CLOSURES
EVENT-DRIVEN JAVASCRIPT
Monday, March 28, 2011 17
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. FEELS A LITTLE HEAVY
FEELS A LITTLE AWKWARD
Monday, March 28, 2011 27
28. SAME EXAMPLE,
DIFFERENT PATTERN
Monday, March 28, 2011 28
29. FUNCTIONS, CLOSURES
TAKING ADVANTAGE OF JAVASCRIPT
Monday, March 28, 2011 29
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. ONLY DIFFERENCE, NO “NEW”
var el = $(“.presentation:first”);
var prez = Presentation(prez);
prez.play();
prez.stop();
JQUERY
Monday, March 28, 2011 31
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. 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. 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
36. WHAT IF WE DIDN’T
CARE ABOUT A
PUBLIC API?
Monday, March 28, 2011 36
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. 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
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
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. STRUCTURE RELIES ON
FUNCTION SCOPE
EXECUTES IMMEDIATELY
(function(){
})();
JS MODULE
EXECUTES AFTER DOM READY
$(function(){
});
JQUERY MODULE
Monday, March 28, 2011 45
46. DECLARE PAGE CHECKS FIRST
$(function(){
if(!$("html.presenter").length) return;
PRESENTER.JS
});
JQUERY
Monday, March 28, 2011 46
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. 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. QUICKER TO SCAN. KEEPING
DECLARATIONS ABOVE
FUNCTION DEFINITIONS
CREATES MORE
READABLE CODE.
Monday, March 28, 2011 49
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. 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
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
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