1

How can I (efficiently - not slowing the computer [cpu]) highlight a specific part of a page?

Lets say that my page is as so:

<html>
<head>
</head>
<body>
"My generic words would be selected here" !.
<script>
//highlight code here
var textToHighlight = 'selected here" !';
//what sould I write here?
</script>
</body>
</html>

My idea is to "clone" all the body into a variable and find via indexOf the specified text, change(insert a span with a background-color) the "cloned" string and replace the "real" body with the "cloned" one.
I just think that it isn't efficient.
Do you have any other ideas? (be creative :) )

funerr
  • 7,212
  • 14
  • 81
  • 129

3 Answers3

5

I've adapted the following from my answers to several similar questions on SO (example). It's designed to be reusable and has proved to be so. It traverses the DOM within a container node you specify, searching each text node for the specified text and using DOM methods to split the text node and surround the relevant chunk of text in a styled <span> element.

Demo: http://jsfiddle.net/HqjZa/

Code:

// Reusable generic function
function surroundInElement(el, regex, surrounderCreateFunc) {
    // script and style elements are left alone
    if (!/^(script|style)$/.test(el.tagName)) {
        var child = el.lastChild;
        while (child) {
            if (child.nodeType == 1) {
                surroundInElement(child, regex, surrounderCreateFunc);
            } else if (child.nodeType == 3) {
                surroundMatchingText(child, regex, surrounderCreateFunc);
            }
            child = child.previousSibling;
        }
    }
}

// Reusable generic function
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
    var parent = textNode.parentNode;
    var result, surroundingNode, matchedTextNode, matchLength, matchedText;
    while ( textNode && (result = regex.exec(textNode.data)) ) {
        matchedTextNode = textNode.splitText(result.index);
        matchedText = result[0];
        matchLength = matchedText.length;
        textNode = (matchedTextNode.length > matchLength) ?
            matchedTextNode.splitText(matchLength) : null;
        surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
        parent.insertBefore(surroundingNode, matchedTextNode);
        parent.removeChild(matchedTextNode);
    }
}

// This function does the surrounding for every matched piece of text
// and can be customized  to do what you like
function createSpan(matchedTextNode) {
    var el = document.createElement("span");
    el.style.backgroundColor = "yellow";
    el.appendChild(matchedTextNode);
    return el;
}

// The main function
function wrapText(container, text) {
    surroundInElement(container, new RegExp(text, "g"), createSpan);
}

wrapText(document.body, "selected here");
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • My problem is that I don't know the parent node. so in that aspect it wont be that efficient. – funerr May 16 '12 at 12:41
  • @agam360: You don't need to know the parent node, only an ancestor node. You need that for an `innerHTML` solution too, and my solution will generally outperform one based on `innerHTML`, as well as being more robust (think of an attribute containing the search term, for example). – Tim Down May 16 '12 at 13:43
  • How do you cleanup anything which was wrapped, before wrapping again? see [demo](http://jsbin.com/hizihoq/edit?html,js,output) – vsync Jun 15 '17 at 15:48
1
<html>
<head>
</head>
<body>
<p id="myText">"My generic words would be selected here" !.</p>
<script>
//highlight code here
var textToHighlight = 'selected here" !';
var text = document.getElementById("myText").innerHTML
document.getElementById("myText").innerHTML = text.replace(textToHighlight, '<span style="color:red">'+textToHighlight+'</span>');
//what sould I write here?
</script>
</body>
</html>
foued611
  • 359
  • 2
  • 7
  • 1
    What are your efficiency concerns? _Efficient_ by itself doesn't provide enough information. – SelimOber May 16 '12 at 13:05
  • @SelimOber, I mean by efficient: - Low CPU consumption – funerr May 16 '12 at 13:16
  • There are two methods for your operation: 1 - with regulare expression => required many resources (CPU-heavy) 2 - looking for a string and replace without Regex (see my example) => perfomant but not limited to complex strings Sorry for my Englich – foued611 May 16 '12 at 13:44
  • 1
    The bigger problem is the inefficiency of replacing the whole `innerHTML` of a potential complex element, which will dwarf any inefficiency of using a regular expression. The alternative is DOM traversal. – Tim Down May 16 '12 at 13:46
0

Use this in combination with this and you should be pretty ok. (It is almost better than trying to implement selection / selection-highlighting logic yourself.)

Styxxy
  • 7,462
  • 3
  • 40
  • 45