Slide 1

Slide 1 text

sneaking structure into your DOM-based application 1 Monday, November 14, 2011

Slide 2

Slide 2 text

ohai ☛ Garann Means ☛ Austin All-Girl Hack Night ☛ Girl Develop It Austin ☛ @garannm / garann.com 2 Monday, November 14, 2011

Slide 3

Slide 3 text

$(document).ready(function() { ! $.get("pages/title.html",function(r) { ! ! $(".page_text").html(r); ! }, "html"); ! setInterval(function() { ! ! var currentpage = parseInt($("#currentpage").val()), ! ! ! num = window.location.hash.substring(1) || 0; ! ! if (currentpage != num) { ! ! ! goToPage(num); ! ! } ! }, 100); ! $(".continue a").click(function(e) { ! ! e.preventDefault(); ! ! goToPage(parseInt($("#currentpage").val())+1); ! });! ! $(".choose a").click(function(e) { ! ! e.preventDefault(); ! ! var newpage = $(this).attr("rel"); ! ! goToPage(newpage); ! });! ! $(document).bind("keydown",function(e) {! ! ! ! clearTimeout(typingPause);! ! ! ! var currentpage = parseInt($("#currentpage").val()); ! ! var k = keys[e.keyCode]; ! ! if (k == "left") { ! ! ! currentpage--; ! ! ! goToPage(currentpage); ! ! } else if (k == "right") { ! ! ! currentpage++; ! ! ! goToPage(currentpage); ! ! } else if (k) { ! ! ! newpage += "" + k; ! ! ! typingPause = setTimeout( ! ! ! ! function() { ! ! ! ! ! goToPage(newpage); ! ! ! ! }, 500); ! ! }! ! ! });! ! $('.pan-container').each(function(){ ! ! var $this=$(this).css({position:'relative', overflow:'hidden', cursor:'move'}) ! ! var $img=$this.children().eq(0) //image to pan ! ! var options={$pancontainer:$this, pos:$this.attr('data-orient'), curzoom:1, canzoom:$this.attr('data-canzoom'), wrappersize:[$this.width(), ! ! $img.imgmover(options) ! }) }); var newpage = ""; var typingPause; function goToPage(pagenum, pagetext, pageimg) {! ! $("#currentpage").val(pagenum); ! window.location.hash = pagenum;! ! newpage = ""; ! parseInt(pagenum) ? $("h3 span").text(pagenum) : $("h3 span").text("");! ! ! $.get("pages/" + pagetext,function(r) { ! ! $(".page_text").html(r);! ! ! ! pageimg ? $(".page_image").html('') : $(".page_image").html(""); ! ! if ($(".page_image").children().length) { ! ! ! $(".page_image").children().each(function() { ! ! ! ! $(this).zoomin({ ! ! ! ! ! bgcolor: "#999" ! ! ! ! }); ! ! ! }); ! ! }! ! ! ! $(".continue a").click(function(e) { ! ! ! e.preventDefault(); ! ! ! goToPage(parseInt($("#currentpage").val())+1); ! ! });! ! ! ! $(".choose a").click(function(e) { ! ! ! e.preventDefault(); ! ! ! var newpage = $(this).attr("rel"); ! ! ! goToPage(newpage); ! ! }); 3 Monday, November 14, 2011

Slide 4

Slide 4 text

you have a problem. ☛ old code ☛ other people’s code ☛ written too fast ☛ scope creep 4 Monday, November 14, 2011

Slide 5

Slide 5 text

but.. http://www.flickr.com/photos/vintagechica/5597949576/ 5 Monday, November 14, 2011

Slide 6

Slide 6 text

your baby is ugly. http://www.flickr.com/photos/quinnanya/2885102816/in/photostream/ 6 Monday, November 14, 2011

Slide 7

Slide 7 text

your baby is ugly. ☛ window.everything ☛ data in HTML ☛ all the plugins ever!! ☛ $(...).click(manageState) ☛ jQuery 1.oldAndBusted 7 Monday, November 14, 2011

Slide 8

Slide 8 text

wontfix? ☛ performance gets worse ☛ hacks beget hacks ☛ unmaintainable ☛ un-upgradable 8 Monday, November 14, 2011

Slide 9

Slide 9 text

rewrite from scratch? ☛ undocumented business logic ☛ new bugs for old bugs ☛ business people will freak out ☛ users will be impatient ☛ how long you got? 9 Monday, November 14, 2011

Slide 10

Slide 10 text

code written under duress is probably what got you here 10 Monday, November 14, 2011

Slide 11

Slide 11 text

why ‘sneaky’ ☛ business people see their features ☛ users see bugs fixed ☛ you see something other than the back of a giant boulder 11 Monday, November 14, 2011

Slide 12

Slide 12 text

it’s gonna get dirty http://www.flickr.com/photos/johnpaulgoguen/3359392738/ 12 Monday, November 14, 2011

Slide 13

Slide 13 text

it’s gonna get ridiculous http://www.flickr.com/photos/mhaithaca/447863503/ 13 Monday, November 14, 2011

Slide 14

Slide 14 text

where you want to be 14 Monday, November 14, 2011

Slide 15

Slide 15 text

where you want to be ☛ namespacing ☛ data separate from DOM ☛ not dependent on plugins ☛ state management separate from DOM ☛ jQuery 1.newHotness 14 Monday, November 14, 2011

Slide 16

Slide 16 text

(and then you can think about..) ☛ MVC ☛ AMD ☛ unit tests ☛ event delegation ☛ deferreds ☛ etc. 15 Monday, November 14, 2011

Slide 17

Slide 17 text

keep it simple ☛ one release at a time ☛ stick to the plan ☛ don’t get scared 16 Monday, November 14, 2011

Slide 18

Slide 18 text

let’s get cracking 17 Monday, November 14, 2011

Slide 19

Slide 19 text

release 0 ☛ take stock ☛ add TODOs ☛ basic reuse 18 Monday, November 14, 2011

Slide 20

Slide 20 text

TODO ! // TODO: find something less icky ! setInterval(function() { ! ! var currentpage = parseInt($("#currentpage").val()), ! ! ! num = window.location.hash.substring(1) || 0; ! ! if (currentpage != num) { ! ! ! goToPage(num); ! ! } ! }, 100); ! ! // TODO: rethink this whole thing ! $(document).bind("keydown",function(e) { ! ! ! ! clearTimeout(typingPause); ! ! ... ! ! var currentpage = parseInt($("#currentpage").val()); ! ! var k = keys[e.keyCode]; 19 Monday, November 14, 2011

Slide 21

Slide 21 text

cache/improve selectors ! newpage = ""; ! parseInt(pagenum) ? ! ! $("h3 span").text(pagenum) : ! ! $("h3 span").text("");! ! $.get("pages/" + pagetext,function(r) { ! ! $(".page_text").html(r);! ! ! ! pageimg ? ! ! ! $(".page_image").html('

Slide 22

Slide 22 text

cache/improve selectors ! // r0: saved .page_image selector ! var $h = $("h3 span"), ! ! $p = $("div.page_image"); ! newpage = ""; ! parseInt(pagenum) ? $h.text(pagenum) : $h.text("");! ! $.get("pages/" + pagetext,function(r) { ! ! $(".page_text").html(r);! ! ! ! pageimg ? ! ! ! $p.html('') : ! ! ! $p.html(""); ! ! var imgs = $p.children(); ! ! if (imgs.length) { ! ! ! $.each(imgs,function() { ! ! ! ! $(this).zoomin({ ! ! ! ! ! bgcolor: "#999" ! ! ! ! }); ! ! ! }); 21 Monday, November 14, 2011

Slide 23

Slide 23 text

avoid repetition $(document).ready(function() { ! $.get("pages/title.html",function(r) { ! ! $(".page_text").html(r); ! }, "html"); ! ! $(".continue a").click(function(e) { ! ! e.preventDefault(); ! ! goToPage(parseInt($("#currentpage").val())+1); ! }); ! ! $(".choose a").click(function(e) { ! ! e.preventDefault(); ! ! var newpage = $(this).attr("rel"); ! ! goToPage(newpage); ! }); }); 22 Monday, November 14, 2011

Slide 24

Slide 24 text

avoid repetition $(document).ready(function() { ! ! // r0: removed repeated code (+ link wireups) ! goToPage(0); }); 23 Monday, November 14, 2011

Slide 25

Slide 25 text

now you have.. ☛ tasks defined ☛ reuse ☛ abstraction 24 Monday, November 14, 2011

Slide 26

Slide 26 text

release 1 ☛ all your code under one namespace ☛ that’s it. ☛ find, replace, test ☛ memory lane 25 Monday, November 14, 2011

Slide 27

Slide 27 text

leave window alone var newpage = ""; // TODO: is this necessary? var keys = { ! "37": "left", ! "39": "right" }; var typingPause; // TODO: fewer arguments function goToPage(pagenum, pagetext, pageimg) { ! ... 26 Monday, November 14, 2011

Slide 28

Slide 28 text

leave window alone // r1: added this namespace var cyoa = cyoa || { ! ! newpage: "", ! ! // TODO: is this necessary? ! ! keys: { ! ! ! "37": "left", ! ! ! "39": "right" ! ! }, ! ! typingPause: null ! }; // TODO: fewer arguments cyoa.goToPage = function(pagenum, pagetext, pageimg) { 27 Monday, November 14, 2011

Slide 29

Slide 29 text

check for inline JS click here!!1 if (newpage == “”) document.write(“no new page to load”); 28 Monday, November 14, 2011

Slide 30

Slide 30 text

check external code // r1: added this namespace var cyoa = cyoa || { ! ! newpage: "", ! ! ... ! ! typingPause: null ! }; // r1: well it would have been cool, anyway.. var newpage = function() { ! return cyoa.newpage; }; 29 Monday, November 14, 2011

Slide 31

Slide 31 text

now you have.. ☛ your stuff is isolated ☛ group pieces of app ☛ good overview 30 Monday, November 14, 2011

Slide 32

Slide 32 text

release 2 ☛ hidden fields ☛ attributes ☛ add new objects at the right level ☛ stay out of display code.. for now 31 Monday, November 14, 2011

Slide 33

Slide 33 text

val() // TODO: fewer arguments cyoa.goToPage = function(pagenum, pagetext, pageimg) { ! ... ! $("#currentpage").val(pagenum); ! window.location.hash = pagenum; 32 Monday, November 14, 2011

Slide 34

Slide 34 text

val() // TODO: fewer arguments cyoa.goToPage = function(pagenum, pagetext, pageimg) { ! ... ! cyoa.currentPage = pagenum; ! window.location.hash = pagenum; 33 Monday, November 14, 2011

Slide 35

Slide 35 text

attr() If you decide to refactor, turn to page 4. $("div.choose a").click(function(e) { ! e.preventDefault(); ! var newpage = $(this).attr("rel"); ! cyoa.goToPage(newpage); }); 34 Monday, November 14, 2011

Slide 36

Slide 36 text

attr() If you decide to refactor, turn to page 4. (or) // TODO: add to state object $("div.choose a").click(function(e) { ! e.preventDefault(); ! var newpage = $(this).attr("rel"); ! cyoa.goToPage(newpage); }); 35 Monday, November 14, 2011

Slide 37

Slide 37 text

now you have.. ☛ DOM data vs. non-DOM data ☛ access data more quickly ☛ change HTML without breaking app 36 Monday, November 14, 2011

Slide 38

Slide 38 text

release 3 ☛ isolate existing plugins ☛ formalize widgets ☛ my.plugin = function($t) or $.fn.plugin 37 Monday, November 14, 2011

Slide 39

Slide 39 text

wrapping plugins ! ! var imgs = $p.children(); ! ! if (imgs.length) { ! ! ! $.each(imgs,function() { ! ! ! ! $(this).zoomin({ ! ! ! ! ! bgcolor: "#999" ! ! ! ! }); ! ! ! }); ! ! } 38 Monday, November 14, 2011

Slide 40

Slide 40 text

wrapping plugins ! ! // r3: removed plugin setup code ! ! cyoa.setUpZoom($p.children()); ... cyoa = { ! ! setUpZoom: function($t) { ! ! ! if ($t.length) { ! ! ! ! $t.each(function() { ! ! ! ! ! cyoa.zoomin($(this), { ! ! ! ! ! ! bgcolor: "#999" ! ! ! ! ! }); ! ! ! ! }); ! ! ! } ! ! } } 39 Monday, November 14, 2011

Slide 41

Slide 41 text

not $.fn.everything ☛ known element type? ☛ known class? ☛ known properties? ☛ known length? ☛ that’s a widget, y’all 40 Monday, November 14, 2011

Slide 42

Slide 42 text

now you have.. ☛ easy plugin swap/upgrade ☛ app-specific stock of widgets ☛ your junk out of $.fn.* ☛ widgets within relevant state in... 41 Monday, November 14, 2011

Slide 43

Slide 43 text

release 4 ☛ state objects ☛ steps in arrays ☛ non-linear states: myApp.states[“thisState”] ☛ state functions ☛ init ☛ change state ☛ error 42 Monday, November 14, 2011

Slide 44

Slide 44 text

confine state info // TODO: fewer arguments cyoa.goToPage = function(pagenum, pagetext, pageimg) { ! // TODO: put this logic someplace else ! if (!pagetext) { ! ! switch (parseInt(pagenum)) { ! ! ! case 0: ! ! ! ! cyoa.goToPage0(); ! ! ! ! break; ! ! ! case 1: ! ! ! ! cyoa.goToPage1(); ! ! ! ! break; ! ! ! case 2: ! ! ! ! cyoa.goToPage2(); ! ! ! ! break; ! ! ! ... 43 Monday, November 14, 2011

Slide 45

Slide 45 text

confine state info // r4: added more structured state management cyoa.state = { ! init: function() { ! ! var num = window.location.hash.substring(1) || 0; ! ! cyoa.state.goToPage(num); ! }, ! ! goToPage: function(pagenum) { ! ! ! ! // r4: check that page is valid ! ! pagenum = parseInt(pagenum); ! ! if (pagenum < 0 || pagenum >= cyoa.states.length) ! ! ! return; ! ! ! ! cyoa.currentPage = pagenum; ! ! window.location.hash = pagenum; 44 Monday, November 14, 2011

Slide 46

Slide 46 text

confine state info // TODO: NOT THIS. cyoa.goToPage0 = function() { ! cyoa.goToPage(0,"title.html"); } cyoa.goToPage1 = function() { ! cyoa.goToPage(1,"whatToDo.html"); } cyoa.goToPage2 = function() { ! cyoa.goToPage(2,"youHaveDied.html","explosion.jpg"); } cyoa.goToPage3 = function() { ! cyoa.goToPage(3,"youHaveDied.html","sisyphus.jpg"); } cyoa.goToPage4 = function() { ! cyoa.goToPage(4,"sellIt.html"); } 45 Monday, November 14, 2011

Slide 47

Slide 47 text

confine state info // r4: created array with page/state info cyoa.states = [ ! {page: "title.html"},! ! {page: "whatToDo.html"},! ! {page: "youHaveDied.html", image: "explosion.jpg"},! ! {page: "youHaveDied.html", image: "sisyphus.jpg"},! ! {page: "sellIt.html"} ]; // extra credit: routing info 46 Monday, November 14, 2011

Slide 48

Slide 48 text

now you have.. ☛ state differences encapsulated ☛ single place to switch state ☛ clear definitions ☛ add states cleanly ☛ add in routing with less risk 47 Monday, November 14, 2011

Slide 49

Slide 49 text

release 5 ☛ pub/sub instead of event handlers ☛ reduce anonymous functions ☛ event handlers manage $(this) ☛ onreadystatechange publishes 48 Monday, November 14, 2011

Slide 50

Slide 50 text

publish/subscribe ! ! $.get("pages/" + state.page,function(r) { ! ! ! $("div.page_text").html(r); ! ! ! ! ! ! img ? ! ! ! ! $p.html('') : ! ! ! ! $p.html(""); ! ! ! // r3: removed plugin setup code ! ! ! cyoa.setUpZoom($p.children()); ! ! ! ! ! ! $("div.continue a").click(function(e) { ! ! ! ! e.preventDefault(); ! ! ! ! cyoa.state.goToPage(cyoa.currentPage+1); ! ! ! }); ! ! ! ! ! ! ...! ! ! ! ! ! }, "html"); 49 Monday, November 14, 2011

Slide 51

Slide 51 text

publish/subscribe ! ! $.get("pages/" + state.page,function(r) {! ! ! ! ! ! cyoa.event.publish("pageLoaded", [r]);! ! ! }, "html"); ... ! cyoa.event.subscribe("pageLoaded", function(r) {! ! ! ! ! $("div.page_text").html(r); ! ! ! ! $("div.continue a").click(function(e) { ! ! ! e.preventDefault(); ! ! ! cyoa.currentPage += 1; ! ! }); ! ! ! ! }); 50 Monday, November 14, 2011

Slide 52

Slide 52 text

pub/sub and get/set cyoa = { ! ! _currentPage: 0, ! ! // r5: getter and setter for currentPage ! ! get currentPage() { return this._currentPage; }, ! ! set currentPage(n) { ! ! ! if (n < 0 || n >= cyoa.states.length) return; ! ! ! this._currentPage = n; ! ! ! cyoa.event.publish("pageChanged"); ! ! } } ... cyoa.event.subscribe("pageChanged", function(r) {!! ! cyoa.state.goToPage(); }); 51 Monday, November 14, 2011

Slide 53

Slide 53 text

now you have.. ☛ application events vs. DOM events ☛ no manual callback chains ☛ easily add features that observe events ☛ control state through properties 52 Monday, November 14, 2011

Slide 54

Slide 54 text

release 6 ☛ upgrade ☛ regression test ☛ swap out non-forward-compatible plugins ☛ regression test ☛ maybe roll back (sorries :( ) 53 Monday, November 14, 2011

Slide 55

Slide 55 text

upgrade and test ! cyoa.event.subscribe("pageLoaded", function(r) {! ! ! ! ! $("div.page_text").html(r); ! ! ! ! $("div.continue a").click(function(e) { ! ! ! e.preventDefault(); ! ! ! cyoa.currentPage += 1; ! ! }); ! ! ! ! }); 54 Monday, November 14, 2011

Slide 56

Slide 56 text

upgrade and test ! // r6: don't keep binding this every time ! $("div.page_text") ! ! .delegate("div.continue a","click",function(e) { ! ! e.preventDefault(); ! ! cyoa.currentPage += 1; ! }); 55 Monday, November 14, 2011

Slide 57

Slide 57 text

when you can’t upgrade ☛ bugs should be easier to find ☛ fix them ☛ keep trying ☛ ala carte features 56 Monday, November 14, 2011

Slide 58

Slide 58 text

but once you do.. !! ☛ dependency management ☛ event delegation ☛ deferreds ☛ unit tests ☛ documentation ☛ ..framework? maybe? 57 Monday, November 14, 2011

Slide 59

Slide 59 text

nobody saw a thing ☛ business people: “Oh I thought you finished that five releases ago?” ☛ users: less “It’s too slow,” more “Why can’t I make this have polka dots?” 58 Monday, November 14, 2011

Slide 60

Slide 60 text

you: ☛ “:D” ☛ can develop faster ☛ can fix easier ☛ can get hit by all the buses you want 59 Monday, November 14, 2011

Slide 61

Slide 61 text

hey, since you did such a super job on that refactor... 60 Monday, November 14, 2011

Slide 62

Slide 62 text

thanks! ☛ who’s got questions? ☛ shy questions: ☛ @garannm ☛ [email protected] 61 Monday, November 14, 2011