JavaScript Code Organizations, Patterns Slides - Zach Dennis

Post on 05-Dec-2014

4.655 views 3 download

description

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

Transcript of JavaScript Code Organizations, Patterns Slides - Zach Dennis

JAVASCRIPT CODE ORGANIZATION, PATTERNS

ZACH DENNIS

MUTUALLY HUMAN SOFTWARE

1Monday, March 28, 2011

INLINE JAVASCRIPTIS NOT SUSTAINABLE

<div class="presentation"> <a onclick="playPresentation()"> Play Presentation </a></div>

2Monday, March 28, 2011

POOR MODULARITY / REUSE

CODE MAINTENANCE PROBLEMS

LACK OF CACHING

DIFFICULTY SCALING TO LARGER APPS

3Monday, March 28, 2011

UNOBTRUSIVE JAVASCRIPTPATHWAY TO THE GREAT VALLEY

4Monday, March 28, 2011

2 TECHNIQUESSEPARATE CONTENT FROM BEHAVIOR,

REGISTER EVENT HANDLERS PROGRAMMATICALLY

5Monday, March 28, 2011

SEPARATE BEHAVIOR FROM CONTENT

<div class="presentation"> <a class="play"> Play Presentation </a></div>

$(“.presentation .play”).click(function(){// ...

});

HTML

JS

6Monday, March 28, 2011

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

7Monday, March 28, 2011

THE GREAT VALLEY EFFECT

8Monday, March 28, 2011

A BETTER VANTAGE POINT

SEE PATTERNS

CODE DECOMPOSITION

CODE ORGANIZATION

APPLY PATTERNS

9Monday, March 28, 2011

DRIVES SEMANTICSELECTOR DRIVEN CODE

10Monday, March 28, 2011

REINFORCE SEMANTIC MARKUP

$(“.presentation .play”).click(function(){// ...

}); JS

<div class="presentation"> <a class="play"> Play Presentation </a></div> HTML

.presentation .play { background-image: url(...); font-size: 1.em;} CSS

11Monday, March 28, 2011

SELECTOR DRIVEN CODE

IT CAN BE SCANNED AND RE-ORGANIZED MORE

EFFECTIVELY. IT’S EASIER TO VISUALIZE AND MANIPULATE.

12Monday, March 28, 2011

BUT THEN

13Monday, March 28, 2011

