• Save
Beyond the DOM: Sane Structure for JS Apps
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Beyond the DOM: Sane Structure for JS Apps

  • 3,906 views
Uploaded on

Delivered at 2012 FrontTrends in Warsaw.

Delivered at 2012 FrontTrends in Warsaw.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
3,906
On Slideshare
3,745
From Embeds
161
Number of Embeds
3

Actions

Shares
Downloads
0
Comments
0
Likes
14

Embeds 161

http://coderwall.com 159
https://twitter.com 1
http://pmomale-ld1 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Beyond the DOM: Sane Structure for JS Apps Rebecca Murphey • @rmurphey • FrontTrends 2012Thursday, April 26, 12
  • 2. rmurphey.com • @rmurphey • bocoup.comThursday, April 26, 12
  • 3. function ObjInlineDown(e) { if (is.ie) e = event if (is.ie && ! is.ieMac && e.button != 1 && e.button != 2) return if (is.ieMac && e.button != 0) return if (is.ns && ! is.ns4 && e.button != 0 && e.button != 2) return if (is.ns4 && e.which != 1 && e.which != 3) return this.onSelect() this.onDown() } function ObjInlineUp(e) { if (is.ie) e = event if (is.ie && ! is.ieMac && e.button != 1 && e.button != 2) return if (is.ieMac && e.button != 0) return if (is.ns && ! is.ns4 && ! is.nsMac && e.button != 0 && e.button != 2) return if (is.ns4 && e.which != 1 && e.which != 3) return if ((!is.ns4 && e.button == 2) || (is.ns4 && e.which == 3)) { if (this.hasOnRUp) { document.oncontextmenu = ocmNone this.onRUp() setTimeout("document.oncontextmenu = ocmOrig", 100) } } else if (this.hasOnUp) this.onUp() }Thursday, April 26, 12
  • 4. Thursday, April 26, 12
  • 5. Thursday, April 26, 12
  • 6. Thursday, April 26, 12
  • 7. <div id="searchForm"> <form class="form-inline"> <input type="text" placeholder="Enter your search term"> <button type="submit">Search</button> </form> <ul id="searchResults"></ul> </div>Thursday, April 26, 12
  • 8. $("#searchForm form").submit(function(e) { alert(submit); e.preventDefault(); var term = $(#searchForm input).val(), req = $.getJSON(http://search.twitter.com/search.json?callback=?&q= + encodeURIComponent(term)); req.then(function(resp) { var resultsHTML = $.map(resp.results, function(r) { return <li> + <p class="tweet"> + r.text + </p> + <p class="username"> + r.from_user + </p> + </li>; }).join(); $(#searchResults).html(resultsHTML); }); });Thursday, April 26, 12
  • 9. Thursday, April 26, 12
  • 10. Thursday, April 26, 12
  • 11. a).hasClass(md_fullpage)) { // alert(clicked section is current section AND fullpage mode is active; teaser should load); // Minimize jQuery(#md_tabs_navigation a).removeClass(md_fullpage); jQuery(.md_body).hide(); jQuery(#md_feature).slideDown(normal,function(){ var bodyContent = jQuery(#md_body_+ section); bodyContent.fadeOut(normal,function(){ jQuery(#md_tabs_navigation a).each(function(){ var thisSection = jQuery(this).html().replace(<span<,).replace(</span<,); var thisSection_comp = thisSection.toLowerCase().replace( ,_); jQuery(#md_body_+ thisSection_comp).load( /app/modules/info/loadTeaser.php?sect=+ thisSection_comp, function(){ tb_init(.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox); bodyContent.animate({ height: toggle, opacity: toggle },"slow"); } ); }); }); }); jQuery(#expandtabs span).empty().append(Expand Tabs); } else { // if the clicked section is NOT the current section OR were NOT in full page mode // then lets go to full page mode and show the whole tab // Maximize // alert(clicked section is not the current section OR full page mode is not active; full section should load); jQuery(#md_tabs_navigation li).removeClass(current); jQuery(#md_tab_+ section).addClass(current); jQuery(#md_tabs_navigation a).addClass(md_fullpage); jQuery(.md_body).hide(); jQuery(#md_feature).slideUp(normal,function(){ var bodyContent = jQuery(#md_body_+ section); bodyContent.fadeOut(normal,function(){ bodyContent.empty(); var pageLoader = info/loadSection.php?sect=+ section; if (section == contact_us) { pageLoader = contact/loadContactForm.php?form_id=1; } bodyContent.load(/app/modules/+ pageLoader,function(){ // ADD THICKBOXES tb_init(.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox); $recent_news_links = jQuery(ul.md_news li a.recent_news_link); $recent_news_links .unbind(click) .each(function(){ var hrefMod = this.href; hrefMod = hrefMod.replace(/article/,loadNews).replace(/storyid/,id); this.href = hrefMod; }) .click(function(){Thursday, April 26, 12 var t = this.title || this.name || null;
  • 12. Thursday, April 26, 12
  • 13. $("#searchForm form").submit(function(e) { alert(submit); search input e.preventDefault(); var term = $(#searchForm input).val(), req = $.getJSON(http://search.twitter.com encodeURIComponent(term)); req.then(function(resp) { search data var resultsHTML = $.map(resp.results, functi return <li> + <p class="tweet"> + r.text + </p> + <p class="username"> + r.from_user + </li>; }).join(); $(#searchResults).html(resultsHTML); search results }); });Thursday, April 26, 12
  • 14. Thursday, April 26, 12
  • 15. Thursday, April 26, 12
  • 16. Thursday, April 26, 12
  • 17. Thursday, April 26, 12
  • 18. Thursday, April 26, 12
  • 19. define([ jquery, text!template.html ], function($, html) { return function() { $(body).append(html); }; });Thursday, April 26, 12
  • 20. <script data-main="app/config" src="/lib/require.js"></script>Thursday, April 26, 12
  • 21. require.config({ deps : [ main ], paths : { // JavaScript folders lib : ../lib, plugins : ../lib/plugins, tests : ../tests, app : ., // Libraries jquery : ../lib/jquery, underscore : ../lib/underscore, backbone : ../lib/backbone, text : ../lib/plugins/text } });Thursday, April 26, 12
  • 22. app/main require([ use!backbone, jquery, router, models/app ], function(B, $, Router, app) { $(function() { app.router = new Router(); B.history.start(); }); });Thursday, April 26, 12
  • 23. Thursday, April 26, 12
  • 24. views display data, announce user interaction, and await further instruction models & collections manage application state and communicate with the server controllers set up views, transport messages from views to models & collectionsThursday, April 26, 12
  • 25. app/views/searchForm app/views/recentSearches app/views/resultsThursday, April 26, 12
  • 26. app/controllers/search #mainbar #sidebarThursday, April 26, 12
  • 27. searches collection keeps track of recent search terms search data collection fetches results from the server for a given search term app model keeps track of general application state, including the current search search model for representing individual searchesThursday, April 26, 12
  • 28. app/views/searchForm search controller server search data searches collection app model app/views/recentSearches app/views/resultsThursday, April 26, 12
  • 29. $("#searchForm form").submit(function(e) { alert(submit); e.preventDefault(); var term = $(#searchForm input).val(), req = $.getJSON(http://search.twitter.com/search.json?callba encodeURIComponent(term)); req.then(function(resp) { var resultsHTML = $.map(resp.results, function(r) { return <li> + <p class="tweet"> + r.text + </p> + <p class="username"> + r.from_user + </p> + </li>; }).join(); $(#searchResults).html(resultsHTML); }); });Thursday, April 26, 12
  • 30. app/views/searchForm search controller server search data searches collection app model app/views/recentSearches app/views/resultsThursday, April 26, 12
  • 31. prepare : function() { _.bindAll(this, release, _onSearch, _disable); }, events : { submit .search-form : _onSearch }, _onSearch : function(e) { e.preventDefault(); if (this.disabled) { return; } var term = $.trim(this.$(.js-input).val()); if (!term) { return; } this._disable(); this.trigger(search, term); }, release : function() { this.disabled = false; this.$(.js-submit).removeAttr(disabled); },Thursday, April 26, 12
  • 32. searchForm.on(search, update); function update(t) { var term = $.trim(t), existing = searches.where({ term : term }), dfd = $.Deferred(), search; app.set(currentSearch, term); if (term) { if (existing.length) { search = existing[0]; search.update(); } else { search = new Search({ term : term }); searches.add(search); } searchData.fetch({ data : { term : term } }) .then(dfd.resolve, dfd.reject) .always(searchForm.release); app.router.navigate(search/ + term); } else { dfd.resolve(); } return dfd; }Thursday, April 26, 12
  • 33. it("should announce the form submission", function() { var t; sf.on(search, function(term) { t = term; }); el.find(.js-input).val(searchterm); el.find(.search-form).submit(); expect(t).to.be(searchterm); });Thursday, April 26, 12
  • 34. it("should update the page when the search form announces a search", function(done) { var searchFormEl = $(.component.search-form).parent(), searchForm = _.filter(s.views, function(v) { return v.$el[0] === searchFormEl[0]; })[0]; s.searchData.on(change, function() { expect($(.component.results).html()).to.contain(srchr); expect($(.component.recent-searches).html()).to.contain(srchr); expect(navigatedTo).to.be(search/srchr); done(); }); searchForm.trigger(search, srchr); });Thursday, April 26, 12
  • 35. app/views/searchForm search controller server search data searches collection app model app/views/recentSearches app/views/resultsThursday, April 26, 12
  • 36. function update(t) { var term = $.trim(t), existing = searches.where({ term : term }), dfd = $.Deferred(), search; app.set(currentSearch, term); if (term) { if (existing.length) { search = existing[0]; search.update(); } else { search = new Search({ term : term }); searches.add(search); } searchData.fetch({ data : { term : term } }) .then(dfd.resolve, dfd.reject) .always(searchForm.release); app.router.navigate(search/ + term); } else { dfd.resolve(); } return dfd; }Thursday, April 26, 12
  • 37. this.bindTo(this.searchData, add change, this._update); this.bindTo(this.searchData, fetching, function() { this._empty(); this.reset(); });Thursday, April 26, 12
  • 38. Thursday, April 26, 12
  • 39. function update(t) { var term = $.trim(t), existing = searches.where({ term : term }), search; app.set(currentSearch, term); if (existing.length) { search = existing[0]; search.update(); } else { search = new Search({ term : term }); searches.add(search); } searchData.fetch({ data : { term : term } }) .always(searchForm.release); app.router.navigate(search/ + term); }Thursday, April 26, 12
  • 40. describe("#update", function() { it("should update the time", function(done) { var search = new Search(), oldTime = search.get(time); setTimeout(function() { search.update(); expect(search.get(time)).to.be.greaterThan(oldTime); done(); }, 1000); }); });Thursday, April 26, 12
  • 41. it("should update when there is a new search", function() { expect(el.html()).not.to.contain(baz); rs.currentSearch = function() { return baz; }; rs.searches.add({ term : baz }); expect(el.html()).to.contain(baz); expect(el.find(.active).html()).to.contain(baz); });Thursday, April 26, 12
  • 42. memory management requirejs builds for production multi-page apps w/history apiThursday, April 26, 12
  • 43. rmurphey.com • @rmurphey • bocoup.com github.com/rmurphey/srchr-demoThursday, April 26, 12