I have an algorithm which calculates the characters to highlight in text based on query.

For example, if I pass to the algorithm text "1000, Brussels, Belgium" and query "1000 bruss" it will return me [[0, 4], [6, 11]]. But now, I need to write an algorithm which will wrap the characters with <strong></strong>, so the result should be <strong>1000</strong>, <strong>Bruss</strong>els, Belgium. I wrote the algorithm, but i have a feeling it might be better or it might be solved more elegant.

const highlight = (content, query, matches) => {
  if (!query) {
    return content;
  }

  const openTag = "<strong>";
  const closeTag = "</strong>";

  let result = content;
  let shift = 0;

  matches.forEach(([startIndex, endIndex]) => {
    const s =
      openTag +
      result.slice(startIndex + shift, endIndex + shift) +
      closeTag +
      result.slice(endIndex + shift);

    if (shift) {
      result = result.slice(0, startIndex + shift) + s;
    } else {
      result = s;
    }

    shift += openTag.length + closeTag.length;
  });

  return result;
};

Is there any better way to solve the problem?

Another example:

  • Text: 'Amsterdam, North-Holland, Netherlands'
  • Query: 'amst'
  • Matches: [0, 4]

Consider using mark.js

Replacing nodes in the DOM can be tricky and lead to unintended consequences as elements shift and fire events. Although it adds a third party dependency, it has a pretty clean API and abstracts away the logic so you don't have to own it.

<!-- begin snippet: js hide: false console: true babel: false --> <!-- language: lang-js -->
var instance = new Mark(".mark-context");
instance.mark("100 bruss");
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"></script>

<p class="mark-context">
  1000, Brussels, Belgium
</p>
<!-- end snippet -->