Highligth substring javascript
by marbx
HTML
<input type="text" />
<p>
<b>JavaScript</b> is a high-level, dynamic, multi-paradigm, object-oriented, prototype-based, weakly-typed language traditionally used for client-side scripting in web browsers. JavaScript can also be run outside of the browser with the use of a framework like Node.js, Nashorn, Wakanda, or Google Apps Script. Despite the name, it is unrelated to the Java programming language and shares only superficial similarities.
Unless a tag for a framework or library is also included, a pure JavaScript answer is expected for questions with the javascript tag.
</p>
JavaScript
function highlight_text_nodes($nodes, word) {
if (!$nodes.length) {
return;
}
var text = '';
// Concatenate the consecutive nodes to get the actual text
for (var i = 0; i < $nodes.length; i++) {
text += $nodes[i].textContent;
}
var $fragment = document.createDocumentFragment();
while (true) {
// Tweak this if you want to change the highlighting behavior
var index = text.toLowerCase().indexOf(word.toLowerCase());
if (index === -1) {
break;
}
// Split the text into [before, match, after]
var before = text.slice(0, index);
var match = text.slice(index, index + word.length);
text = text.slice(index + word.length);
// Create the <mark>
var $mark = document.createElement('mark');
$mark.className = 'found';
$mark.appendChild(document.createTextNode(match));
// Append it to the fragment
$fragment.appendChild(document.createTextNode(before));
$fragment.appendChild($mark);
}
// If we have leftover text, just append it to the end
if (text.length) {
$fragment.appendChild(document.createTextNode(text));
}
// Replace the nodes with the fragment
$nodes[0].parentNode.insertBefore($fragment, $nodes[0]);
for (var i = 0; i < $nodes.length; i++) {
var $node = $nodes[$nodes.length - i - 1];
$node.parentNode.removeChild($node);
}
}
/*
* Highlights all instances of `word` in `$node` and its children
*/
function highlight($node, word) {
var $children = $node.childNodes;
var $current_run = [];
for (var i = 0; i < $children.length; i++) {
var $child = $children[i];
if ($child.nodeType === Node.TEXT_NODE) {
// Keep track of consecutive text nodes
$current_run.push($child);
} else {
// If we hit a regular element, highlight what we have and start over
highlight_text_nodes($current_run, word);
$current_run = [];
// Ignore text inside of our <mark>s
if ($child.nodeType === Node.ELEMENT_NODE &&...