$(".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

14Monday, March 28, 2011

MONOLITHIC JAVASCRIPTFORMED OF A SINGLE LARGE FILE.

15Monday, March 28, 2011

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

16Monday, March 28, 2011

PATTERNS FOR AVOIDING MONOLITHS

TRADITIONAL CLASS-BASED OO

FUNCTIONS, CLOSURES

EVENT-DRIVEN JAVASCRIPT

17Monday, March 28, 2011

TRADITIONAL CLASS-BASED OOAPPLYING TRIED AND TRUE TECHNIQUES WITH JAVASCRIPT

18Monday, March 28, 2011

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

19Monday, March 28, 2011

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

20Monday, March 28, 2011

var el = $(“.presentation:first”);

var prez = new Presentation(prez);

prez.play();

prez.stop();

JQUERY

USING THE CLASS

21Monday, March 28, 2011

BENEFITS

DATA/BEHAVIOR ENCAPSULATION

CLEAR BOUNDARIES OF RESPONSIBILITIES

API DEFINITION

MODULAR / REUSABLE CODE

22Monday, March 28, 2011

ASSIGN PSEUDO-PRIVATE VARIABLE

var Presentation = $.Class.create({

initialize: function(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(){ // ... }, stop: function(){ // ... },});

this._element = element;

this._element.addClass(“playing”);

JQUERY

23Monday, March 28, 2011

var Presentation = $.Class.create({

initialize: function(element) { this._element = element;

this.play.bind(“click”, $.proxy(this.play, this)); this.stop.bind(“click”, $.proxy(this.stop, this)); }, play: function(){ this._element.addClass(“playing”); // ... }, stop: function(){ // ... },});

this.play = element.find(“.play”);this.stop = element.find(“.stop”);

FIND AND ASSIGN ELEMENTS WE NEED ACCESS TO

JQUERY

24Monday, March 28, 2011

var Presentation = $.Class.create({

initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”);

}, play: function(){

this._element.addClass(“playing”); // ... }, stop: function(){ // ...

this.play.bind(“click”, $.proxy(this.play, this));this.stop.bind(“click”, $.proxy(this.stop, this));

JQUERY

REGISTER EVENT HANDLERS

25Monday, March 28, 2011

var Presentation = $.Class.create({

initialize: function(element) { this._element = element; this.play = element.find(“.play”); this.stop = element.find(“.stop”);

}, play: function(){

._element.addClass(“playing”); // ... }, stop: function(){ // ...

this.play.bind(“click”, );this.stop.bind(“click”, );

JQUERY

KEEP REFERENCE TO THIS

$.proxy(this.play, this);$.proxy(this.stop, this)

this

26Monday, March 28, 2011

FEELS A LITTLE HEAVYFEELS A LITTLE AWKWARD

27Monday, March 28, 2011

SAME EXAMPLE,DIFFERENT PATTERN

28Monday, March 28, 2011

FUNCTIONS, CLOSURESTAKING ADVANTAGE OF JAVASCRIPT

29Monday, March 28, 2011

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

FUNCTIONS, SCOPE, CLOSURES

30Monday, March 28, 2011

var el = $(“.presentation:first”);

var prez = Presentation(prez);

prez.play();

prez.stop();

JQUERY

ONLY DIFFERENCE, NO “NEW”

31Monday, March 28, 2011

var Presentation = function(element){ presentation.delegate(".play", "click", play); presentation.delegate(".stop", "click", stop);

function play(){ // ... } function stop(){ // ... } return { play: play, stop: stop }}; JQUERY

var presentation = element;

ACCESSIBLE PRIVATE VARIABLES

presentation.addClass(“playing”);

32Monday, March 28, 2011

var Presentation = function(element){ var presentation = element;

function play(){ presentation.addClass(“playing”); // ... } function stop(){ // ... } return { play: play, stop: stop }}; JQUERY

STRAIGHT FORWARD EVENT DELEGATION

presentation.delegate(".play", "click", play);presentation.delegate(".stop", "click", stop);

33Monday, March 28, 2011

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 {

};}; JQUERY

API DEFINITION

play: play,stop: stop

34Monday, March 28, 2011

FEELS MORE LIKE HOME

35Monday, March 28, 2011

WHAT IF WE DIDN’T CARE ABOUT A

PUBLIC API?

36Monday, March 28, 2011

(function(){ $(".presentation").delegate(".play", "click", play); $(".presentation").delegate(".stop", "click", stop);

function play(){ $(this).parents(“.presentation:first”).addClass(“playing”); // ... } function stop(){ // ... }})();

SHORTER. SIMPLER. SWEETER.

JQUERY

37Monday, March 28, 2011

CREATED A FUNCTION, EXECUTED IT

REMOVE UNNECESSARY VARS

RELY ON SELECTOR-DRIVEN EVENT HANDLERS

NO NEED TO MANUALLY BIND TO ELEMENTS

WHAT JUST HAPPENED?

38Monday, March 28, 2011

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: playPresentation, stop: stopPresentation }});

function(){ $(".presentation").delegate(".play", "click", play); $(".presentation").delegate(".stop", "click", stop);

function play(){ $(this).parents(“.presentation:first”).addClass(“playing”); // ... } function stop(){ // ... }}();

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

CLASS-BASED FUNCTION, CLOSURES EVENT-DRIVEN

39Monday, March 28, 2011

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

40Monday, March 28, 2011

ONE STEP FURTHER

41Monday, March 28, 2011

EVENT-DRIVEN JAVASCRIPTDECLARATIVELY READABLE SELECTOR DRIVEN CODE

42Monday, March 28, 2011

6 GUIDELINES

43Monday, March 28, 2011

MEANINGFUL FILE NAMES TO ORGANIZE BEHAVIOR

PRESENTER.JS

VIEWER.JS

SLIDES/

PRESENTER/

REMOTE-CONTROLS.JS

VIEWER/

RESIZING.JS

REMOTE-CONTROLS.JS

GRID.JS

*USE COMBINATOR, COMPRESSOR, MINIFIER FOR DEPLOYMENT

44Monday, March 28, 2011

STRUCTURE RELIES ON FUNCTION SCOPE

(function(){

})(); MODULE

$(function(){

}); MODULE JQUERY

EXECUTES IMMEDIATELY

EXECUTES AFTER DOM READY

JS

45Monday, March 28, 2011

DECLARE PAGE CHECKS FIRST

$(function(){ });

JQUERY

if(!$("html.presenter").length) return;PRESENTER.JS

46Monday, March 28, 2011

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

JQUERY

PRESENTER.JS

47Monday, March 28, 2011

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(){ // ...

JQUERY

PRESENTER.JS

48Monday, March 28, 2011

QUICKER TO SCAN. KEEPING DECLARATIONS ABOVE

FUNCTION DEFINITIONS CREATES MORE

READABLE CODE.

49Monday, March 28, 2011

EVENTS DRIVE CROSS-CLOSURE COMMUNICATION

function nextSlide(){ var prez = $(this).parents('.presentation:first'); // ...

current_slide.removeClass('current'); next_slide.addClass('current');

};prez.trigger('slide:changed', { slide: next_slide });

$("body").delegate(".presentation", "slide:changed", transitionToSlide);

THUMBNAIL-CONTROLS.JS

PRESENTER.JS

$("body").delegate(".presentation", "slide:changed", changeSlideOnRemoteViewers);

REMOTE-VIEWER-CONTROLS.JS

_ _ _

50Monday, March 28, 2011

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

51Monday, March 28, 2011

52Monday, March 28, 2011

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

53Monday, March 28, 2011

54Monday, March 28, 2011

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

55Monday, March 28, 2011