Client-Side Script Integration in ASP.NET
- Client-Side Interaction on the Web
- Useful Client-Side Scripting Techniques
- Summary
In This Chapter
Client-Side Interaction on the Web
Useful Client-Side Scripting Techniques
Summary
ASP.NET provides plenty of clever server-side controls that ultimately generate HTML elements in the browser. However, with the notable exception of the validation controls and one or two other features, that's really all they do. In fact, when they start to build Web applications, most developers who are used to building Windows applications find that the interface features Web developers have become accustomed to using are quite poor.
We can't do much about the actual client-side HTML elements and controls that are available because that's the whole nature of the Web. Content is supposed to be universally supported in all browsers, and browsers are supposed to follow accepted standards. Therefore, if your application needs some fancy new kind of multistate psychedelic flashing button, you're going to have to find a way to build it yourself. And depending on how you implement it, you might then have to find a way to persuade all the people who use your site to download this great new control and install it on their machine.
Avoiding Meaningless and Annoying Content
In reality, most people have seen enough in the way of annoying Java applets, malicious ActiveX controls, time-wasting Flash animations, and pointless Shockwave effects. They expect an application to do what it says on the box by being intuitive and easy to understand and working seamlessly and as fast as possible, given the nature of Internet connections.
Client-side scripting has been a feature of Web development for almost as long as the Web in its current incarnation has been around. Scripting provides an increasing number of useful features that you can take advantage of to make Web applications appear more seamless, responsive, and interactive, while still running well in almost all popular browsers in use today.
This book is about ASP.NET and not client-side scripting, but, in fact, the two are no longer really divisible. ASP.NET generates client-side script in varying quantities, depending on the server controls you place on a page. Even simple effects such as auto-postback depend on some client-side script.
And you saw client-side script being used in the ComboBox control you created in Chapter 5, "Creating Reusable Content."
This chapter takes a look at the major client-side script issues that affect you when you create ASP.NET pages, as well as when you create reusable content such as user controls and server controls. This is by no means a reference work on client-side scripting, but it reinforces some of the basic techniques and demonstrates useful ways that even very simple script can solve common issues you come up against when building ASP.NET Web applications.
Client-Side Interaction on the Web
Client-side interaction is hard to achieve because of the disconnected nature of HTTP and the way that browsers and Web servers work. Information is passed to and from the client only during distinct phases of the Web-surfing process. The server builds the page and sends it to the browser, and the browser submits the page back to the server when it's ready for another one.
Okay, so there are some well-known ways that you can get around this issue, usually by installing a component in the browser that can send and receive HTTP requests without having to reload the current page. The XMLHTTP component within the MSXML parser in Internet Explorer 5 and above is a good example. You can also use Macromedia Flash and a range of third-party plug-ins or components for other browsers. However, the point is that if you want a page to be interactive to the extent that it "does stuff" while loaded into the browser, you need to find a way to execute code within the confines of the browser.
When you're building items of reusable content, as demonstrated in Chapter 5, client-side scripting allows you to push the envelope beyond the simple flow layout of HTML controls to provide extra features that are often seen in traditional executable applications. The following sections explore the fundamental aspects of where, when, and howand then move on to look at some useful techniques that integrate client-side and server-side programming and provide examples you can use in your own pages.
Client-Side Scripting in the Browser
Client-side scripting has been supported in the mainline Web browsers since Netscape Navigator 2 and Internet Explorer 3. These browsers, and many others, support the simple HTML Document Object Model (DOM) by exposing specific elements to script that runs within the browser. Such elements include frames, forms, controls (such as <input> and <select>), images, links, and anchors (<a> elements with name="..." rather than href="..."). Script can also access the fundamental objects such as the current window and the document within a frame or a window.
This level of accessibility to the page content allows the traditional effects such as reading and setting the values of controls, submitting a form, or swapping images in an <img> element. It also supports a small set of useful events, such as detecting when a control gets or loses the focus or receives a click (via keyboard or mouse). However, this basic level of support for scripting does not offer the three main features that you often need when building better controls or interactive content:
Access to all the elements on the page, with the ability to read and set the content of each one, show or hide it, and generally manipulate it.
Access to a full range of keypress events, so that you can manage how a control behaves, depending on user interaction via the keyboard.
The ability to position elements outside the flow model, using fixed (absolute) coordinates that are relative to a container (such as the page or a parent element). It's nice to be able to do this dynamically and even be able to move elements around while the page is displayed.
CSS2 and Dynamic HTML
While much has been made of the "browser wars" over the past few years, the situation today regarding the use of client-side scripting is actually a lot more favorable than it was. Microsoft and Netscape added a feature set they called Dynamic HTML to their version 4 browsers, although the blatant incompatibility between them (and the resulting outcry from Web developers and standards bodies alike) was perhaps one of the key factors in the evolution of more comprehensive client-side standards over the following years.
Today we have Cascading Style Sheets (CSS) at version 2, HTML at version 4, and XHTML at version 1.0; together, they provide not only a comprehensive display model based on the original CSS recommendation but also a standard set of methods for accessing and manipulating document content from script or code running on the client. While these recommendations are fundamentally similar to the original Microsoft implementation in Internet Explorer 4, there are subtle differences. However, the mainline manufacturers all have "version 6" browsers available that generally do meet the basic CSS2, HTML4, and XHTML recommendations. These include the following:
Internet Explorer 5.x and 6.x, although CSS2 support is generally more comprehensive and less buggy in version 6 than in earlier versions. And there are still some issues with the way that the box display model works.
Mozilla 1.x (effectively a version 6 browser) and Netscape 6.x, which use the same rendering engine (depending on minor version number) and generally support the latest standards very well. Minor exceptions are occasional buggy rendering, particular with absolutely positioned elements.
Opera 6.x and 7.x, which both have comprehensive support for the latest standards, although problems with dynamic positioning have occurred in version 6.0. Opera 4.0 and 5.0 also supported CSS2 to a large extent.
CSS2 Support in Version 6 Browsers
In reality, some of the more esoteric features of CSS2 are not fully supported in all version 6 browsers or are less than totally compatible across the different version 6 browsers. However, the basic techniques that we take advantage of in our examples do work in all the current version 6 browsers.
Selecting Your Target
Are most users out there using a version 6 browser? Admittedly, our own Web site is mainly aimed at developers working with the latest Microsoft technologies, so the results we see are probably not representative of the population, but around 75% of our visitors are using Internet Explorer 5 or higher, Netscape/Mozilla 6 or higher, and Opera 6 or higher. Looking at the stats available on other sites, the percentage of visitors using these newer browsers varies from something over 55% to almost 90%.
Why Use the Latest Browser?
You probably wouldn't want to risk driving on an icy freeway during rush hour in a 1910 Model T Ford. Four-inch-wide tires, vague steering, and a distinct lack of braking performance when compared to those in modern vehicles, would make this a risky undertaking at the best of times. Likewise, using an old and unsupported browser is an equally foolhardy adventure these days, with the proliferation of malicious scripts, annoying Java applets, and downright dangerous ActiveX controls that are out there on the Web and being delivered daily in junk email messages. Most car drivers appreciate the added safety of antilock brakes, airbags, and seatbelts, and the sensible browser user does the same by choosing the latest browser so that he or she can stay secure with the updates and patches provided for it.
It's probably reasonable to assume that you can take advantage of CSS2 and HTML4 features to add client-side interactivity to your pages, without affecting the majority of users. Of course, that doesn't mean you can ignore the rest because there are issues such as providing accessibility to users of text-only browsers, page readers, and other devices aimed at specialist markets or disabled users.
The language of choice for client-side programming is, of course, JavaScriptbecause only Internet Explorer can natively support VBScript. There are several versions of JavaScript available, but the "vanilla" version 1.x satisfies almost all requirements for the simple client-side interactivity you need when building most user controls and server controls. And because Internet Explorer actually has its own JScript/ECMAScript interpreter rather than a real JavaScript one, staying with the features in JavaScript 1.0 or 1.1 provides the best compatibility option.
Version 6 Browser-Compatible Code Techniques
Given the three tasks listed earlier in this chapter that you most commonly need to accomplish in client-side scriptaccess to all elements, access to keypress information, and dynamic positioning of elementsthe following sections look at how these can be achieved in modern browsers using script.
Accessing Elements Within a Page
Internet Explorer 4 was the first mainstream browser to provide full access to all the elements in a page by exposing them from the document object as a collection called all. It also allowed selection of a set of elements by type, via the use of the getElementsByTagname method. While CSS2 provides the same getElementsByTagname method, it replaces the document.all collection with two methods named getElementById and getElementByName. Because ASP.NET sets the id and name attributes of an element that is created by a server control to the same value (with the exception of the <input type="radio"> element), the getElementById and getElementByName methods generally provide the same result.
Therefore, the technique for getting a reference to an element within client-side script depends on whether you are only going to send the page to a CSS2-compliant client or whether you want the code to adapt to different client types automatically. The accepted technique for providing adaptive script in a page is to test for specific features that identify the browser type or the support it provides for CSS2. These features are summarized in Table 6.1.
Table 6.1 Features You Can Use to Detect the Browser Type or Its Feature Support
Feature |
Description |
document.all collection |
Supported by Internet Explorer 4.0 and above |
document.layers collection |
Supported by Netscape Navigator 4.x only |
getElementById method |
Supported by CSS2-compliant browsers |
Using the ASP.NET BrowserCapabilities Object
You can use the ASP.NET BrowserCapabilities object to sniff the browser type and deliver the appropriate page or include the appropriate script or controls. Chapter 7, "Design Issues for User Controls," and Chapter 8, "Building Adaptive Server Controls," demonstrate this approach.
By using the features described in Table 6.1, you can write code such as that shown in Listing 6.1 to execute different sections of script, depending on which browser loads the page. Notice that this causes Internet Explorer 5.x to execute the CSS2-compliant code. If you find that this does not perform correctly with your specific client-side scripts, you can change the tests so as to place Internet Explorer versions 4.x and 5.x into the same section by checking the value of the navigator.appName and navigator.appVersion properties as well.
Listing 6.1Detecting the Client's Feature Support in Script Code
if (document.getElementById) { ... code for CSS2-compliant browsers here ... } else if (document.all) { ... code for IE 4.x here ... } else if (document.layers) { ... code for Netscape Navigator 4.x here ... } else { ... code for older browsers here ... }
However, as discussed earlier, the number of users still running Navigator 4.x and Internet Explorer 4.x is extremely low, so you generally need to test only for CSS2 support and provide fallback for all other browsers. There's not a lot of point in spending long development times on supporting browsers that only 1% of users may still be running.
Accessing Keypress Information
Microsoft's early implementation of Dynamic HTML exposed three keypress events for all the interactive elements on a page and for the document object itself. These are the keydown, keypress, and keyup events, and they occur in that order. The keypress event exposes the ANSI code of the key that was pressed, and the other two events expose a value that identifies the key itself (as located within the internal keyboard mappings) rather than the actual character.
Listing 6.2 shows the generally accepted technique for detecting a keypress that works in Internet Explorer version 4.x and higher and in CSS2-enabled browsers. If the event is exposed by the window object, as in Internet Explorer 4 and above, it is extracted from the keyCode property of the event object. In CSS2-compliant browsers, the event is passed to the function by the control to which the function is attached as a parameter, and it can be extracted from the which property.
Listing 6.2Detecting a Keypress Event and the Code of the Key That Was Pressed
<element onkeypress="showKey(event);"> ... <script language="javascript"> <!-- var iKeyCode = 0; if (window.event) iKeyCode = window.event.keyCode else if (e) iKeyCode = e.which; window.status = iKeyCode.toString(); //--> </script>
Dynamic and Absolute Element Positioning
The final feature set that you often need a browser to support when creating user controls and server controls is a way of positioning elements within and outside the usual flow of the page, changing that setting dynamically, and specifying the size of elements. Again, the original Microsoft Dynamic HTML approach has survived almost intact in CSS2, so these features are available in Internet Explorer 4.x and above, as well as in CSS2-compliant browsers. In more strict terms, the features that you are most likely to take advantage of are summarized in Table 6.2.
Table 6.2 Dynamic and Absolute Element Positioning Features
Feature |
Description |
Showing and hiding elements |
Set the display selector of the style attribute to block, inline, or hidden. Other values can be used, but these three are most useful. The value block forces this element to start on a new line and following content to wrap to a new line. The value inline means that preceding and following content will be on the same line, unless that content forces a new line. The value hidden removes the element and all child elements from the page. |
Absolute positioning |
Set the position selector of the style attribute to absolute to fix an element using the top and left coordinates provided as the top and left style selectors. This removes the element from the flow layout of the page. The alternative is position:relative, which forces the element to follow the flow layout of the page but also allows it to act as a container within which child elements can be absolutely positioned. If no parent element contains position:absolute or position:relative, the current element is positioned with respect to the top left of the browser window. |
Specifying the actual size of elements |
Set the width and height selectors of the style attribute to fixed values. These values can be specified with units px (pixels), pt (points), in (inches), cm (centimeters), mm (millimeters), or pc (picas) or the typographical units em, en, and ex. The default is px. |
Positioning and moving elements dynamically |
The values for the display, position, top, left, width, and height selectors can be changed while the page is loaded, and the page will immediately reflect these changes by showing, hiding, or moving the element. |
The Client-Side Code in the ComboBox User Control
To demonstrate the feature sets described so far in this chapter, let's briefly review some of the code from Chapter 5, "Creating Reusable Content." That chapter shows how easy it is to build a ComboBox user control for use in browsers that support CSS2 (see Figure 6.1).
Figure 6.1 The customer ComboBox user control created in Chapter 5.
This control includes client-side code that manipulates the control elements and their values while the page is loaded into the browser, using most of the features just discussed. Listing 6.3 shows the complete client-side code section. In each of the three functions in Listing 6.3, you can see that you get a reference to the controls you want to manipulate by using the getElementById function that is exposed by the document object.
Listing 6.3The Client-Side Script for the ComboBox User Control
<script language='javascript'> function selectList(sCtrlID, sListID, sTextID) { var list = document.getElementById(sCtrlID + sListID); var text = document.getElementById(sCtrlID + sTextID); text.value = list.options[list.selectedIndex].text; if (sListID == 'dropbox') openList(sCtrlID); } function scrollList(sCtrlID, sListID, sTextID) { var list = document.getElementById(sCtrlID + sListID); var text = document.getElementById(sCtrlID + sTextID); var search = new String(text.value).toLowerCase(); list.selectedIndex = -1; var items = list.options; var option = new String(); for (i = 0; i < items.length; i++) { option = items[i].text.toLowerCase(); if (option.substring(0, search.length) == search ) { list.selectedIndex = i; break; } } } function openList(sCtrlID) { var list = document.getElementById(sCtrlID + 'dropbox'); var btnimg = document.getElementById(sCtrlID + 'dropbtn'); if(list.style.display == 'none') { list.style.display = 'block'; btnimg.src = document.getElementById(sCtrlID + 'imageup').src; } else { list.style.display = 'none'; btnimg.src = document.getElementById(sCtrlID + 'imagedown').src; } return false; } </script>
Alternative Client Support Options
The code in Listing 6.3 doesn't provide support for non-CSS2 browsers. This is because the only ones that support another feature needed for this control (absolute positioning) are Internet Explorer 4.x and Netscape 4.x. Because the number of hits likely to be encountered from these two browsers is negligible, it doesn't seem worth supporting them.
However, extending support to Internet Explorer 4 isn't hard; you would just need to add the test for the document.all collection, as shown in Listing 6.4, and then access the elements by using this collection. The remaining code will work fine as it is.
Listing 6.4Adapting the selectList Function to Work in Internet Explorer 4.x
function selectList(sCtrlID, sListID, sTextID) { var list; var text; if (document.all) { list = document.all[sCtrlID + sListID]; text = document.all[sCtrlID + sTextID]; } else { list = document.getElementById(sCtrlID + sListID); text = document.getElementById(sCtrlID + sTextID); } text.value = list.options[list.selectedIndex].text; if (sListID == 'dropbox') openList(sCtrlID); }
Accessing the document.all Collection and the getElementID Method in JavaScript
Remember that document.all is a collection (array) of elements, so in JavaScript, you must use square brackets ([]) to access the members. On the other hand, getElementId uses ordinary parentheses (()) because it's a method, and you are providing the element ID as a parameter.
Keypress Events in the ComboBox Control
The scrollList function shown in Listing 6.3 continually selects the first matching value in the list while the user is typing in the text box section of the ComboBox. To work, it must be called every time a key is pressed so that it can search the list for the appropriate value (if one exists). To achieve this, you handle the onkeyup event, which runs when the user releases a key.
You attach the scrollList function to the input element that implements the text box by using server-side code (as shown in Chapter 5). When the page gets to the client, the HTML declaration of the text box (with the nonrelevant style information omitted) looks like this:
<input name="cboTest2:textbox2" type="text" id="cboTest2_textbox2" onkeyup="scrollList('cboTest2_', 'dropbox', 'textbox2')" />
You can see that a keyup event will pass the three required parameters to the scrollList function. However, you aren't actually interested in detecting which key was pressed because the function just compares the values within the text box and the list to figure out which entry to select. This means that you don't have to pass the event object (required to detect which key was pressed in Netscape and Mozilla browsers) as a parameter. In later examples, you'll see occasions where you do need to detect the actual key value.
Element Positioning in the ComboBox Control
The version of the ComboBox control that provides a drop-down list uses absolute positioning to fix the width of the enclosing <div> element, the width of the text box within it, and the position and size of the <select> list that implements the drop-down list part of the control. You can see in Listing 6.5 that the top of the list is positioned 25 pixels below the top of the text box and 20 pixels to the left of the text box. The widths of the text box and list are adjusted accordingly, depending on the width of the enclosing <div> element. All these values are calculated on the server and are used to create the style selectors shown in Listing 6.5.
Listing 6.5The Style Selectors for Positioning the Text Box and List in the ComboBox User Control
<div id="cboTest1_dropdiv" Style="position:relative;width:150;"> <input type="text" id="cboTest1_textbox2" ... style="vertical-align:middle;width:133;" /> <input type="image" id="cboTest1_dropbtn" ... /> <select size="5" id="cboTest1_dropbox" ... style="display:none;position:absolute;left:20;top:25;width:130;"> <option value="aardvark">aardvark</option> ... <option value="lynx">lynx</option> </select> <img id="cboTest1_imageup" style="display:none" ... /> <img id="cboTest1_imagedown" style="display:none" ... /> </div>
Notice that the list has the selector display:none so that it's not visible in the page when it loads. Likewise, the two <img> elements that hold the up and down button images are not visible either. They are simply there to preload the images so that they can be instantly switched when the user opens and closes the list.
Showing and Hiding the List Control
The code in the openList function shown in Listing 6.3 has the job of showing and hiding the drop-down list when the user clicks the up/down button or makes a selection from the list. It's simply a matter of switching the display selector for the list between none and block, depending on whether the list is already open or closed. At the same time, you switch the button image. The relevant code section is shown in Listing 6.6.
Listing 6.6Showing and Hiding the Drop-Down List Part of the ComboBox Control
if(list.style.display == 'none') { list.style.display = 'block'; btnimg.src = document.getElementById(sCtrlID + 'imageup').src; } else { list.style.display = 'none'; btnimg.src = document.getElementById(sCtrlID + 'imagedown').src; }