Home > Mobile >  Avoid removing inner HTML while replacing text
Avoid removing inner HTML while replacing text

Time:01-08

I'm trying to highlight the text of HTML elements without removing their inner HTML. Here is an example:

const buttonEl = `<button>
  <span>
    Icon
  </span>
  Text
</button>
`;

document.body.innerHTML = buttonEl;

const foundButtonEl = document.querySelector("button");
const elements = [];

elements.push(foundButtonEl);

function addHighlight(elements, text) {
  const regex = new RegExp(text);

  elements.forEach((element, index) => {
    element.innerHTML = element.textContent.replace(
      regex,
      `<mark>$&</mark>`
    );
  });
}

addHighlight(elements, "T");

If you run this example, you'll notice that T will be highlighted, but the <span> tags will be removed.

How to highlight the text without affecting the inner HTML?

CodePudding user response:

You change the inner HTML of button. So one way would be to put a span around your Text and use an id and then use document.getElementById("text") I guess. See for example

const buttonEl = `<button>
    <span>
      Icon
    </span>
    <span id="text">
    Text
    </span>
  </button>
  `;

  document.body.innerHTML = buttonEl;

  const foundButtonEl = document.getElementById("text");
  const elements = [];

  elements.push(foundButtonEl);

  function addHighlight(elements, text) {
    const regex = new RegExp(text);

    elements.forEach((element, index) => {
      element.innerHTML = element.textContent.replace(
        regex,
        `<mark>$&</mark>`
      );
    });
  }

  addHighlight(elements, "T");

CodePudding user response:

The .textContent property returns (or set) the text content of all children and since the <button> element may contain further nested elements, its strict content might be mixed with promiscuous content coming from other elements like your span icon.

Here in this demo I added a layer that will convert the button content so that every single text node found will be removed and changed to a span.textnode so that later when you'll need to style the text content adding the <mark> it will be strightforward to change ONLY the contents of inner span.textnode elements.

To better show the concept I also added the fontawesome asset and style the icon inside the button.

const buttons = document.querySelectorAll("button");

function addHighlight(buttons, text){
  buttons.forEach(button => {
    initButton(button);
    highlightTextInButton(button, text);
  });
}

function initButton(button){
 //for each child node in button
 for (var i = 0; i < button.childNodes.length; i  ) {
    const child = button.childNodes[i];
    //if it's of type TEXT_NODE
    if (child.nodeType === Node.TEXT_NODE) {
      //creates a new span.textnode
      const span = document.createElement('span');
      span.classList.add('textnode');
      //with this same content
      span.textContent = child.nodeValue;
      //removes the text node
      child.remove();
      //adds the new span.textnode
      button.append(span);
    }
  }
}

function highlightTextInButton(button, text){
  const regex = new RegExp(text);
  button.querySelectorAll(':scope > .textnode')
    .forEach(textnode=>{
      const textContent = textnode.textContent;
      textnode.innerHTML = textContent.replace(regex,`<mark>$&</mark>`);
    });
}

addHighlight(buttons, "T");
.textnode{
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8 y gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />

<button>
  <i ></i>
  Text
</button>

  • Related