+1  A: 

You can preserve the caret position by inserting a marker element at its current location before doing your replacement on the element's innerHTML. (Using DOM methods to traverse the text nodes and searching each for the text you want would be preferable to using innerHTML, by the way).

The following works, so long as the caret is not positioned within or adjacent to the word "text". I also added a timer to prevent calling this function every time a key is pressed and to wait for the user to stop typing for half a second.

function insertCaretMarker() {
    var range;
    var markerId = "sel_" + new Date() + "_" + ("" + Math.random()).substr(2);
    if (window.getSelection) {
        var sel = window.getSelection();
        range = sel.getRangeAt(0);
        range.collapse(true);
        var markerEl = document.createElement("span");
        markerEl.appendChild(document.createTextNode("\u00a0"));
        markerEl.id = markerId;
        range.insertNode(markerEl);
    } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        range.collapse(true);
        if (range.pasteHTML) {
            range.pasteHTML("<span id=\"" + markerId + "\">&nbsp;</span>");
        }
    }
    return markerId;
}

function restoreCaret(markerId) {
    var el = document.getElementById(markerId);
    var range;
    if (el) {
        if (window.getSelection && document.createRange) {
            var sel = window.getSelection();
            range = document.createRange();
            range.setStartBefore(el);
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (document.body.createTextRange) {
            range = document.body.createTextRange();
            range.moveToElementText(el);
            range.collapse(true);
            range.select();
        }
        el.parentNode.removeChild(el);
    }
}

function preserveCaretPosition(func) {
    var id = insertCaretMarker();
    func();
    restoreCaret(id);
}

var highlightTimer;

function highlight(elem) {
    if (highlightTimer) {
        window.clearTimeout(highlightTimer);
    }
    highlightTimer = window.setTimeout(function() {
        highlightTimer = null;
        var replaceStart = '<b>';
        var replaceEnd = '</b>';
        // only replace/move cursor if any matches
        // note the spacebands - this prevents duplicates
        if (elem.innerHTML.match(/ test /)) {
            preserveCaretPosition(function() {
                elem.innerHTML = elem.innerHTML.replace(/ test /g, ' ' + replaceStart + 'test' + replaceEnd + ' ');
            });
        }
    }, 500);
}
Tim Down
WOAW... you really got it! thanks... ^^
Richard