JSFiddle - React, Tailwind, and code Playground
by RichieHindle
HTML
<p><span id='top'>Hello <b>all<i> the</i></b> world!</span></p>
<p><button id='highlight_button'>Highlight</button>
<button id='unhighlight_button'>Unhighlight</button></p>
CSS
.highlight {
background-color: #aff;
}
b {
letter-spacing: 0.2em;
}
JavaScript
// Returns a highlighted span containing the given text.
function make_highlight(text) {
var span = document.createElement('span');
span.className = 'highlight';
span.innerText = text;
return span;
}
// Returns an unhighlighted text node containing the given text.
function make_plain(text) {
return document.createTextNode(text);
}
// Highlight the given character range within the given element.
function highlight_range(root_elt, start, end) {
// Our current character position within the text.
var pos = 0;
// This recursive helper function does the actual work, replacing any text
// that lies within the given range with highlighted spans.
function highlight_elt(elt) {
for (var i = 0; i < elt.childNodes.length; i++) {
var child = elt.childNodes[i];
if (child.nodeType == 1) {
// Child is an Element node; recurse.
highlight_elt(child);
} else if (child.nodeType == 3) {
// Child is a text node; highlight it if it intersects the range.
var text = child.nodeValue;
var straddlesStart = (pos < start && pos + text.length > start);
var straddlesEnd = (pos < end && pos + text.length > end);
if (pos >= start && pos + text.length <= end) {
// It's entirely within the range; highlight the whole thing.
elt.replaceChild(make_highlight(text), child);
} else if (straddlesStart && !straddlesEnd) {
// Split into two, second half highlighted.
var first = make_plain(text.substring(0, start-pos));
var second = make_highlight(text.substring(start-pos));
elt.replaceChild(second, child);
elt.insertBefore(first, second);
i++; // Skip the new child.
} else if (!straddlesStart && straddlesEnd) {
...