Searching Body Text with textRange, part 1: The Basic Script
Many websites have search engines that allow visitors to search through the pages of the entire site. That's good, but what if you have single pages that hold a large amount of content by themselves? You might want to provide a way to search through an individual page of your website; your visitors will be grateful for the convenience. This article helps you get started.
Contributed by Dan Wellman Rating: / 17 June 23, 2005
Having a search engine built into your site that allows visitors to search through the pages of your website will add value to almost any site, more so as the site gets bigger. But a search engine such as this won't help visitors search through the text on one particular page of your site. Most of the time, this isn't particularly necessary, but for websites made up of pages that contain a lot of information, this kind of search capability is an excellent feature. These days, there is so much information available, from such a wide variety of sources, that if you don't make it as easy as possible for people to find the information they are looking for with the minimum amount of manual scanning of text, they will go elsewhere to find it. A large percentage of surfers are rapidly turning into mouse potatoes (couch potatoes of the 21st century).
Fortunately, IE provides the textRange() object that can be used to turn either a block of text or an entire document (including HTML if you so desire) into a textRange, upon which various methods can be applied. Once such method is the findText() method, that can be used to find a particular letter, word, or sentence. A combination of methods can be used in effect to create an in-document search engine with a scope of the entire body of the document.
There are several ways in which this could be achieved. While the method that this example is based around may not be the easiest, it provides us with an ideal opportunity to look at some other aspects of JavaScript, such as dialog boxes and exception handling. Okay, exception handling may be a slightly grandiose title for what we'll be doing, but it serves as a good introduction to thinking about what a user of your web page could do to it that is outside the normal operational range of parameters, and how you can cope with this behavior if and when it occurs (which it undoubtedly will!).
I will provide the main script in both JavaScript and VBScript variants. Which one you choose to use will depend largely on personal preference as both scripts work equally as well.
I should point out now that this script (both the JavaScript and VBScript variants) will only work on Microsoft IE browsers. Not only is the textRange object only implemented by IE, and the findText method also only used in IE (although other browsers do support a createRange method), but the showModalDialog function that is used to call the search window is also an IE only method (although, the prompt method could be used in place of this to produce a dialog box to capture the search word.)
In reality, this issue should only affect a minority of your website visitors, as current trend analysis reveals that up to 88 percent of US-based surfers still use IE as their primary browser. This figure is dropping slowly as more users take up Firefox, and part of the current 88 percent may actually be using the Opera browser, which often reports itself as IE. Nevertheless, while designing scripts that will only run on certain users' computers is not an ideal scenario to find yourself in, I’m sure you’ll agree that it is better to include something that will improve the experience of most visitors, then to not include it at all.
Additionally, your site may have some kind of browser-detection model in place that can re-direct visitors to specific parts of your site that cater to their browser type. The code contained and explained in this article could happily reside on an IE-only page, while an equivalent script that works on Gecko-based browsers is in place on other pages. A Gecko-friendly script will be the subject of article three in this series.
Regardless of which scripting language you choose to use, you're going to need a Web page that holds the text you wish to be able to search through, and the buttons that generate the input-accepting dialog box. Any page that has a large amount of text on it will benefit from this type of search capability. For this example, I've used an extract from the script of the latest Star Wars movie (obtained with thanks and appreciation from the excellent http://galacticempire.co.uk/). Create the following page in a text editor:
<html>
<head>
<title>Finding Text</title>
</head>
<body>
<h1>Finding Text</h1>
<hr>
<center>
<table spacing="2">
<tr><td><button id=buttonSearch title="Click to search for a word" onclick="textSearch()">Find...</button></td>
</table>
</center>
<p><h3>1) EXT. SPACE</h3>
A long time ago in a galaxy far, far away...<br/>
A vast sea of stars serves as the backdrop for the Main Title, followed by a rollup, which crawls into infinity.<br/><br/>
War! The Republic is crumbling under attacks by the ruthless Sith Lord, Count Dooku. There are heroes on both sides. Evil is everywhere.<br/>
In a stunning move, the fiendish droid leader, General Grievous, has swept into the Republic capital and kidnapped Chancellor Palpatine, leader of the Galactic Senate.<br/>
As the Separatist Droid Army attempts to flee the besieged capital with their valuable hostage, two Jedi Knights lead a desperate mission to rescue the captive Chancellor...<br/>
Pan down to reveal a Republic attack cruiser. Continue to pan with the Cruiser as two Jedi starfighters enter and head toward an enemy Battle Cruiser. Truck with the Jedi Fighters as they manoeuvre in unison, dodging flack and enemy laser fire. R2-D2 is on Anakin's ship. R4-P17 is on Obi-Wan's ship. A giant space battle is revealed as the tiny Jedi ships continue their assault in a synchronous ballet.</p>
Anakin smiles as he blasts a Trade Federation droid drop fighter.<br/>
<em>ANAKIN:</em> There isn't a droid made that can out fly you, Master, and no other way to get to the Chancellor...<br/>
<em>OBI-WAN:</em> Look out, four droids inbound...</p>
<p><h3>4) EXT. CORUSCANT-SPACEBATTLE</h3>
The two Jedi fighters swerve in unison as four Trade Federation droid drop fighters attack. After several clever moves by the Jedi, two of the Federation droid drop fighters collide with each other in a ball of flame.</p>
Obi-Wan struggles to maintain control of his ship.<br/>
<em>OBI-WAN:</em> We've got to split them up.<br/>
<em>ANAKIN:</em> Break left, fly through the guns on that tower.<br/>
Obi-Wan flies to the left of a huge tower on a Republic cruiser. The two droid drop fighters follow.<br/>
<em>OBI-WAN:</em> Easy for you to say...why am I always the bait?<br/>
<em>ANAKIN:</em> Don't worry. I'm coming around behind you.<br/>
Obi-Wan deftly maneuvers around a large Starship's superstructure, but the two droid fighters stay on his tail, blasting him with intense laser fire.<br/>
<em>OBI-WAN:</em> Anakin, they're all over me!<br/>
<em>ANAKIN:</em> Dead ahead! Closing...lock onto him, Artoo...<br/>
R2 beeps his reply as Anakin swoops in for the kill. Anakin blasts one of the droid drop fighters. It explodes.<br/>
<em>ANAKIN:</em>(continuing, laughs) We got him, Artoo!<br/>
Anakin blasts away at the second droid drop fighter as R2 beeps an angry warning.<br/>
<em>ANAKIN:</em> I copy, Artoo.<br/>
<em>OBI-WAN:</em> I'm going down on the deck.<br/>
<em>ANAKIN:</em> Good idea...I need some room to maneuver.<br/>
Obi-Wan dives toward the surface of one of the larger Trade Federation battleships and is forced to fly through a maelstrom of laser flack. He skims the surface, followed by the droid drop fighter, which is followed by Anakin.<br/>
<em>ANAKIN:</em>(continuing) Cut right. Do you hear me?! Cut right. Don't let him get a handle on you. Come on, Artoo, lock on! Lock on!<br/>
R2 beeps. The crosshairs merge on the droid drop fighter.<br/>
<em>OBI-WAN:</em> Hurry up! I don't like this!<br/>
Obi-Wan flies through a narrow gap between two towers on a Battleship. The droid drop fighter hits one of Obi-Wan's wings with a laser blast, and parts of the ship go flying around Obi-Wan's Astro Droid, R4.<br/>
<em>OBI-WAN:</em>(continuing) Ouch!<br/>
R4 BEEPS a blue streak.<br/>
<em>OBI-WAN:</em>(continuing) Don't even try to fix it, Arfour. I've shut it down.<br/>
<em>ANAKIN:</em> We're locked on ... we've got him...<br/>
Anakin drops in behind the droid drop fighter and blows him apart. R2 squeals with delight.<br/>
<em>ANAKIN:</em>(continuing) Yeah! We got him...good going, Artoo.<br/>
<em>OBI-WAN:</em> Next time you're the bait...Now let's find the Command Ship and get on with it...<br/>
R-4 BEEPS a blue streak.<br/>
<em>ANAKIN:</em> Lock onto them, Artoo. Master, General Grievous's ship is directly ahead.<br/>
R2 beeps a reply, and it reads out in Anakin's cockpit.<br/>
<em>ANAKIN:</em>(continuing) The one crawling with vulture droids.</p>
<em>OBI-WAN:</em> I see it. Oh, this is going to be easy.<br/>
Ahead is a Trade Federation cruiser with batlike droid vulture fighters stalking around on the hull. The vulture fighters transform into flight configuration, lift off the cruiser, and attack the Jedi starfighters.</p>
</body>
</html>
Save this as findingtext.htm. It may seem a little over the top, but you need a decent sized chunk of body text to see the real benefits of this capability. You don’t need to place the button in a table, but in the second article the reason why will become apparent. Actually, the table element should not be used to handle the positioning of elements at all; this should be managed by CSS, but it provides a quick and easy solution without the article heading off on a tangent.
As far as your HTML page goes, this is pretty much it; all you need is to include a call to the script file in the head of the document, which you can add once the JavaScript or VBScript file has been completed. Keeping your script out of the main document to create a separate behavior layer is the way forward as far as application, be that Web or desktop, design is concerned. Get into the habit of doing it now! A presentation layer (consisting of any stylesheets used by the main page) if desired, can also be included, but will not be discussed in this article.
So now that you have the Web page in place, you need the script file and an additional window to act as a dialog box. Let's code the script file, beginning with the JavaScript flavor first. Think first about what we want the script to do; we want to be able to click a button (the search button), and have a dialog box open into which we can enter a search term. The dialog window will then pass the search value back to the script file, and the script will look at the body text of the initial Web page and highlight the first word that matches the search term. The following function will do all of the above (except for the value-passing, which will be handled from within the search window):
The first thing this function does is set a variable called range equal to the body text of the document. It does this using the create method of the TextRange() object. Next it sets a variable called searchWord and calls the dialog window. You use the showModalDialog method because you want the dialog window to grab the focus and keep hold of it until one of the buttons on the dialog window is clicked. The parameters passed are the location/name of the dialog window, the null keyword, and some stylistic elements. The middle value can be used to pass values to the dialog window, but as we want to get a value from the dialog, we specify null here.
A second variable called match is now set to either true or false depending on the results of the findText method, which is applied to the range variable (which contains the body text of the document). The parameters passed to the findText method are the search word (stored in the searchWord variable), the null keyword, and an optional flag which controls the search. Using null as the middle parameter will make the search travel forward, which will be the most common method; a negative value can be entered to make the search travel backward. The 2 flag means that whole words only will be matched. Partial words can be matched using 0.
Finally, the if statement highlights the search word if it is found (if match does not equal false), or pops up a message if the search word does not exist in the text (if match does equal false). Save this file as behaviour.js in the same location as the Web page above. You can now edit the Web page to link to the script file. Add the following code to the head of the document:
<script language="JavaScript" src="behaviour.js">
</script>
Also add a function call to the search buttons onClick method:
<button id=buttonSearch title="Click to search for a department" onclick="textSearch()">
All that is required now is the dialog box itself. The following code will give us the window that we want:
<html>
<head>
<script language="JavaScript">
function doOK() {
if (textSearch.value==""){
alert("Enter a word or click cancel");
return
}
window.returnValue=textSearch.value;
window.close();
}
function doCancel(){
window.returnValue="";
window.close();
}
</script>
<title>Search Window</title>
</head>
<body style="background-color:silver;">
<br>
<p style="text-align:center">Enter a search word:</p>
You'll notice that this page contains all of the script and style references within the document, which goes against what we have discussed so far about separating your project into layers that correspond to behavior, style, and so forth. As this window is not designed to be viewed as a proper window, but should act simply as a dialog box that will only appear momentarily when called, I feel that it is acceptable for it to act as a self-contained unit and can therefore be the exception rather than the rule.
The functionality of the page is simple. It has a script containing two functions; one function is mapped to the ok button and one to the cancel button. The ok function gives an alert if the input field is left blank, but providing a value has been entered, it passes this back to the element that called it in the first place, which as we know is the searchWord variable in the behavior file. The cancel function simply returns nothing and closes the dialog window.
Save this as searchwindow.htm in the same location as the other two files. Opening the findingtext.htm file, the text search can now be carried out. I think you'll agree, however, that there are several problems with the dialog box used to enter search terms. Primarily, the problem is that it doesn't look very good -- there are things on it that look bad and are not needed, like the status bar and the help button. You can tidy this up a little by including the following attributes in the showModalDialog method in the behaviour file:
status:no;help:no
Simply append this to the end of the bracketed code. This will improve the look of the search window slightly and remove elements that have no function or use. Using the search button on the initial Web page will now call a window that looks and behaves like a dialog and can be used to find a specific word in the block of text that makes up the body of the document.
There is a problem with this however; when the dialog window is open, if the ok button is clicked and there is nothing in the search input, an alert informs the user of this. But when the cancel button is clicked, the dialog simply closes and nothing is returned to the searchWord variable. When this happens, an error occurs, as there is nothing in the script to specify what should happen if this occurs. This is an important part of the errata issues we will look at in the next article; you have to build into your script every attempt to catch any errors that may occur as a result of improper use of your Web page. Add the following if statement directly below the searchWord declaration in the main script file:
if (searchWord=="") {
return false
}
Now the script knows what to do when an empty string is returned.
There are also limitations to the functionality of this script; primarily, there is nothing to take into account that there may be more than one occurrence of whatever word the visitor is searching for. Part two of this article goes on to explain how this functionality can easily be added, and goes on to discuss errata in more detail. The promised VBScript version also forms part of this article.