Tuesday, October 12, 2010

Blogger "Related Posts" Code

In a previous post I noted that there is no easy way to write conditional code based on which categories a post belongs to. In Blogger a category is called a "label". In this site I use labels to denote which section an article belongs to. So for me "category", "label" and "section" are the same thing. (Just saying, in case my terminology gets confusing!)

This is an issue because before now, if you were reading a specific post on this site, you would have no indication of what section that article belonged to. I wish to not only make this obvious, but also present other page code based on the current section. I found the hints of a solution from an article on WebUpd8. As a step towards integration, I decided to implement their method of listing Related Posts. This article will present my optimisations to their code.

I note in passing that the code does not originate with that site and bears no attribution. I repeat my modified version here with respect to the original author, whomever that might be.

The first thing I did to clean up the code was to move the two JavaScript functions out of the in-line code, putting them instead in my usual external JavaScript file. I wish one could make other Blogger code modular in this way, but unfortunately not. An immediate benefit of this is readability, as we can get rid of all the HTML escape codes that need to be present when the code is in the Blogger template.

Here is the first function. It's quite readable even if you have little idea of how JavaScript works. It has two main functions: it reads a JSON feed and constructs different HTML elements from it. To dynamically build HTML on the calling page, each element must be hooked onto a parent element. Note var TargetElement, which must be set to the name of the initial element we will use to build our code. You can change this if, for some reason, you need to change the designator in the template code.

(I have found the best coverage of JSON feeds at Beautiful Beta. They've got handy documentation and tools to examine the structure of the feeds you are getting from your blog.)


// takes a json feed and creates an HTML-formatted list of the elements
function RelatedPostEntries(json) {
  // change the next three variables as required
  var homeUrl = 'http://www.theatreofnoise.com/';
  var maxNumberOfPostsPerLabel = 5;
  var TargetElement = 'relatedposts';

  var ul = document.createElement('ul');
  var maxPosts = (json.feed.entry.length <= maxNumberOfPostsPerLabel) ? json.feed.entry.length : maxNumberOfPostsPerLabel;

   for (var i=0; i<maxPosts; i++) {
      var entry = json.feed.entry[i];
    var alturl;
    for (var k=0; k<entry.link.length; k++) {
      if (entry.link[k].rel == 'alternate') {
        alturl = entry.link[k].href;
        break;
      }
    }
    var li = document.createElement('li');
    var a = document.createElement('a');
    a.href = alturl;

    if(a.href!=location.href) {
      var txt = document.createTextNode(entry.title.$t);
      a.appendChild(txt);
      li.appendChild(a);
      ul.appendChild(li);
    }
   }

  for (var l=0; l<json.feed.link.length; l++) {
    if (json.feed.link[l].rel == 'alternate') {
      var raw = json.feed.link[l].href;
      var label = raw.substr(homeUrl.length+13) + ':';
      var txt = document.createTextNode(label);
      var h = document.createElement('b');
      h.appendChild(txt);
      var div = document.createElement('div');
      div.appendChild(h);
      div.appendChild(ul);
      document.getElementById(TargetElement).appendChild(div);
    }
   }
}


The second function I have extended somewhat to act as a general code hook. This creates a new script element on our page that sends a JSON feed to a callback function. This might seem a very roundabout way of doing things except that it happens to be the only way to get a JSON feed from the page to a JavaScript function.


function CodeHook(url, label, callback) {
  var script = document.createElement('script');
  script.setAttribute('src', url + 'feeds/posts/default/-/' + label + '?alt=json-in-script&callback=' + callback);
  script.setAttribute('type', 'text/javascript');
  document.documentElement.firstChild.appendChild(script);
}


With those two functions in hand, the page code can now be explained. Edit your template with widget code expanded. Immediately after <data:post.body/>, or wherever else you want the output to be, put the following:


<!-- Related Posts -->
<b:if cond='data:blog.pageType == "item"'><div>
  <p /><b>RELATED POSTS</b>

  <div id='relatedposts'/>
  <script type='text/javascript'>
    // CodeHook and RelatedPostEntries reside in external javascript file

    var maxNumberOfLabels = 2;    // change as you wish

    var homeUrl = "<data:blog.homepageUrl/>";
    var numLabel = 0;
    var labelArray = new Array();

    <b:loop values='data:posts' var='post'>
    <b:loop values='data:post.labels' var='label'>
      var textLabel = "<data:label.name/>";
      var test=0;
      for (var i=0; i < labelArray.length; i++)
      if (labelArray[i] == textLabel) test=1;
      if (test==0) {
        labelArray.push(textLabel);
        var maxLabels = (labelArray.length <= maxNumberOfLabels) ? labelArray.length : maxNumberOfLabels;
        if (numLabel < maxLabels) {
          CodeHook(homeUrl, textLabel, 'RelatedPostEntries');
          numLabel++;
        }
      }
    </b:loop>
    </b:loop>
  </script>
</div></b:if>
<!-- END Related Posts -->


The first conditional ensures that this only gets run on Item pages, that is, individual post pages. This, and a placebo DIV, wrap the entire code section, an intriguing mix of JavaScript and Blogger template code. I'll explain a bit about how it works.

To begin with we have a variable, maxNumberOfLabels, that helps us control how extensive the output is going to be. Then we extract the home page URL and initialise an array to hold the label values. The Blogger loops cycle through the post and all of its labels, so the JavaScript can run on each of them. But first we check that the post label is the same as the one we are currently interested in.

The result is as you see on any of the article pages on this blog. And this gives us all the elements we need to have conditional elements on other parts of the web page. I'll get to that next time.

RELATED POSTS

No comments:

Post a Comment