Your SlideShare is downloading. ×
Beyond the DOM:             Sane Structure for JS Apps             Rebecca Murphey • @rmurphey • FrontTrends 2012Thursday,...
rmurphey.com • @rmurphey • bocoup.comThursday, April 26, 12
function ObjInlineDown(e) {                   if (is.ie) e = event                   if (is.ie && ! is.ieMac && e.button !...
Thursday, April 26, 12
Thursday, April 26, 12
Thursday, April 26, 12
<div id="searchForm">                     <form class="form-inline">                          <input type="text" placehold...
$("#searchForm form").submit(function(e) {                  alert(submit);                  e.preventDefault();           ...
Thursday, April 26, 12
Thursday, April 26, 12
a).hasClass(md_fullpage)) {                           // alert(clicked section is current section AND fullpage mode is act...
Thursday, April 26, 12
$("#searchForm form").submit(function(e) {                                            alert(submit);                      ...
Thursday, April 26, 12
Thursday, April 26, 12
Thursday, April 26, 12
Thursday, April 26, 12
Thursday, April 26, 12
define([                   jquery,                   text!template.html                 ], function($, html) {            ...
<script data-main="app/config" src="/lib/require.js"></script>Thursday, April 26, 12
require.config({                   deps : [ main ],                         paths : {                           // JavaScr...
app/main                 require([                   use!backbone,                   jquery,                   router,    ...
Thursday, April 26, 12
views display data, announce user interaction,                 and await further instruction                 models & coll...
app/views/searchForm   app/views/recentSearches              app/views/resultsThursday, April 26, 12
app/controllers/search                         #mainbar      #sidebarThursday, April 26, 12
searches collection keeps track of recent                 search terms                 search data collection fetches resu...
app/views/searchForm                                             search controller               server              searc...
$("#searchForm form").submit(function(e) {                      alert(submit);                      e.preventDefault();   ...
app/views/searchForm                                             search controller               server              searc...
prepare : function() {                            _.bindAll(this, release, _onSearch, _disable);                         }...
searchForm.on(search, update);                         function update(t) {                           var term            ...
it("should announce the form submission", function() {                           var t;                           sf.on(se...
it("should update the page when the search form announces a search", function(done) {                     var searchFormEl...
app/views/searchForm                                             search controller               server              searc...
function update(t) {                           var term             = $.trim(t),                               existing   ...
this.bindTo(this.searchData, add change, this._update);                 this.bindTo(this.searchData, fetching, function() ...
Thursday, April 26, 12
function update(t) {                           var term              = $.trim(t),                               existing  ...
describe("#update", function() {                  it("should update the time", function(done) {                    var sea...
it("should update when there is a new search", function() {                      expect(el.html()).not.to.contain(baz);   ...
memory management                 requirejs builds for production                 multi-page apps w/history apiThursday, A...
rmurphey.com • @rmurphey • bocoup.com                            github.com/rmurphey/srchr-demoThursday, April 26, 12
Upcoming SlideShare
Loading in...5
×

Beyond the DOM: Sane Structure for JS Apps

3,860

Published on

Delivered at 2012 FrontTrends in Warsaw.

Published in: Technology

Transcript of "Beyond the DOM: Sane Structure for JS Apps"

  1. 1. Beyond the DOM: Sane Structure for JS Apps Rebecca Murphey • @rmurphey • FrontTrends 2012Thursday, April 26, 12
  2. 2. rmurphey.com • @rmurphey • bocoup.comThursday, April 26, 12
  3. 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. 4. Thursday, April 26, 12
  5. 5. Thursday, April 26, 12
  6. 6. Thursday, April 26, 12
  7. 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. 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. 9. Thursday, April 26, 12
  10. 10. Thursday, April 26, 12
  11. 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. 12. Thursday, April 26, 12
  13. 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. 14. Thursday, April 26, 12
  15. 15. Thursday, April 26, 12
  16. 16. Thursday, April 26, 12
  17. 17. Thursday, April 26, 12
  18. 18. Thursday, April 26, 12
  19. 19. define([ jquery, text!template.html ], function($, html) { return function() { $(body).append(html); }; });Thursday, April 26, 12
  20. 20. <script data-main="app/config" src="/lib/require.js"></script>Thursday, April 26, 12
  21. 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. 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. 23. Thursday, April 26, 12
  24. 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. 25. app/views/searchForm app/views/recentSearches app/views/resultsThursday, April 26, 12
  26. 26. app/controllers/search #mainbar #sidebarThursday, April 26, 12
  27. 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. 28. app/views/searchForm search controller server search data searches collection app model app/views/recentSearches app/views/resultsThursday, April 26, 12
  29. 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. 30. app/views/searchForm search controller server search data searches collection app model app/views/recentSearches app/views/resultsThursday, April 26, 12
  31. 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. 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. 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. 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. 35. app/views/searchForm search controller server search data searches collection app model app/views/recentSearches app/views/resultsThursday, April 26, 12
  36. 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. 37. this.bindTo(this.searchData, add change, this._update); this.bindTo(this.searchData, fetching, function() { this._empty(); this.reset(); });Thursday, April 26, 12
  38. 38. Thursday, April 26, 12
  39. 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. 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. 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. 42. memory management requirejs builds for production multi-page apps w/history apiThursday, April 26, 12
  43. 43. rmurphey.com • @rmurphey • bocoup.com github.com/rmurphey/srchr-demoThursday, April 26, 12

×