Creating a simple search box

Jake Howlett, 3 June 2001

Category: Forms; Keywords: search input path

CodeStore is not alone in having a search input box on every page - Notes.net has one, as do most sites where the user wants to look for certain information straight away, rather than simply browsing from link to link. Some examples:

image

Adding the functionality is fairly easy, as I shall describe below. What is not so obvious is how to get around the problems associated with the way in which the browser handles submission of this form. This is what a couple of people have asked me about and hence why I am writing this article.

Creating the Search Box:

Because we want to add the same box to every page and probably have it in the same place, it is best to create the box in a Subform. This way you can easily add it to every form and when you make changes they get updated to all necessary pages. This is what the box we are going to create will look like:

Search


This is simply a table with two rows and two columns. The first row is merged in to one column and the second row contains the search box and the search button in respective cells. To create this table we could easily use the Notes Table element in the designer, but I prefer to use Pass-Thru HTML as this gives me greater control over what is sent to the browser. Here is what my Subform looks like so far:

image

Notice that even the search field has been created in HTML. Why? Well, because the subform is not only going to be on forms, but also on documents in read-mode and views. When on a document in read mode a standard Notes field will not be seen as a field. The method we use makes use of the fact that Domino still renders a <form>, even in read-mode, so we can have fields on there.

What we do need to do though is add a Notes field to the form with the same name as the search box (Domino errors if you don't). We shall add this field to the subform, this way we are sure it is on every form with the search box. The field is Computed For Display (so that it's not stored in the document) with a value equal to itself and it is hidden. The subform will now look like this:

image

Notice the pink lines of text on the subform. You can do away with these if you like. Personally, I find them a useful way of highlighting that it is a subform and it also shows me where it starts and ends, when it is on the form.

We now have a subform that we can place on all our forms and it will add a tidy little search box. The only thing we need to do now is make it work...

Adding the search functionality:

Regular users of this site will know by now about my love affair with JavaScript. That's what we are going to use. First thing to do is add a call in the onclick event of the "GO!!" button to the function we are going to write. The HTML for the button should look like:

<input type="button" name="Search" value="GO!!" onclick="doSearch(this.form.Query);"/>

Now, when the button is pressed, it calls the doSearch function and passes the Query field's object to it. All we need now is the function to do the search. How one writes this function depends on how flash one wants to make it. I am going to show two versions; a simple one and a flashy one. Let's start with the simple one:

function doSearch ( s ) {
openDbRelativeURL("All?SearchView&Query=" + s.value);
}


This is all we need. All it does is redirect the browser to a new URL that points to a view and opens it using the ?SearchView URL Command, passing it the search term via the Query parameter. To make it even simpler we could have put this one line of code in the onclick event of the button and done away with the function.

You have probably noticed the strange looking call to a function called openDBRelativeURL(). Don't worry, it's not some magical JavaScript feature you never knew about. It's something I wrote so there is no need to work out the Database's filename when creating the URL. You will need to add this to your set of JS functions as well. You can read more about it here or just copy it from below:

function openDbRelativeURL( url, target ){
//Check we have a target window;
target = (target == null ) ? window : target;
//Work out the path of the database;
path = location.pathname.split('.nsf')[0] + '.nsf/';
target.location.href = path + url;
}

So, what's wrong with this simple version of the doSearch function? Nothing, as such, we just need to make it a little more user friendly. The flashy version below does two things. Firstly, it makes sure that the user has entered something to search for, thus preventing an error from the server. Secondly, it uses Regular Expressions to validate what they are searching for. This checks for things like the word "field" and characters such as [ and ), all of which are more than likely going to return an error.

function doSearch ( s ) {
var regExp1 = /\bfield\b/;
var regExp2 = /[(,),<,>,\[,\]]/;
var str = s.value; if ( str == "" ){
alert("Please be sure to enter something to search for.");
s.focus();
} else {
if ( typeof regExp1.source != 'undefined' ) //supports regular expression testing
if ( regExp1.test( str ) || regExp2.test( str ) ){
var alrt = "Please note that you can not include:";
alrt += "\n\nThe reserved word 'field'\nthe characters [, ], (, ), < or >";
alrt += "\n\nin your search query!\n\nIf you are confident that you know";
alrt += "\nwhat you are doing, then you can\nmanually produce the URL required."
s.focus();
return alert( alrt );
}
openDbRelativeURL("All?SearchView&Query=" + escape( str ) + "&start=1&count=10");
}
}


It's at this point that I would normally include a working example. Obviously I don't need to as there is already one on every page ;) If you want to include one on yours then just plug in all the components from above.

A couple of gotchas

If you were to add the above parts together and play around for a while you would, sooner or later, notice a little "bug" in this method. The problem is that when you have a form with only one field in it and you press the enter key the browser thinks you want to submit the form. Because the action of the form will be to submit it, rather than do a search, you will get some confused users. What we need to do is alter the onsubmit property of the form when we know there will only be one field. We only need to do this in documents in read mode and in things like $$ViewTemplates.

If you are lucky enough to be using R5 this is fairly simple. In the onsubmit event of the form, add the following line:

image

This tells the browser that when the form tries to submit and the document is in read mode it should simply call a function called return false instead. Before we look at this function we need to add the isDocBeingEdited variable to the page. Easiest way to do this is add the following line to the HTML Head Contents ($$HTMLHead) of the form:

"<script>var isDocBeingEdited = " + @If(@IsDocBeingEdited; "true"; "false") + ";</script>"

Note: On things like $$ViewTemplates, which can never be in edit mode, you don't need to add this variable and the onsubmit event is simply: return returnFalse(this.Query);

All we need now is the returnFalse function. The way you write this depends on what you want to happen when the user presses enter. The three option being: do nothing, do the search or tell the user to press the button.

function returnFalse(s){
//guess the field if not supplied
s = (s == null) ? document.forms[0].Query : s;

//if you want to do the search, use this line
//doSearch(s);

//if you want to alert the user, use this line
//alert('Please use the \'Go!!\' button to do a search');

//this line should always be here!!
return false;
}


And there you have it. It might seem overly complicated but this approach covers just about everything you are likely to come across when designing a search box for your site.



Note: If you are using R4 you will not be able to set the onSubmit event of the form in the Domino Designer IDE. You will have to do it with JavaScript.

If you are using IE then the following script will do it:

<script for=window event="onload()">
document.forms[0].onsubmit = returnFalse;
</script>

If you are using NN then you will need to add the following JS near the bottom of the form somewhere:

<script>
document.forms[0].onsubmit = returnFalse;
</script>

Obviously you will need to hide both scripts when the forms are being used in edit mode.