• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Sneaking structure into your DOM-based application
 

Sneaking structure into your DOM-based application

on

  • 12,735 views

You thought you were building a proof of concept, but then that proof of concept went live. Or you had two weeks to build what should have taken two months. Or the handful of progressive enhancements ...

You thought you were building a proof of concept, but then that proof of concept went live. Or you had two weeks to build what should have taken two months. Or the handful of progressive enhancements you threw onto a page to make the user experience a little nicer somehow evolved into an entire single-page app. Whatever the reason, you find yourself with a full-blown application built around click events and a staggering number of plugins you can't even remember downloading. If you could rewrite it, you'd use a framework built with your scenario in mind, but it gets 17 zillion hits a day and there's only one of you and starting from scratch isn't an option. No, what you need is the philosophy of a framework broken into discrete pieces that fit into a one- or two-week release cycle. This talk aims to provide bite-sized strategies you can implement in a short amount of time with minimal disruption to unchain your application from the DOM.

Demo code on github: https://github.com/garann/cyoa

Statistics

Views

Total Views
12,735
Views on SlideShare
12,395
Embed Views
340

Actions

Likes
17
Downloads
75
Comments
2

15 Embeds 340

http://paper.li 108
http://lanyrd.com 97
http://a0.twimg.com 55
http://us-w1.rockmelt.com 33
http://twitc.com 15
http://www.linkedin.com 11
http://www.techgig.com 8
http://twitter.com 3
https://wego.talkerapp.com 3
https://www.linkedin.com 2
http://tweetedtimes.com 1
https://twitter.com 1
http://instacurate.com 1
http://115.112.206.131 1
http://translate.googleusercontent.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

