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