0

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?

2 Answers 2

1

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 class="fa fa-star"></i>
  Text
</button>

Sign up to request clarification or add additional context in comments.

Comments

1

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");

1 Comment

And another point is, that you use element.innerHTML = element.textContent... Since element.textContent is only "Icon" and "Text" you also only set the innerHTML property to "Icon" and "Text". Using your methods without changing the DOM would be to replace element.innerHTML = element.textContent.replace(...) to element.innerHTML = element.innerHTML.replace(...) But be aware that that also might change your elements (if search text is "t" and element is button for exaple)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.