2

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>
Lex27
  • 115
  • 10
  • "...but it works wrong." Uhh... how so? Want to try specifics? – Marc Mar 12 '20 at 20:50
  • Why your html commented out? Is it meant to be that way? – MonteCristo Mar 12 '20 at 20:50
  • sorry. html is just for example how it should work - before and after. – Lex27 Mar 12 '20 at 20:53
  • If you want the text only, only retrieve the text (i.e., `textContent`), not the HTML (`innerHTML`). Use the code in [get text of an element without children in javascript](https://stackoverflow.com/q/9955955/215552) for tips on how to get just the element's text, and not the children's. – Heretic Monkey Mar 12 '20 at 21:58

3 Answers3

5

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

User863
  • 19,346
  • 2
  • 17
  • 41
  • 1
    thank you! may be i did something wrong but this function did lot not needed p tags. i don't need wrap already wrapped text elements. ```

    Text5

    Text8

    ``` And i don't need wrap elements outside div tags. ```Text1```
    – Lex27 Mar 23 '20 at 12:30
  • 1
    @Lex27 My fault. Its fixed – User863 Mar 23 '20 at 12:36
  • give me plz some time to figure it out. If I just copy the code, I get many errors like - " TypeError: Cannot read property 'tagName' of null" and "TypeError: Cannot read property 'innerHTML' of null". – Lex27 Mar 23 '20 at 12:57
  • and it still wrap texts outside divs ```

    Text1

    ```, I fixed it ``` node.parentNode.tagName === 'DIV' ```
    – Lex27 Mar 23 '20 at 13:06
  • 1
    Pass only the div you needed `fn(document.getElementById('yourdivid'))` to wrap – User863 Mar 23 '20 at 13:09
2

By considering the childElementCount of the div we can get your desired result. Rewrite your function like below.

const fn = (document) => {
    let elements = document.getElementsByTagName('div');
    elements = Array.prototype.slice.call(elements);
    elements.forEach(el => {
      if (el.innerHTML && el.childElementCount === 0) {
        el.innerHTML = `<p>${el.innerHTML}</p>`;
        }
    });
  }
  • thank you. But i think i need use "textContent" for some occasions. example: ```
    text1

    something

    text2
    ```, should transorm to ```

    text1

    something

    text2

    ```
    – Lex27 Mar 12 '20 at 22:12
1

var elements = document.getElementsByTagName('div');
  for(var i=0;i<elements.length;i++){
    if(elements[i]===0){
      console.log(elements[i].textContent);
     
                       }
  else{
    var y=elements[i].innerHTML
console.log(y)
      }
  }
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>testing result</title>
</head>
<body>
  
<div>Text1</div>
<div>
<div>Text2</div>
<p>Text3</p>
</div>
<div>Text4</div>
</body>


</body>
</html>

i tryed this but i cant get last two div(4 and 2) like this output in the console <div>text4</div> and <div>text2</div> i hope this my help and i dont know how to print last two div innerHTML

Mo Salah
  • 11
  • 4