Reputation: 2741
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
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>
Upvotes: 0
Views: 1005
Reputation: 8407
You can always use React.createElement
e.g.
React.createElement('div', null, `Hello ${this.props.toWhat}`);
But, best practice might be something like this.
// reusable Tree component
export default class Tree extends Component {
static propTypes = {
children: PropTypes.array.isRequired
}
render() {
const { children } = this.props
return (
<ol>
{children.map(leaf =>
<li key={leaf.id}>
<span>{leaf.text}</span>
{leaf.children && <Tree children={leaf.children}/>}
</li>
)}
</ol>
)
}
}
// (re)use it
function render() {
return (
<Tree children={ tree } />
);
}
You could even make the HTML Elements variable.
<Tree children={ tree } listNode="ul" listElementNode="li" />
then in the Tree component
function render() {
const {listNode: UL, listElementNode: LI} = this.props;
return (<UL></UL>);
}
Upvotes: 2
Reputation: 4812
Currently, you have a recursive function (makeOl
), which i replaced with a renderLeaf
function:
a way to render this would be:
class Tree extends React.Component {
render() {
let leafs = this.props.children
return (
<React.Fragment>
{leafs.map(this.renderLeaf)}
</React.Fragment>
)
}
renderLeaf(leaf) {
return (
<ol>
<li>
{leaf.text}
{leaf.children && leaf.children.map(this.renderLeaf)}
</li>
</ol>
)
}
}
you can then use this as: <Tree children={tree} />
Upvotes: 1