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) {
              ...