I've got two functions, one to parse an html string to get its headers into an array
const str = "<h1>test-1<h1><h2>test1-1<h2><h3>test1-1-1</h3><h1>test1-2<h1><h2>test1-2-1</h2><h3>test1-2-2</h3><h1>test-2</h1><h1>test-3</h1><h1>test-4</h1> " const wrapper = document.createElement('div'); wrapper.innerHTML = str.trim(); let tree = []; let leaf = null; for (const node of wrapper.querySelectorAll("h1, h2, h3, h4, h5, h6")) { const nodeLevel = parseInt(node.tagName[1]); const newLeaf = { level: nodeLevel, text: node.textContent, children: [], parent: leaf }; while (leaf && newLeaf.level <= leaf.level) leaf = leaf.parent; if (!leaf) tree.push(newLeaf); else leaf.children.push(newLeaf); leaf = newLeaf; } and another to parse those headers into a list for a table of contents feature
const ol = document.createElement("ol"); (function makeOl(ol, leaves) { for (const leaf of leaves) { const li = document.createElement("li"); li.appendChild(new Text(leaf.text)); if (leaf.children.length > 0) { const subOl = document.createElement("ol"); makeOl(subOl, leaf.children); li.appendChild(subOl); } ol.appendChild(li); } })(ol, tree); it outputs a string like this
"<ol><li>test-1<ol><li>test1-1<ol><li>test1-1-1</li></ol></li><li>test1-2<ol><li>test1-2-1</li><li>test1-2-2</li></ol></li></ol></li><li>test-2</li><li>test-3</li><li>test-4</li></ol>" which renders to something like
- test-1
- test1-1
- test1-1-1
- test1-2
- test1-2-1
- test1-2-2
- test1-1
- test-2
- test-3
- test-4
I'm still getting used to the jsx part of React and I'm wondering how to convert that function so that the ol's and li's are React/jsx elements rather than a string of raw html as that'd require another step to render eg.
<div dangerouslySetInnerHTML={{__html: olString}} /> the way I'm using to using jsx with arrays is something like this
const list = tree.map((headers) => <li>{headers.value}</li>) <div><ul>{list}</ul></div>