How to Create an Ajax Autocomplete Text Field: Part 8 [con't]
The Fix Is In
With a little ingenuity, each of these issues can be remedied. To compensate for the scrollbar, we can manually increase the width of the table. Add the following code to the bottom of the parseMessages() function,
where we set the container's visibility property to "visible."
Add the following code for the setListWidth() function to the JavaScript:
The RegExp test() function determines whether or not we even want a scrollbar
by checking for the scrollbar class in the container's className
property. A ternary operator (expression ? true : false) then evaluates the
table's clientHeight against the container's. Without a scrollbar,
they're the same, but when the vertical scrollbar is present, the completeTable's
exceeds the container's by the hidden rows' combined height.
We can then compensate for the scrollbar then by adding its width to the container's style.width property. The scrollbar is 16 pixels wide in most browsers,
but not always. In Safari running on Windows 2000, it's only 15 pixels.
The false part of the ternary operator sets the container's clientWidth
to the table's value. At first glance, this code might seem redundant, since
it would already be that size by default. Indeed it is, except when we've already
set it. Chances are that the scrollbar will appear after the first letter, but
won't be required as you type in more characters. Therefore, the scrollbar
would no longer be displayed, freeing up the 16 pixels. It's best to remove them.
height in Internet Explorer. This is somewhat
easier to fix than the previous one, since it only affects the one browser.
It turns out that while Internet Explorer doesn't recognize the max-height
property, it allows us to set the height to an expression.
CSS expressions were introduced in Internet Explorer 5.0 and they allow you
to to assign a JavaScript expression to a CSS property. To set the maximum height
for the completeTable, change the height property in the div#container
rule to the following:
In order of execution, the expression:
- retrieves the
completeTableelement using the DOMgetElementById(String elementID)function. - converts its
clientHeightproperty to an integer, so that we may compare it to a value of100. - tests to see if it exceeds 100, using a ternary operator (
expression ? true : false). - sets the
heightproperty to 100 pixels if it evaluates totrue. - sets the
heightto thecompleteTable's currentclientHeightif it evaluates tofalse.
A word to the wise, be careful when writing expressions because errors aren't handled in the same as in regular JavaScript. A runtime error can even cause the AutocompleteControl to not appear at all! It's a good thing that this particular expression is a standard workaround.
The last issue is the most problematic to deal with. If we knew the row height
in advance, we could just multiply that by the number of rows that we want visible.
Unfortunately, the row height is largely determined by the <TD>
padding property, so we're out of luck there. The behavior stems from
the setting of the list height in pixels rather than rows. Webmasters
who want to use our control in their Web pages shouldn't have to enter
pixels in a CSS file, anyway. What they want is to include the
control in their page and set the list's size to the number of desired rows
to display, such as "10."
The <jsp:param> Element Revisited
As mentioned in last week's article, when using the <jsp:include> tag
to insert a dynamic resource in a page, the request is sent to the included
resource, the included page is executed and the result is included in
the response from the calling JSP page. The <jsp:include> tag provides
a means to pass name/value pairs to the included resource via the <jsp:param>
element. In the included resource, you can retrieve the parameters by using
the getParameter(string paramName) method of the request object. Used
in conjunction with the <%= %> output shorthand, you can display the variable
or use it in a client-side script. Therefore, we can add a <jsp:param>
element in the AutocompleteSearchCSS.jsp page to set the list size:
In the AutocompleteControl.jsp file, we add the following line to convert
the parameter to a JavaScript variable:
Back in our AutocompleteList.js file, we add some code to set the list
height and maxHeight properties.
The parseMessages() Function
We can't set the list height in the init() function with the other variables
because we need the row height to calculate the container <DIV>'s
maxHeight. We'll have the information we need after the rows have been
added to the table, in the parseMessages() function:
The purpose of the setListHeight() function is to calculate the list's maxHeight
property based on the supplied actualListSize parameter. The outermost
if statement achieves two goals. First, it ascertains that there was
a listSize variable supplied via a parameter within the AutocompleteControl's
tag. That tells us that the user wants a scrollbar to appear if the number of
items in the list exceeds that value. Second, it only accepts numeric values
because the isNaN() function returns true if the listSize is "Not
a Number." Hence the exclamation in front of the isNaN() function returns
true if it IS a number. Since the listSize is a
user property it's a good idea to validate it. The desired containerHeight
in pixels can then be calculated based on the actualListSize and the
desired listSize.
In DOM-compliant browsers we can set the maxHeight property to the containerHeight
directly. Internet Explorer 5/6 require that we apply the setExpression(property,
expression, language) function to the height property since they don't
support the maxHeight property. The isMaxHeightSupported global
variable specifically targets IE 7+ browsers (which do support maxHeight) so we don't use the expressions on them. The third argument is optional,
but we should supply it because the default is JScript. The setExpression()
function also accepts VBScript code.
The Autocomplete.css file contains a rule called div.scrollbar to add
the scrollbar to the list automatically when required. We add it to any
preexisting classes by using the "+=" operator.
The last line in the function sets itself to an empty one that does nothing
because we only have to set the maxHeight and className once.
Rather than use an if statement every time the parseMessages() function
executes, we call the function every time, but nothing will happen after
the first run through.
Here's the code for the setListHeight() function:
Testing for max-height Support
Although newer browsers recognize the max-height property, there are
still enough Internet Explorer 6 browsers out there to warrant supporting the
height expression approach. Not being a supporter of browser sniffing,
I prefer to test on a case by case basis, such as container.setExpression
in the previous code snippet. It's ultimately a lot easier than checking browser
types and versions. Having said that, this technique doesn't work with CSS
styles because the scripting engine doesn't verify that the property exists.
For this reason, you can set the maxHeight to anything you want and even
retrieve the value later without errors, but the property won't affect the displaying
of the element in any way!
What we can do is apply the style to a test element and test to see that it
yields the expected result. To check for the maxHeight property, we
have to mimic the list <DIV> element and include another one within it
that's larger. That's what the testForMaxHeightSupport() function does. Here's
how it works:
In the CSS file, we add an ID rule that contains the max-height and
overflow properties. The <DIV> is made invisible and
given absolute positioning so that it won't move any of the other
page elements:
The isMaxHeightSupported variable needs to be added to the other global
variables at the top of the script:


