Advertisement

Building Large jQuery Applications

Front-end architecture consultant at Rebecca Murphey Consulting LLC
Apr. 28, 2010
Advertisement

More Related Content

Advertisement
Advertisement

Building Large jQuery Applications

  1. http://www.flickr.com/photos/mocambique/511151694/ Building Large jQuery Applications Rebecca Murphey
  2. Me independent JavaScript developer based in Durham, N.C. contributor to O’Reilly’s jQuery Cookbook co-host of the yayQuery podcast code organization fanatic
  3. What counts as a large application? http://www.flickr.com/photos/hippie/2475855465/
  4. distinct but related pieces of functionality extensive server communication frequent updating of the page without reload
  5. jQuery out of the box doesn’t give us many tools for developing large applications; its essentially a DOM manipulation and Ajax library.
  6. at doesn’t stop people from building large applications with jQuery.
  7. $(document).ready(function() { $('#myFeature li') .append('<div/>') .click(function() { var $this = $(this); var $div = $this.find('div'); $div.load('foo.php?item=' + $this.attr('id'), function() { $div.show(); $this.siblings() .find('div').hide(); } ); }) });
  8. // NAVIGATION function togglePage(section) { // if the clicked section is already the current section AND we're in full page mode // minimize the current tab if (jQuery('#md_tab_'+ section).hasClass('current') && jQuery('#md_tab_'+ section + ' 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 we're NOT in full page mode // then let's go to full page mode and show the whole tab
  9. How do you write an application that uses jQuery without making a tangled mess?
  10. e key is to think of our applications as small pieces of functionality, and then write our code accordingly.
  11. Patterns & Practices http://www.flickr.com/photos/docman/3270589892/
  12. Above all else: Use jQuery as the DOM and Ajax tool it is, not as a framework.
  13. Use classes to organize your code; write methods that do exactly one thing.
  14. myApp.common.Messaging = Class.extend({ animDuration : 500, hideDelay : 1500, init : function() { this.el = $('<div class="message"/>').prependTo('body').hide(); $.subscribe('/message/notification', $.proxy(this._showMessage, this)); }, _showMessage : function(msg) { var hide = $.proxy(this._hideMessage, this); this.el.append('<p>' + msg + '</p>'); if (!this.el.is(':visible')) { this.el.slideDown(this.animDuration, $.proxy(function() { this.showTimeout = setTimeout(hide, this.hideDelay); }, this)); } else { clearTimeout(this.showTimeout); this.showTimeout = setTimeout(hide, this.hideDelay); } }, _hideMessage : function() { this.el.slideUp(this.animDuration); this._cleanup(); }, _cleanup : function() { this.el.empty(); } });
  15. Use the server to send data, not markup; use a templating system (like mustache.js) to create markup on the client side.
  16. myApp.widgets.Tools = Class.extend({ itemTemplate : '<li>' + '<input type="checkbox" name="{{description}}" checked>' + '<label for="{{description}}">{{description}}</label>' + '</li>', services : [], init : function(el) { this.el = el; this.el.delegate('input', 'click', $.proxy(this._handleChoice, this)); $.subscribe('/service/add', $.proxy(this._handleAdd, this)); }, _handleChoice : function(e) { var input = $(e.target), service = input.attr('name'); $.publish('/service/' + service + '/toggle', [ input.is(':checked') ]); }, _handleAdd : function(service) { if ($.inArray(service, this.services) >= 0) { return; } var html = Mustache.to_html(this.itemTemplate, { description : service }); this.el.append(html); } });
  17. Use a consistent pattern for de ning components that have a DOM representation.
  18. var DomThinger = Class.extend({ config : {}, init : function(el /* jQuery object */, opts /* mixin */) { this.el = el; $.extend(this.config, opts); } });
  19. Communicate between components using a centralized messaging hub (i.e. pub/sub).
  20. myApp.common.Messaging = Class.extend({ init : function() { this.el = $('<div class="message"/>').prependTo('body').hide(); $.subscribe('/message/notification', $.proxy(this._showMessage, this)); }, _showMessage : function(msg) { /* ... */ }, _hideMessage : function() { /* ... */ }, _cleanup : function() { /* ... */ } }); myApp.services._base = Class.extend({ description : '', init : function(opts) { $.subscribe('/search/term', $.proxy(this._doSearch, this)); $.publish('/message/notification', [ 'Service ' + this.description + ' added' ] ); }, _doSearch : function(term) { /* ... */ }, });
  21. DRY and maintain separation of concerns.
  22. $.proxy Take control of your functions by specifying the meaning of “this” inside of them.
  23. myApp.services._base = Class.extend({ description : '', init : function(opts) { $.subscribe('/search/term', $.proxy(this._doSearch, this)); $.publish('/message/notification', [ 'Service ' + this.description + ' added' ] ); }, _doSearch : function(term) { /* ... */ }, });
  24. $.fn.delegate All the cool kids are using it (say goodbye to $.fn.live)
  25. myApp.widgets.Saved = Class.extend({ init : function(el) { this.el = el; // event delegation for searching and removing searches this.el.delegate('li', 'click', $.proxy(this._doSearch, this)); this.el.delegate('span.remove', 'click', $.proxy(this._removeSearch, this)); }, _doSearch : function(e) { /* ... */ }, _removeSearch : function(e) { /* ... */ } });
  26. Tools for Building & Dependency Management http://www.flickr.com/photos/flightsaber/2204113449/
  27. Only a handful of solutions, none that I love.
  28. RequireJS (building and loading) LABjs (loading only) modulr (building only) Roll your own Dojo?
  29. Caveats
  30. I’ve written plenty of code that didn’t work this way. Sometimes for better, sometimes for worse.
  31. If your needs aren’t complex, don’t write complex code to solve them. is approach might be overkill for your project.
  32. $(document).ready(function() { $('#myFeature li') .append('<div/>') .click(function() { var $this = $(this); var $div = $this.find('div'); $div.load('foo.php?item=' + $this.attr('id'), function() { $div.show(); $this.siblings() .find('div').hide(); } ); }) });
  33. is approach works really well for applications with distinct but related pieces of functionality, and for applications that are always evolving.
  34. Other options for code org include plugins, object literals, and prototypal inheritance.
  35. anks. rebeccamurphey.com yayquery.com twitter.com/rmurphey http://pinboard.in/u:rmurphey/t:large-jquery-apps/ http://github.com/rmurphey/large-jquery-apps Special thanks to Alex Sexton, Paul Irish, Adam Sontag, James Burke, and Peter Higgins
Advertisement