skip to Main Content

I need to update the following HTML to wrap each <li> with <li><li-text>{li value}</li-text>

<ul><li>one</li><li>two<ul><li>three<ul><li>four<ul><li>five</li></ul></li></ul></li></ul></li></ul>

hints: I ONLY USING JS, NO JQUERY

I’m trying this, but it’s not working as expected!

var html = `<ul><li>one</li><li>two<ul><li>three<ul><li>four<ul><li>five</li></ul></li></ul></li></ul></li></ul>`.replace(/<li[^>]*>/g,'<li><li-text>$&</li-text>')

The result I’m getting is not correct! the value of list should be between <li-text></li-text> tags!

<ul><li><li-text><li></li-text>one</li><li><li-text><li></li-text>two<ul><li><li-text><li></li-text>three<ul><li><li-text><li></li-text>four<ul><li><li-text><li></li-text>five</li></ul></li></ul></li></ul></li></ul>

2

Answers


  1. Forget about parsing HTML yourself. You can use the browser’s built-in parser, select your li elements, and replace the content to be wrapped with li-text.

    const input = `<ul><li>one</li><li>two<ul><li>three<ul><li>four<ul><li>five</li></ul></li></ul></li></ul></li></ul>`
    
    const parser = new DOMParser()
    const doc = parser.parseFromString(input, "text/html")
    doc.querySelectorAll("li").forEach(li => li.innerHTML = `<li-text>${li.innerHTML}</li-text>`)
    
    const output = doc.body.innerHTML
    console.log(output)
    Login or Signup to reply.
  2. Parse the content as HTML, select all <li> elements, then replace each direct-children text node with a wrapper:

    function addWrapperToLi(html) {
      const parser = new DOMParser();
      const parsed = parser.parseFromString(html, 'text/html');
    
      const textNodes = [...parsed.querySelectorAll('li')].flatMap(
        // .childNodes contains all kinds of nodes, including elements
        element => [...element.childNodes].filter(
          // ...but we only want text nodes.
          node => node.nodeType === Node.TEXT_NODE
        )
      );
    
      textNodes.forEach(node => {
        const wrapper = document.createElement('li-text');
        wrapper.innerText = node.textContent;
        node.parentElement.replaceChild(wrapper, node);
      });
    
      return parsed.body.innerHTML;
    }
    

    Try it:

    console.config({ maximize: true });
    
    function addWrapperToLi(html) {
      const parser = new DOMParser();
      const parsed = parser.parseFromString(html, 'text/html');
    
      const textNodes = [...parsed.querySelectorAll('li')].flatMap(
        element => [...element.childNodes].filter(
          node => node.nodeType === Node.TEXT_NODE
        )
      );
    
      textNodes.forEach(node => {
        const wrapper = document.createElement('li-text');
        wrapper.innerText = node.textContent;
        node.parentElement.replaceChild(wrapper, node);
      });
    
      return parsed.body.innerHTML;
    }
    
    const testcases = [
      '<ul><li>one</li><li>two<ul><li>three<ul><li>four<ul><li>five</li></ul></li></ul></li></ul></li></ul>',
      '<ul><li>one</li><li>two<ul><li>three<ul><li>four<ul><li>five</li></ul>after four</li></ul></li></ul>after two</li></ul>'
    ];
    
    testcases.forEach(
      testcase => console.log(addWrapperToLi(testcase))
    );
    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search