Hash Signaling Made Easy
   Implementing Hash Signaling on
      Existing Django Projects


            David Gouldin
             @dgouldin
What is hash signaling?

    /foo/bar/ ➛ /baz/

  /#/foo/bar/ ➛ /#/baz/
Why would you do that?!

 • Continuous media playback
 • Transport efficiency
 • Seamless page transitions
 • Page render speed
First Cut Goals

• Use existing views and templates
• No-JS graceful fallback
• SEO friendly
• No impact on DOM structure
Solution
 HTML Comments!
<!-- block foo -->
<div>content here</div>
<!-- endblock foo -->
{% ajaxblock %}
• Subclass of {% block %}
• Emits HTML comments
• Comments include:
 • The name of the block
 • The block’s base template name
 • A hash of the block’s content
{% ajaxblock %}
   my_app/foo.html
      {% ajaxblock foo %}
      <div>content here</div>
      {% endblock %}

                     ↓
<!-- block foo:my_app/foo.html
57a29c950da45a470d382a679b9b4abf -->
<div>content here</div>
<!-- endblock foo:my_app/foo.html -->
But there’s a catch ...

• Javascript is normally developed with the
  intention of a a single page lifespan
• Global state is implicitly unloaded by the
  browser on navigation
• Hash signaling abnormally prolongs the life
  of a page
jQuery.lifecycle.js
A helper plugin to deal with the effects of an
       unnaturally long page lifespan

$.lifecycle.register(
‘a_cycle’, [‘foo’], {
  load: function(cycle) {
     ...
  },
  unload: function(cycle) {
     ...
  }
});
jQuery.lifecycle.js
  Cycles provide utility functions which perform
     symmetric load and unload operations

cycle.bind           !   $.bind
cycle.setTimeout     !   window.setTimeout
cycle.setInterval    !   window.setInterval
cycle.addScript      !   <script></script>
Hijacking page rendering
Hook all events which request a new page
   $('a:not(.no-ajax)').live('click', function(){
     var href = $(this).attr('href');
     window.location.hash = '#' + href;
     return false;
   });


Replace old blocks with new content
   $(window).bind('hashchange', function(){
     $.get(location.hash.substr(1), function(html) {
         $.lifecycle.replaceBlocks(html);
     }
   });
lifecycle.replaceBlocks
• Get all blocks from the old and new
  documents
• Compare block hashes to determine which
  ones need replacement
• Run unloader for all cycles registered in
  blocks to be replaced
• Replace block contents (which registers
  new cycles) and run loaders for replaced
  blocks
Clean URLs
/lorem/ipsum/dolor/sit/amet/#/foo/   BAD

            /a/#/foo/                GOOD
Clean URLs
direct_to_template view from base template
   url(r'^a/$',
   'django.views.generic.simple.direct_to_template',
   {'template': 'base.html'})

redirect to your base view in javascript
   <script>
   var ajaxExemptPaths = ['/a/'];
   if (ajaxExemptPaths.indexOf
   (window.location.pathname) === -1) {
       window.location = '/a/#' +
   window.location.pathname + window.location.search;
   }
   </script>
To be released soon

github.com/dgouldin/django-hashsignal




   (Contingent upon the approval of my employer)

Hash Signaling Made Easy

  • 1.
    Hash Signaling MadeEasy Implementing Hash Signaling on Existing Django Projects David Gouldin @dgouldin
  • 2.
    What is hashsignaling? /foo/bar/ ➛ /baz/ /#/foo/bar/ ➛ /#/baz/
  • 3.
    Why would youdo that?! • Continuous media playback • Transport efficiency • Seamless page transitions • Page render speed
  • 4.
    First Cut Goals •Use existing views and templates • No-JS graceful fallback • SEO friendly • No impact on DOM structure
  • 5.
    Solution HTML Comments! <!--block foo --> <div>content here</div> <!-- endblock foo -->
  • 6.
    {% ajaxblock %} •Subclass of {% block %} • Emits HTML comments • Comments include: • The name of the block • The block’s base template name • A hash of the block’s content
  • 7.
    {% ajaxblock %} my_app/foo.html {% ajaxblock foo %} <div>content here</div> {% endblock %} ↓ <!-- block foo:my_app/foo.html 57a29c950da45a470d382a679b9b4abf --> <div>content here</div> <!-- endblock foo:my_app/foo.html -->
  • 8.
    But there’s acatch ... • Javascript is normally developed with the intention of a a single page lifespan • Global state is implicitly unloaded by the browser on navigation • Hash signaling abnormally prolongs the life of a page
  • 9.
    jQuery.lifecycle.js A helper pluginto deal with the effects of an unnaturally long page lifespan $.lifecycle.register( ‘a_cycle’, [‘foo’], { load: function(cycle) { ... }, unload: function(cycle) { ... } });
  • 10.
    jQuery.lifecycle.js Cyclesprovide utility functions which perform symmetric load and unload operations cycle.bind ! $.bind cycle.setTimeout ! window.setTimeout cycle.setInterval ! window.setInterval cycle.addScript ! <script></script>
  • 11.
    Hijacking page rendering Hookall events which request a new page $('a:not(.no-ajax)').live('click', function(){ var href = $(this).attr('href'); window.location.hash = '#' + href; return false; }); Replace old blocks with new content $(window).bind('hashchange', function(){ $.get(location.hash.substr(1), function(html) { $.lifecycle.replaceBlocks(html); } });
  • 12.
    lifecycle.replaceBlocks • Get allblocks from the old and new documents • Compare block hashes to determine which ones need replacement • Run unloader for all cycles registered in blocks to be replaced • Replace block contents (which registers new cycles) and run loaders for replaced blocks
  • 13.
  • 14.
    Clean URLs direct_to_template viewfrom base template url(r'^a/$', 'django.views.generic.simple.direct_to_template', {'template': 'base.html'}) redirect to your base view in javascript <script> var ajaxExemptPaths = ['/a/']; if (ajaxExemptPaths.indexOf (window.location.pathname) === -1) { window.location = '/a/#' + window.location.pathname + window.location.search; } </script>
  • 15.
    To be releasedsoon github.com/dgouldin/django-hashsignal (Contingent upon the approval of my employer)