1

Using the following demo, I am trying to highlight some words using a <span> tag between some text.

This works correctly for one, however if I try add any more highlights to the paragraph, it either highlights the whole thing, or it clears the previous highlight before adding a new one.

How would I be able to insert multiple highlights to one paragraph?

Edit updated the link to try show how i'm adding a second highlight... It doesn't seem to work exactly the same as I have

17
  • How are you adding the more highlights? Commented Feb 5, 2014 at 13:20
  • Do you want to highlight words, or do you want to give it the position of the words? Commented Feb 5, 2014 at 13:21
  • Loop down instead of up if you want to use indices and innerHTML Commented Feb 5, 2014 at 13:22
  • 1
    I would go for a regex replace where you replace all occuring textToReplace by <span>textToReplace</span> Commented Feb 5, 2014 at 13:26
  • 1
    Whoops i'm sorry, check this one: jsfiddle.net/828vG/2 Commented Feb 5, 2014 at 13:39

1 Answer 1

1

After thinking about what you want, the only way I can see it working with your requirements is if you use a Range. The hardest bit is to convert the index into the real location of your the text you want, which took me a while but I believe this shall work

// helper function to walk DOM-tree and find end-points
// basically, converts the index of a character in terms of a HTML element
// into it's offset in a #text Node
function getTextOffset(parent, index) {
    if (parent.nodeType === 3)
        return {'node': parent, 'offset': index};
    var e = parent.childNodes[0], i = index, prev = null;
    while (1) {
        while (e.nodeType !== 3 || e === prev)
            if (e.childNodes && e.childNodes.length)
                e = e.childNodes[0];
            else if (e.nextSibling)
                e = e.nextSibling;
            else {
                while (!e.nextSibling)
                    if (!e.parentNode || e.parentNode === parent)
                        throw RangeError('Index out of range');
                    else
                        e = e.parentNode;
                e = e.nextSibling;
            }
        if (e.data.length < i)
            i -= e.data.length, prev = e;
        else
            return {'node': e, 'offset': i};
    }
}

And now all you need is to write a function to create a Range and format it..

function highlight(node, start, end) {
    var r, o, hi;
    // find text
    r = document.createRange();
    o = getTextOffset(node, start); // find start point
    r.setStart(o.node, o.offset);   // set start in range
    o = getTextOffset(node, end);   // find end point
    r.setEnd(o.node, o.offset);     // set end in range
    // now format
    hi = document.createElement('span');
    hi.style.background = 'yellow';
    hi.appendChild(r.extractContents());
    r.insertNode(hi);
    // cleanup
    r.detach();
}

highlight(containerEl, 3, 5); // invoke

DEMO

Sign up to request clarification or add additional context in comments.

4 Comments

This is... Beautiful.
Any chance you could get this working in IE8? I have tried something along the lines of this: jsfiddle.net/7u8Bc, but it still wont work out correctly... PS: JSBin doesnt seem to work in IE8
Tested in IE11 emulating IE8. IE8 does not support Range, so document.createRange; // undefined in IE8-. You might be able to use the Rangy library to make it work.
We have Rangy, was just hoping to be as pureJS as possible :) Thanks for the update though!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.