12 of 2 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • welcome to it !!hi gh quali ty☆ reaso nable pric e☆
    free shi pping accept pay pal,
    if you have interest in it , take action !!!!!
    ***** {{w w w }} {{ happyshopping100 }} {{ com }} ***
    Are you sure you want to
    Your message goes here
    Processing…
  • Minor typo: on slide 36 .attr(’rel’) is meant to be changed to .attr(’href’)
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Sneaking structure into your DOM-based application Sneaking structure into your DOM-based application Presentation Transcript

    • sneaking structure into your DOM-based applicationSunday, April 17, 2011
    • ohai ☛ Garann Means ☛ Vast ☛ Austin All-Girl Hack Night ☛ @garannm / garann.comSunday, April 17, 2011
    • $(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(<img src="img/ + pageimg + " />) : $(".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); ! ! });Sunday, April 17, 2011
    • you have a problem. ☛ old code ☛ other people’s code ☛ written too fast ☛ scope creepSunday, April 17, 2011
    • but.. http://www.flickr.com/photos/vintagechica/5597949576/Sunday, April 17, 2011
    • your baby is ugly. http://www.flickr.com/photos/quinnanya/2885102816/in/photostream/Sunday, April 17, 2011
    • your baby is ugly. ☛ window.everything ☛ data in HTML ☛ all the plugins ever!! ☛ $(...).click(manageState) ☛ jQuery 1.oldAndBustedSunday, April 17, 2011
    • wontfix? ☛ performance gets worse ☛ hacks beget hacks ☛ unmaintainable ☛ un-upgradableSunday, April 17, 2011
    • rewrite from scratch? ☛ undocumented business logic ☛ new bugs for old bugs ☛ business people will freak out ☛ users will be impatient ☛ how long you got?Sunday, April 17, 2011
    • code written under duress is probably what got you hereSunday, April 17, 2011
    • why ‘sneaky’ ☛ business people see their features ☛ users see bugs fixed ☛ you see something other than the back of a giant boulderSunday, April 17, 2011
    • it’s gonna get dirty http://www.flickr.com/photos/johnpaulgoguen/3359392738/Sunday, April 17, 2011
    • it’s gonna get ridiculous http://www.flickr.com/photos/mhaithaca/447863503/Sunday, April 17, 2011
    • where you want to beSunday, April 17, 2011
    • where you want to be ☛ namespacing ☛ data separate from DOM ☛ not dependent on plugins ☛ state management separate from DOM ☛ jQuery 1.newHotnessSunday, April 17, 2011
    • (and then you can think about..) ☛ MVC ☛ AMD ☛ unit tests ☛ event delegation ☛ deferreds ☛ etc.Sunday, April 17, 2011
    • keep it simple ☛ one release at a time ☛ stick to the plan ☛ don’t get scaredSunday, April 17, 2011
    • let’s get crackingSunday, April 17, 2011
    • release 0 ☛ take stock ☛ add TODOs ☛ basic reuseSunday, April 17, 2011
    • 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];Sunday, April 17, 2011
    • 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(<img src="img/ + pageimg + " ! ! ! $(".page_image").html(""); ! ! if ($(".page_image").children().length) { ! ! ! $(".page_image").children().each(function() { ! ! ! ! $(this).zoomin({ ! ! ! ! ! bgcolor: "#999" ! ! ! ! }); ! ! ! }); ! ! } ! ! ...Sunday, April 17, 2011
    • 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(<img src="img/ + pageimg + " />) : ! ! ! $p.html(""); ! ! var imgs = $p.children(); ! ! if (imgs.length) { ! ! ! $.each(imgs,function() { ! ! ! ! $(this).zoomin({ ! ! ! ! ! bgcolor: "#999" ! ! ! ! }); ! ! ! });Sunday, April 17, 2011
    • 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); ! }); });Sunday, April 17, 2011
    • avoid repetition $(document).ready(function() { ! ! // r0: removed repeated code (+ link wireups) ! goToPage(0); });Sunday, April 17, 2011
    • now you have.. ☛ tasks defined ☛ reuse ☛ abstractionSunday, April 17, 2011
    • release 1 ☛ all your code under one namespace ☛ that’s it. ☛ find, replace, test ☛ memory laneSunday, April 17, 2011
    • 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) { ! ...Sunday, April 17, 2011
    • 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) {Sunday, April 17, 2011
    • check for inline JS <a href=”javascript:goToPage(9)”>click here!!1</a> <script type=”text/javascript”> if (newpage == “”) document.write(“no new page to load”); </script>Sunday, April 17, 2011
    • 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; };Sunday, April 17, 2011
    • now you have.. ☛ your stuff is isolated ☛ group pieces of app ☛ good overviewSunday, April 17, 2011
    • release 2 ☛ hidden fields ☛ attributes ☛ add new objects at the right level ☛ stay out of display code.. for nowSunday, April 17, 2011
    • val() // TODO: fewer arguments cyoa.goToPage = function(pagenum, pagetext, pageimg) { ! ... ! $("#currentpage").val(pagenum); ! window.location.hash = pagenum;Sunday, April 17, 2011
    • val() // TODO: fewer arguments cyoa.goToPage = function(pagenum, pagetext, pageimg) { ! ... ! cyoa.currentPage = pagenum; ! window.location.hash = pagenum;Sunday, April 17, 2011
    • attr() <a rel="4">If you decide to refactor, turn to page 4.</a> $("div.choose a").click(function(e) { ! e.preventDefault(); ! var newpage = $(this).attr("rel"); ! cyoa.goToPage(newpage); });Sunday, April 17, 2011
    • attr() <a href="#4">If you decide to refactor, turn to page 4.</a> (or) // TODO: add to state object $("div.choose a").click(function(e) { ! e.preventDefault(); ! var newpage = $(this).attr("rel"); ! cyoa.goToPage(newpage); });Sunday, April 17, 2011
    • now you have.. ☛ DOM data vs. non-DOM data ☛ access data more quickly ☛ change HTML without breaking appSunday, April 17, 2011
    • release 3 ☛ isolate existing plugins ☛ formalize widgets ☛ my.plugin = function($t) or $.fn.pluginSunday, April 17, 2011
    • wrapping plugins ! ! var imgs = $p.children(); ! ! if (imgs.length) { ! ! ! $.each(imgs,function() { ! ! ! ! $(this).zoomin({ ! ! ! ! ! bgcolor: "#999" ! ! ! ! }); ! ! ! }); ! ! }Sunday, April 17, 2011
    • 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" ! ! ! ! ! }); ! ! ! ! }); ! ! ! } ! ! } }Sunday, April 17, 2011
    • not $.fn.everything ☛ known element type? ☛ known class? ☛ known properties? ☛ known length? ☛ that’s a widget, y’allSunday, April 17, 2011
    • now you have.. ☛ easy plugin swap/upgrade ☛ app-specific stock of widgets ☛ your junk out of $.fn.* ☛ widgets within relevant state in...Sunday, April 17, 2011
    • release 4 ☛ state objects ☛ steps in arrays ☛ non-linear states: myApp.states[“thisState”] ☛ state functions ☛ init ☛ change state ☛ errorSunday, April 17, 2011
    • 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; ! ! ! ...Sunday, April 17, 2011
    • 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;Sunday, April 17, 2011
    • 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"); }Sunday, April 17, 2011
    • 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 infoSunday, April 17, 2011
    • now you have.. ☛ state differences encapsulated ☛ single place to switch state ☛ clear definitions ☛ add states cleanly ☛ add in routing with less riskSunday, April 17, 2011
    • release 5 ☛ pub/sub instead of event handlers ☛ reduce anonymous functions ☛ event handlers manage $(this) ☛ onreadystatechange publishesSunday, April 17, 2011
    • publish/subscribe ! ! $.get("pages/" + state.page,function(r) { ! ! ! $("div.page_text").html(r); ! ! ! ! ! ! img ? ! ! ! ! $p.html(<img src="img/ + img + " />) : ! ! ! ! $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");Sunday, April 17, 2011
    • 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; ! ! }); ! ! ! ! });Sunday, April 17, 2011
    • 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(); });Sunday, April 17, 2011
    • now you have.. ☛ application events vs. DOM events ☛ no manual callback chains ☛ easily add features that observe events ☛ control state through propertiesSunday, April 17, 2011
    • release 6 ☛ upgrade ☛ regression test ☛ swap out non-forward-compatible plugins ☛ regression test ☛ maybe roll back (sorries :( )Sunday, April 17, 2011
    • 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; ! ! }); ! ! ! ! });Sunday, April 17, 2011
    • upgrade and test ! // r6: dont keep binding this every time ! $("div.page_text") ! ! .delegate("div.continue a","click",function(e) { ! ! e.preventDefault(); ! ! cyoa.currentPage += 1; ! });Sunday, April 17, 2011
    • when you can’t upgrade ☛ bugs should be easier to find ☛ fix them ☛ keep trying ☛ ala carte featuresSunday, April 17, 2011
    • but once you do.. !! ☛ dependency management ☛ event delegation ☛ deferreds ☛ unit tests ☛ documentation ☛ ..framework? maybe?Sunday, April 17, 2011
    • 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?”Sunday, April 17, 2011
    • you: ☛ “:D” ☛ can develop faster ☛ can fix easier ☛ can get hit by all the buses you wantSunday, April 17, 2011
    • hey, since you did such a super job on that refactor...Sunday, April 17, 2011
    • thanks! ☛ who’s got questions? ☛ shy questions: ☛ @garannm ☛ garann@gmail.comSunday, April 17, 2011