Structured JavaScript

by George Brocklehurst (@georgebrock)

Presented at BarCampLondon 8,

Structured JavaScript

MyApp.imageGallery = (function() {

  var imagesURL = 'http://example.org/images/';
  var loadMoreImages = function() { … };

  return {
    showLightbox: function() { … }
  };

}());

Initialising the right scripts

Ask the HTML what it needs

<body class='with-js-image-gallery'>

Common JavaScript API
MyApp.imageGallery = (function() {
  …

  return {
    globalInit:   function() { … },
    init:         function() { … }
  };

}());
A little bit of glue…
var key, className;

for(key in MyApp) {
  if(MyApp.hasOwnProperty(key)) {

    if(typeof MyApp[key].globalInit === "function") {
      MyApp[key].globalInit();
    }

    if(typeof MyApp[key].init === "function" &&
      jQuery('body').hasClass(classNameFromKey(key))) {
      MyApp[key].init();
    }
  }
}
var classNameFromKey = function(key) {
  return 'with-js-' + key.replace(/[a-z][A-Z]/g, function(str) {
    return str[0] + '-' + str[1].toLowerCase();
  });
};
What about AJAX?
Reading HTTP headers in JavaScript

xhr.getResponseHeader("X-With-JavaScript");

Calling init twice?
<div class='gallery'>
  …
  <a href='…'>More</a>
  <a href='…'>More</a>
</div>
Defensive init functions
return {
  init: function() {
    jQuery('.gallery:not(.enhanced)')
      .addClass('enhanced')
      .each(function() {
        var moreLink = jQuery('<a></a>')
                         .text('More')
                         .click(loadMoreImages);

        jQuery(this).append(moreLink);
      });
  }
};
This all sounds like a lot of work
Requesting JavaScript
def use_javascript_for(feature)
  @js_features ||= []
  @js_features << feature.to_s.downcase
  if request and request.xhr?
    header = @js_features.uniq.join(' ')
    response.headers['X-With-JavaScript'] = header
  end
end

For example:

<div class='gallery'> … </div>
<% use_javascript_for 'image-gallery' %>
Adding the classes to the body
def javascript_feature_classes
  @js_features ||= []
  @js_features.uniq.map{ |f| "with-js-#{f}" }.join(" ")
end

For example: <body class='<%= javascript_feature_classes %>'>

Any questions?

George Brocklehurst

“georgebrock” on Twitter, GitHub, etc.

These slides with notes and links:
georgebrock.github.com/talks/barcamplondon8/