Searching Body Text with textRange: Building on the Script and the VBScript Alternative
Once you have the basic script for performing text searches within the text of web pages, you may want to extend the functionality of this and add a button that will find the next occurrence of the word you have searched for. This will improve the usability of the whatever web page you add this to.
Contributed by Dan Wellman Rating: / 17 June 30, 2005
Initially, you may as well add the second button to the main web page (findingText.htm). Add the following block of code directly beneath the first button, using the appropriate table tags to nestle it next to the original button:
button id=buttonNext title="Click to find the next match" onclick="searchNext()">Find Next...</button>
Now open the behaviour.js file and you can add the corresponding searchNext() function. Before doing this however, you need to know a little bit more about how the findText method works. When it finds a match for the word that has been searched for, it reduces the textRange object so that it just encompasses the matched word. What you need to do is move the text range forward by one word and then expand the textRange once more so that it covers the remaining body text.
Add the new searchNext function below the textSearch function in the behaviour file:
function searchNext() { if (searchWord=="") { window.alert ("No Search value entered"); return false } range.move("word", 1); range.moveEnd("word", 1000); match = range.findText(searchWord,0,2); if (match != false) { range.select(); } else { window.alert ("Couldn't find any more instances of '" + searchWord + "'"); } }
As this function also needs to make use of the searchWord, range and match variables, these need to be defined as global variables by declaring them outside of a function. Add them to the top of the file:
var strDialogValue var range var match
Now both of the functions can make use of the variables and share their values. The second function is very similar to the first except that instead of just returning false in the initial if statement, it also sends an alert to the user if no search word has been entered (if, for example, the user has clicked the find button, but then clicked cancel and then clicked the find next button.) It also uses the move method to move the range forward by one word, and uses the moveEnd method to move the end of the range forward by 1000 words. As there are less than one thousand words in the body of the page, this will definitely include the rest of the text. If no further matches are found, an alert is sent advising of this.
This should now be a fully working script, but it can still produce errors if it is not used correctly. If someone clicks on the find next button without having first clicked on the find button and entered a search word, the page errors. As it is possible for this to happen, is bound to happen and you should therefore incorporate into your script something that will detect whether this has happened and cater for it accordingly without producing errors.
Fortunately, this is fairly easy. All you need to do is add a flag to the first function that is set when that function is called (when the find button is clicked.) If the second button is clicked and that flag has not been set, you can produce an alert that tells the user to click the find button first and enter a search term, thus preventing the error. First add another global variable to the top of the page:
var button1clicked
Now, in the first function (textSearch), set the flag to 1 at the beginning of the function:
button1clicked = 1
This tells the script that the find button has definitely been clicked. Now a simple if statement at the top of the second function to test the value of the flag variable quickly lets the script accordingly if the first button has not been clicked:
if (button1clicked != 1) { window.alert ("Please enter a search value first"); return false }
Like we added an if statement in the first article to ensure that the page did not error when no value was passed back to a variable that was expecting one, this statement simply checks that button1clicked does equal 1, but if not, alerts the user.
Finally, if a user clicks the x button at the top of the dialog box instead of clicking the cancel button an alert informs the user that ‘undefined’ couldn’t be found, which is rather silly as undefined was not entered as a search term. This happens because in this case, as when the user searches for a word that is not found in the body text, match does equal false and the appropriate alert is fired. It would be better all round if nothing happened when the x button was clicked, just like when the cancel button is clicked. Change the first if statement in the first function to this:
if (searchWord=="") { return false } else if (searchWord==null) { return false }
Now test it. If you search for a word that is not present in the text, you still get the "couldn’t find ‘word’" message, and if you click the x button or the cancel button on the search dialog, nothing happens and no errors occur. Similarly, the second if statement of the second function can also be modified in the same way so that if a user opens the search dialog window (thus setting button1clicked to 1) but then closes it without entering a search word and then clicks the find next button, an error message more appropriate is displayed:
if (searchWord=="") { window.alert ("No Search value entered"); return false } else if (searchWord==null) { window.alert("No search value entered"); return false }
The script should now be able to handle anything a user throws at it.
As promised, I will now give you the VBScript variation of the whole script. Some may question what the point of using VBScript actually is when the JavaScript version does everything that is needed from it? I’m including this s o that people can choose whichever version they use depending on whichever language they prefer. Additionally, as VBScript is basically a cut-down version of Visual Basic, learning VBScript is an excellent primer for those wishing to progress to application programming with full VB or even VB.NET. Those that don’t want to know should look away now.
The VBScript file in its entirety then is as follows. I won’t go through the code step by step because you should already be able to tell what is going on in the file:
dim searchWord dim range dim match dim button1clicked
sub buttonSearch_onclick() button1clicked = 1 set range = document.body.createTextRange() searchWord=window.showModalDialog("Searchwindow.htm", null,"dialogWidth:300px;dialogHeight:200px;status:no;help:no;") if searchWord="" then exit sub end if
match = range.findText(searchWord,0,2) if match <> false then range.select() else MsgBox "Couldn't find " & searchWord,vbExclamation,"No Match" end if end sub
sub buttonNext_onclick() if searchWord="" then MsgBox "No search value entered",vbCritical, "No Search Word" exit sub end if if button1clicked <> 1 then MsgBox "Please enter a search value first", vbCritical, "No Search Word" exit sub end if range.move "word", 1 range.moveEnd "word", 1000 match = range.findText(searchWord,0,2) if match <> false then range.select() else MsgBox "Couldn't find any more instances of " & searchWord,vbExclamation,"No Further Matches" end if end sub
Initially, the global variables are defined using the dim keyword instead of the var keyword. This is standard practice in VBScript. Next up, although VBScript does support the use of functions, this script has made use of subs instead.
The main difference between subs and functions is that functions return values whereas subs do not. The set keyword is used to set the value of the range variable equal to the textRange we have defined.
Another difference is the way in which alerts are defined. The MsgBox function is used in place of the alert method and you’ll see that this gives you greater control over the general working of the alert. Several of the MsgBox elements have been set as vbExclamation and several as vbCritical. Not only does this change the icon that appears on the alert, it also changes the sound that is associated with it (provided the users’ sound settings cater for this.) Also, instead of specifying a return of false following a search term not being entered, you can simply exit the subroutine. Syntactically, these are the main differences between the two scripts. The search window does not need to be changed in anyway; the JavaScript ModalDialog window will happily pass the required value back to the VBScript file without causing any problems. You may also have noticed that some of the additional error handling if statements have been left out as the some of the user actions do not cause errors with the VBScript in the same way as they do with JavaScript.
Before this will work properly, you need to adjust the main HTML page that calls the script. Firstly, the file referenced in the script src attribute needs to be changed to behaviour.vbs or the JavaScript file will still be called, secondly, you need to remove the onclick event handlers from the button code. The subs in the VBScript file have the onclick events tacked on to the end of the button names in the first line of the sub.
As a point of interest; once a word has been found and highlighted, it is easy to use the range to replace the word searched for as well. Theoretically, another button could be added to the example web page that called a textReplace() function. The code this contained could produce a second dialog window which asked for a word to replace the highlighted word with.
It is then simply a case of setting range.text equal to the new word variable. While it is difficult to think of the point in using the replace method in this example, I’m sure there are cases when it would be useful, a simple script-based spell checking function added to form text area’s is one possibility.
As I mentioned in the first article, both of these scripts will work only on IE browsers. The JavaScript version may work on IE on other platforms such as the MAC (I don’t know as I haven’t tested it), but the VBScript will only work on MS platforms. The most popular alternative browser to IE at the moment is FireFox, a browser that uses the Gecko rendering engine to draw the various elements that make up web pages in the browser. This is also used by Netscape and Mozilla browsers so having a script that will be compatible with Gecko will clear up any compatibility issues between all major browsers. Provided you have some kind of detection model in place that can refer different browsers to different areas of your site, you can have the IE only script of choice running on one section, and the Gecko based script residing on another section. A gecko compatible script is the subject of discussion in article three.