[ Team LiB ] Previous Section Next Section

15.3 Automating the Search-and-Replace of Body Content

NN n/a, IE 4(Win)

15.3.1 Problem

You want to offer a search-and-replace function for body content.

15.3.2 Solution

This recipe works only in Internet Explorer 4 or later for Windows because of its heavy dependence upon the TextRange object (not implemented in IE/Mac through Version 5.x). On the plus side, it also works with textarea element content.

Use the rangeReplace.js library (Example 15-1 in the Discussion) to automate search and replace operations with the IE TextRange object. You have your choice of two functions, depending on the type of search-and-replace user experience you prefer. Invoke the srBatch( ) function for unprompted batch replacement:

srBatch(document.body, "Law", "legislation", false, false);

The five parameters specify:

  • A reference to the element to become the text range

  • A string to search for

  • A string to replace found strings

  • A Boolean for case-sensitive search

  • A Boolean for whole-word matches

The companion srQuery( ) function takes the same set of arguments, but highlights each match, and prompts the user to confirm each replacement:

srQuery(document.body, "Law", "legislation", false, false);

To undo the replacements performed by the last invocation of either function, invoke the undoReplace( ) function with no arguments.

15.3.3 Discussion

Example 15-1 shows the rangeReplace.js library. Link it into any document that requires this functionality.

Example 15-1. The rangeReplace.js library
// Global range object variable
var rng;
   
// Return TextRange.findText( ) third parameter arguments
function getArgs(caseSensitive, wholeWord) {
    var isCaseSensitive = (caseSensitive) ? 4 : 0;
    var isWholeWord = (wholeWord) ? 2 : 0;
    return isCaseSensitive ^ isWholeWord;
}
   
// Unprompted search and replace
function srBatch(container, search, replace, caseSensitive, wholeWord) {
    if (search) {
        var args = getArgs(caseSensitive, wholeWord);
        rng = document.body.createTextRange( );
        rng.moveToElementText(container);
        clearUndoBuffer( );
        for (var i = 0; rng.findText(search, 1000000, args); i++) {
            rng.text = replace;
            pushUndoNew(rng, search, replace);
            rng.collapse(false)  ;   
        }
    }
}
   
// Prompted search and replace
function srQuery(container, search, replace, caseSensitive, wholeWord) {
    if (search) {
        var args = getArgs(caseSensitive, wholeWord);
        rng = document.body.createTextRange( );
        rng.moveToElementText(container);
        clearUndoBuffer( );
        while (rng.findText(search, 10000, args)) {
            rng.select( );
            rng.scrollIntoView( );
            if (confirm("Replace?")) {
                rng.text = replace;
                pushUndoNew(rng, search, replace);
            }
            rng.collapse(false)  ;
        }    
    }
}
   
/****************
    Undo Buffer
*****************/
// Temporary storage of undo information
var undoObject = {origSearchString:"",newRanges :[  ]};
   
// Store original search string and bookmarks of each replaced range
function pushUndoNew(rng, srchString, replString) {
    undoObject.origSearchString = srchString;
    rng.moveStart("character", -replString.length);
    undoObject.newRanges[undoObject.newRanges.length] = rng.getBookmark( );
}
   
// Empty array and search string global
function clearUndoBuffer( ) {
    undoObject.origSearchString = "";
    undoObject.newRanges.length = 0;
}
   
// Perform the undo
function undoReplace( ) {
    if (undoObject.newRanges.length && undoObject.origSearchString) {
        for (var i = 0; i < undoObject.newRanges.length; i++) {
            rng.moveToBookmark(undoObject.newRanges[i]);
            rng.text = undoObject.origSearchString;
        }
        clearUndoBuffer( );
    }
}

The library begins with a helper function, getArgs( ), invoked by both of the main search-and-replace functions. The IE TextRange object's findText( ) method, used in both of the main functions, takes three parameters. The first is the string to look for, followed by an integer of how many characters of the range to search through, and an integer corresponding to search detail codes. The four possible codes are: 0 (match partial words); 1 (match backwards); 2 (match whole words); or 4 (match case). Using binary arithmetic, these codes can be combined into a single value denoting two or more of these switches being on. The getArgs( ) converts the two Boolean argument values to the appropriate binary code for findText( ).

Both of the search-and-replace functions, srBatch( ) and srQuery( ), operate the same way to create the text range, set the boundaries to the referenced element, clear the undo buffer (described next), and perform the search-and-replace operation via the TextRange's findText( ) method. The difference between the two functions is that srQuery( ) selects each found match to highlight the text, and then asks the user whether the highlighted text should be replaced.

Both search-and-replace functions are supplemented by the same undo capabilities. A global object (undoObject) keeps track of the changes made during a search-and-replace execution, using the IE TextRange bookmark feature (which preserves a range definition for reuse later). At each replacement operation, the pushUndoNew( ) function preserves information about the operation in the undoObject object's properties. Before the function stores the range, however, it adjusts the range to encompass the newly inserted text so that it can be selected and removed later.

Prior to each search-and-replace traversal, the values of undoObject are cleared via the clearUndoBuffer( ) function so as not to interfere with previous operations. When the user wishes to undo the operation, the undoReplace( ) function iterates through the ranges stored in undoObject, and replaces those ranges with the original search string.

The first argument of either of the search-and-replace functions is a reference to the HTML container that bounds the text you wish to search. While you can supply a reference to the entire body if you like, doing so means that the operations will occur on things like form control labels and other elements you may not wish to have included. Therefore, structure your HTML document so that the target content is separate from any fixed content you don't want changed. You can always wrap a collection of paragraphs and their headings in an arbitrary span element to provide the necessary container.

Don't confuse the TextRange bookmark with the kinds of URL bookmarks you save in your browser. A bookmark value, derived from the rangeObject.getBookmark( ) method, is a string containing data that is gibberish to the human eye but is interpreted by the browser as a complete range specification. Supplying a bookmark value to the rangeObject.moveToBookmark( ) method sets the range to whatever specification had been preserved before. You can store as many range bookmarks as you like.

The W3C DOM Range object, which shares some characteristics of the IE TextRange object, isn't sufficiently flexible or powerful to accomplish the same search-and-replace tasks. Part of this is the implementations seen thus far in Netscape 6 and 7. Perhaps, in time, both the standard and browser implementation will improve enough to be practical for a search-and-replace operation.

15.3.4 See Also

The introduction to this chapter for information about text ranges.

    [ Team LiB ] Previous Section Next Section