0

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

  1. test-1
    1. test1-1
      1. test1-1-1
    2. test1-2
      1. test1-2-1
      2. test1-2-2
  2. test-2
  3. test-3
  4. 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> 
2
  • I'm not sure what you're asking here. JSX is just a syntax specification. Are you just trying to rebuild your parsed HTML tree as a tree of React element nodes? Commented May 22, 2018 at 13:59
  • @LINKIWI, exactly Commented May 22, 2018 at 14:07

2 Answers 2

2

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>); } 
Sign up to request clarification or add additional context in comments.

7 Comments

While I'm pretty sure this is what OP is trying to accomplish, to answer the question directly of how to convert into JSX syntax, you could do const Elem = 'li'; return <Elem>...</Elem>
I´m writing this at the moment. :)
do you have a unqiue ´id´ in every of your list elements?
seems to create empty ol tags after each h3
well, that is then some implementation issue. I can quite confidently assure you, that my answer is working in general. I would suggest, you update your code/question with what you got. alternatively, create a new question with the code you have and the problem/errors you get.
|
1

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} />

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.