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!
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(/h\d/i);
}).get();