Use .join("|") to create a Matching Group with optional | values.
Basically it will create a Regex like /(bar|foo)/ and extract the matching value using $& placeholder:
const words = ["bar", "foo"];
const regex = new RegExp(`(${words.join("|")})`, 'gi');
jQuery(function($) { // Better DOM ready. And $ alias in scope
$(".msg").html(function() {
if (regex.test(this.textContent))
return this.textContent.replace(regex, "<span class='red'>$&</span>");
});
});
.red { color: red; }
<div class="msg">select bar and foo in this FOO sentence</div>
<div class="msg">Foo enters <b>the bar</b> (HTML is lost)</div>
<div class="msg"><b>Nothing to edit here</b> (HTML is preserved)</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
Also, no need to use .each() when you need only to update the innerHTML - use directly the jQuery's .html() Method as above.
PS1: :contains is case sensitive, so it makes no sense to use it, specially if afterwards you use the RegExp's i (insensitive) flag.
PS2: if you don't want to highlight bar in bartender use the \b word boundary:
const regex = new RegExp(`\\b(${words.join("|")})\\b`, 'gi');
:containsis case sensitive, so it makes no sense to use it, specially if afterwards you use the RegExp'si(insensitive) flag.