Thursday, 6 October 2016

Dreading Recursion

Yesterday I showed you how I processed a select to get a JSON object; I will now show you what I did with it.

Semantic UI is a CSS and JS framework which is somewhat opinionated about the way pages should be structured. I used to hate it, but after investing so much time working with it, I have grown rather fond of it. I needed to generate a set of nested accordions from the JSON object so I went all proper Compter Science and used a bit of recursion.

Recursion is something that I approach with dread, then enjoy implementing and end up feeling all proud of my success. In this case, I had the basic structure of accordions to play with (Semantic UI dictates that titles and content blocks be on the same level whereas Bootstrap dictates that titles and content be wrapped within a panel – I am not sure which I prefer in all honesty.

This is the structure of the JSON object:

var tree = {
  "One": {},
  "Two": {
    "Two point one": {},
    "Two point two": {}
  },
  "Three": {},
  "Four": {
    "Four point one": {},
    "Four point two": {}
  },
  "Five": {
    "Five point one": {},
    "Five point two": {}
  },
  "Six": {}
};

This is the original markup:

<div id="accordionHolder"></div>

This is how I populate the original markup with the accordion:

$(function() {
  $("#accordionHolder").append(buildAccordion(tree)).ready(function(){
    $(".accordion").accordion({
      "selector": {
        "trigger": ".title .icon"
      }
    });
  });
});

Finally, this is the recursive function to generate the content:

function buildAccordion(obj) {
  var accordion = $("<div></div>", {
    "class": "ui accordion"
  });
  for (var limb in obj) {
    if (obj.hasOwnProperty(limb)) {
      console.log(limb, Object.keys(obj[limb]).length);
      if (Object.keys(obj[limb]).length === 0) {
        accordion.append($("<div></div>", {
          "class": "title",
          "text": limb
        }));
      } else {
        accordion.append($("<div></div>", {
          "class": "title",
          "text": limb
        }).prepend($("<i></i>", {
          "class": "dropdown icon"
        })));
        accordion.append($("<div></div>", {
          "class": "content"
        }).append(buildAccordion(obj[limb])));
      }
    }
  }
  return accordion;
}

I also added this wee bit of CSS to indent the child accordions:

#accordionHolder .content .accordion {
  margin: 0em 0em 0em 2em !important;
}
#accordionHolder .title.active {
  font-weight: bold;
}

It is not a bad way to do it. I thought about refactoring it to do things the Bootstrap way but then started to feel that dread I mentioned before. Working example here.