I’ve been pondering this for a while but cannot come up with a working solution. I can’t even psuedo code it…
Say, for example, you have a page with a heading structure like this:
<h1>Heading level 1</h1> <h2>Sub heading #1</h2> <h2>Sub heading #2</h2> <h3>Sub Sub heading</h3> <h2>Sub heading #3</h2> <h3>Sub Sub heading #1</h3> <h3>Sub Sub heading #2</h3> <h4>Sub Sub Sub heading</h4> <h2>Sub heading #4</h2> <h3>Sub Sub heading</h3>
Using JavaScript (any framework is fine), how would you go about producing a list like this: (with nested lists)
<ol> <li>Heading level 1 <ol> <li>Sub heading #1</li> <li>Sub heading #2 <ol> <li>Sub Sub heading</li> </ol> </li> <li>Sub heading #3 <ol> <li>Sub Sub heading #1</li> <li>Sub Sub heading #2 <ol> <li>Sub Sub Sub heading (h4)</li> </ol> </li> </ol> </li> <li>Sub heading #4 <ol> <li>Sub Sub heading</li> </ol> </li> </ol> </li> </ol>
Everytime I try and begin with a certain methodology it ends up getting very bloated.
The solution needs to traverse each heading and put it into its appropriate nested list – I keep repeating this to myself but I can’t sketch out anything!
Even if you have a methodology in your head but haven’t got time to code it up I’d still like to know it! 🙂
Thank you!
Answer
First, build a tree. Pseudocode (because I’m not fluent in Javascript):
var headings = array(...); var treeLevels = array(); var treeRoots = array(); foreach(headings as heading) { if(heading.level == treeLevels.length) { /* Adjacent siblings. */ if(heading.level == 1) { treeRoots[] = heading; // Append. } else { treeLevels[treeLevels.length - 2].children[] = heading; // Add child to parent element. } treeLevels[treeLevels.length - 1] = heading; } else if(heading.level > treeLevels.length) { /* Child. */ while(heading.level - 1 > treeLevels.length) { /* Create dummy headings if needed. */ treeLevels[] = new Heading(); } treeLevels[] = heading; } else { /* Child of ancestor. */ treeLevels.remove(heading.level, treeLevels.length - 1); treeLevels[treeLevels.length - 1].children[] = heading; treeLevels[] = heading; } }
Next, we transverse it, building the list.
function buildList(root) { var li = new LI(root.text); if(root.children.length) { var subUl = new UL(); li.children[] = subUl; foreach(root.children as child) { subUl.children[] = buildList(child); } } return li; }
Finally, insert the LI
returned by buildList
into a UL
for each treeRoots
.
In jQuery, you can fetch header elements in order as such:
var headers = $('*').filter(function() { return this.tagName.match(/hd/i); }).get();