Luthando Mkhwanazi
Luthando Mkhwanazi

Reputation: 13

Why is my code not affecting entire content?

I have written the following JS for my chrome project which allows you to bold random letters in a word or sentence.

The problem is that whenever there is a hyperlink in a paragraph the code only bolds random letters up until that point and the rest of the text is unaffected.

let all = document.querySelectorAll("*");
all.forEach(a => a.childNodes.forEach(b => makeRandomBold(b)));

function makeRandomBold(node) {
  if (node.nodeType !== 3) {
    return;
  }
  let text = node.textContent;
  node.textContent = "";
  text.split('').forEach(s => {
    if (s !== " " && Math.random() > .49) {
      let strong = document.createElement("strong");
      strong.textContent = s;
      node.parentNode.insertBefore(strong, node);
    } else {
      node.parentNode.insertBefore(document.createTextNode(s), node);
}

I have tried to change from the universal selector tag, to individually selecting HTML elements such as p a span. Which did not work.

Might finding random intervals and putting them in a single strong tag work? What might be causing this?

Upvotes: 1

Views: 68

Answers (1)

acarlstein
acarlstein

Reputation: 1838

Here you have one way to do it. I added comments to the code:

// The selector * will pull everything: html, head, style, script, body, etc.
// Let's indicate that we only want elements inside the body that aren't scripts,
// styles, img, or any element that wont hold a string.
let allElements = document.querySelectorAll("body > *:not(style):not(script):not(img)");

// Now, lets iterate:
allElements.forEach(elem => {
  traverseDOMToBold(elem);
});

// We want to visit all the futher children first, 
// then we can change the content
function traverseDOMToBold(elem){ 
  if (elem.hasChildNodes()){
    [...elem.children].forEach(child => {
      traverseDOMToBold(child);
    });
  }
  boldInnerHTML(elem);
}

// I like to try to create function that have a Single Responsability if possible.
function boldInnerHTML(elem){
  if (elem.innerHTML.length > 0){
    let currentString = elem.innerHTML;
    let newString = `<strong>${currentString}</strong>`;
    elem.innerHTML = currentString.replace(currentString, newString)
  }
}
<h1>Title</h1>
<p>Paragraph</p>
<div>Div</div>
<span id="parent">
  Parent Span
  <span id="child">
    Child Span
  </span>
</span>

<div>
  1.
  <div>
    2.
     <div>
      3.
     </div>
  </div>  
</div>

<ul>
 <li>A.</li>
 <li>B.</li>
 <li>
  <ul>C
   <li>C1.</li>
   <li>C2.</li>
   <li>C3.</li>
  </ul>
 </li>
</ul>

Upvotes: 1

Related Questions