skip to Main Content

I guess it’s trivial but I’m struggling with the following situation:

String (HTML delievered as text):

text<br>text<br><ul><li>text</li></ul><br>

Now I need to replace every text<br> with <div>text</div>
except if text is inside <li>/<ul>.

.replace(/(.*?)<br>/g, '<div>$1</div>')

This works fine but how to prevent <ul><li>text</li></ul><br> from beeing replaced?

3

Answers


  1. Chosen as BEST ANSWER

    This was my attempt before asking for a (shorter) regex solution:

    const dFrag = document.createDocumentFragment();
    str.textContent.split('<br>').forEach(substr => {
      const div = document.createElement('div');
      let ul;
      if (!substr) {
        substr = '<br>';
      }
      div.innerHTML = substr;
      ul = div.querySelector('ul');
      if (ul) {
        dFrag.appendChild(ul);
      } else {
        dFrag.appendChild(div);
      }
    });
    str.innerHTML = '';
    str.appendChild(dFrag);
    

    Cause I'm splitting the string at <br> the tag itself gets lost. So if no substr exists (empty line) I write <br> as substr and empty lines are getting parsed as <div><br></div>. But I was not able to catch an empty line after <ul>:

    <ul>
      <li>text</li>
    </ul>
    <br>
    text<br>
    

    In that case substr exists and the empty line gets lost.


  2. If you prefer using regex use /(?!<li.?>)(?!</li>)(?!<ul.?>)(?!</ul>)(.?)
    /g*

    const html = 'text<br>text<br><ul><li>text</li></ul><br>';
    const regex = /(?!<li.*?>)(?!</li>)(?!<ul.*?>)(?!</ul>)(.*?)<br>/g;
    const replacedHtml = html.replace(regex, '<div>$1</div>');
    console.log(replacedHtml);
    

    But it would be better if you switch to HTML parser to easily navigate and modify the structure of the HTML content

    Login or Signup to reply.
  3. "You can’t parse [HTML] with regex. […] Have you tried using an [HT]ML parser instead?"

    (a terser version can be found in the snippet below)

    function replaceTextBrWithDiv(html) {
      // Create an element that acts as a parser
      const parser = document.createElement('div');
      parser.innerHTML = html;
    
      // Modify an array-like when iterating over it may cause some issues.
      // Copy it first.
      const childNodes = [...parser.childNodes];
    
      // Index-based iterating
      for (let index = 0; index < childNodes.length; index++) {
        const node = childNodes[index];
        const nextNode = childNodes[index + 1];
    
        if (node instanceof Text && nextNode instanceof HTMLBRElement) {
          const div = document.createElement('div');
    
          // Remove text node from parser and append it to div
          div.appendChild(node);
          nextNode.replaceWith(div);
    
          // Skip next node (i.e. <br>)
          index++;
        }
      }
    
      return parser.innerHTML;
    }
    

    Try it:

    console.config({ maximize: true });
    
    function replaceTextBrWithDiv(html) {
      const parser = document.createElement('div');
      parser.innerHTML = html;
    
      parser.childNodes.forEach((node, index, nodes) => {
        const nextNode = nodes[index + 1];
        
        if (node instanceof Text && nextNode instanceof HTMLBRElement) {
          const div = document.createElement('div');
          div.appendChild(node);
          nextNode.replaceWith(div);
        }
      });
      
      return parser.innerHTML;
    }
    
    const content = 'text<br>text<br><ul><li>text</li></ul><br>';
    
    console.log(replaceTextBrWithDiv(content));
    <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