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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.