javascripthtmldom

Find text (child text nodes) and wrap it in a paragraph


I need a function that finds text (child text nodes) inside some (for this example the div) elements and wraps the text in a paragraph.

Text1
<div>
  <div>
    Text2
    <div>Text3</div>
  </div>
  <div>Text4</div>
  <p>Text5</p>
</div>
<div>Text6</div>
<div>
  Text7
  <p>Text8</p>
  Text9
</div>

tried did it like this:

const fn = (doc) => {
  const coll = [...doc.childNodes];
  coll.forEach(el => {
    if (el.tagName === 'DIV' && el.childElementCount === 0) {
      el.innerHTML = `<p>${el.innerHTML}</p>`;
    }
    if (el.childElementCount > 0) {
      el.childNodes.forEach(item => {
          if (item.nodeType === 3 && item.textContent.trim()) {
            const content = item.textContent.trim();
            item.innerHTML = `<p>${content}</p>`;
            console.log('2: ', item.innerHTML);
          }
        });
      fn(el);
    }
  });
}

but it works wrong - in if condition (el.childElementCount > 0) in console log, I got all needed nodes in p tags. but not in result. item.innerHTML = `<p>${content}</p>`; not apply it to document :(. Can anyone help to fix it?

result i need:

Text1
<div>
  <div>
    <p>Text2</p>
    <div><p>Text3</p></div>
  </div>
  <div><p>Text4</p></div>
  <p>Text5</p>
</div>
<div><p>Text6</p></div>
<div>
  <p>Text7</p>
  <p>Text8</p>
  <p>Text9</p>
</div>

Solution

  • You can use replaceWith() to replace the text node with the paragraph element

    function filterTextNode(node) {
      var textNodes = [];
      for (node = node.firstChild; node; node = node.nextSibling) {
        if (node.nodeType == 3 && node.textContent.trim()) textNodes.push(node);
        else textNodes = textNodes.concat(filterTextNode(node));
      }
      return textNodes;
    }
    
    function wrapTextNode(text) {
      var p = document.createElement('p');
      p.innerHTML = text.textContent;
      text.replaceWith(p);
    }
    
    const fn = (doc) => {
      var textNodes = filterTextNode(doc);
      textNodes.forEach(text => {
        if (text.parentNode.tagName != 'P') {
          wrapTextNode(text);
        }
      });
    }
    
    fn(document.body)
    p {
      color: red;
    }
    Text1
    <div>
      <div>
        Text2
        <div>Text3</div>
      </div>
      <div>Text4</div>
      <p>Text5</p>
    </div>
    <div>Text6</div>
    <div>
      Text7
      <p>Text8</p>
      Text9
    </div>

    References