17.6 How do I…Use JavaScript to handle events?
Problem
Whenever there is a change to a form field, I want to trigger a JavaScript handler embedded in the generated HTML to handle that event. Since I need to use frames to create powerful web applications, I also need the ability to control the HTML in one frame by using JavaScript event handlers in another frame. How do I use JavaScript to handle events?
Technique
JavaScript is a scripting language developed by Netscape to enable web authors to design interactive sites. Although it shares many of the features and structures of the Java language, it was developed independently. JavaScript can interact with HTML source code to spice up sites with dynamic content. Note that JavaScript can be used if you are using Netscape Navigator browser. JScript is Microsoft’s version of JavaScript, which is built into the Microsoft Internet Explorer (MSIE) browser. Microsoft supports only a subset of JavaScript, which it calls JScript. Unfortunately, Netscape’s JavaScript and JScript are not entirely compatible. In order to use JavaScript for event handling, you must first understand how PL/SQL, JavaScript, and HTML interact:
The application calls functions and procedures in the PL/SQL Web Toolkit to generate dynamic HTML.
The application also embeds JavaScript code into the generated HTML.
In the generated HTML, form fields have event handlers that invoke JavaScript functions embedded in HTML.
PL/SQL variables can be passed to JavaScript directly or through the event handler.
JavaScript has direct access to HTML form fields that can be referenced using objects.
JavaScript can invoke a PL/SQL procedure using dynamic URLs, that can be targeted into the current window, new window, visible, or hidden frame.
JavaScript functions and built-in methods can be used in HTML hyperlinks as javascript:function_name().
JavaScript can also be used to generate dynamic HTML.
Frames divide web pages into multiple, scrollable regions enabling information to be presented in a more flexible and useful fashion. Each region has a separate page, allowing the display of multiple web pages within one page. One frame can refer to another frame by using JavaScript, allowing one frame to be updated by clicking a link in another. Frames, when used properly, can improve navigability considerably. Currently, frames are well supported by Netscape Navigator 4 and MSIE 4. The procedures in the HTP package used to generate frame tags are listed in Table 17.7.
Table 17.7 Procedures for frame tags
Procedure Description htp.framesetOpen Prints an HTML tag to open a container of frames. htp.framesetClose Prints an HTML tag to close a container of frames. htp.frame Prints a tag that defines a single frame in the frameset. htp.noframesOpen Prints a tag to display text in a browser incapable of frames. htp.noframesClose Closing tag for a browser incapable of frames. Steps
1. Run SQL*Plus and connect as the WAITE account. If you have not created the DEPT17 table from How-To 17.2, then run CHP17_1.SQL, shown in Figure 17.31, which creates the DEPT17 table used in this How-To.
SQL> START CHP17_1
Table created.
1 row created.
1 row created.
1 row created.
1 row created.
2. Run the CHP17_14.SQL file in SQL*Plus, as shown in Figure 17.32. The procedure divides the web page into two frames and directs a separate URL to each frame.
A frameset is opened in line 5 by calling the HTP.FRAMESETOPEN procedure and closed in line 8 with the HTP.FRAMESETCLOSE procedure. Lines 6 and 7 use the HTP.FRAME procedure to create two frames within the frameset. The OWA_UTIL.GET_OWA_SERVICE_PATH function returns the partial URL containing the name of the PL/SQL Agent and the virtual path. The second parameter to HTP.FRAME gives a name to the frame created so that it can be referenced in JavaScript. The form is an example of using HTML frames to create a scrolling select frame on the right that has buttons to select items from a list to populate the non-scrolling edit frame. This technique allows for intelligent navigation.
Another use of frames is to have a table of contents that can remain fixed in one frame while the content frame is updated. Frames work well with JavaScript because they offer more control that can be used to update multiple frames with one click, and create sophisticated, interactive interfaces.
3. Run the CHP17_15.SQL file in SQL*Plus. The procedure is used to generate dynamic HTML for the first frame.
SQL> START CHP17_15
SQL> CREATE OR REPLACE PROCEDURE MENU17 AS
2 BEGIN
3 HTP.HTMLOPEN;
4 HTP.SCRIPT(‘function checkNum(inObj)
5 {
6 inStr = inObj.value
7 for(var i = 0; i < inStr.length; i++) {
8 if (inStr.charAt(i) < “0” || inStr.charAt(i) > “9”) {
9 alert(“Department No is a number, please re-enter.”)
10 inObj.value = “”
11 return
12 }
13 }
14 }’, ‘JavaScript’);
15 HTP.BODYOPEN;
16 HTP.FORMOPEN(‘ACTION17’, ‘POST’, ‘_top’, CATTRIBUTES => ‘NAME = “editForm”’);
17 HTP.TABLEOPEN;
18 HTP.TABLEROWOPEN(‘LEFT’,’TOP’);
17 HTP.TABLEHEADER(‘Department No:’,’RIGHT’);
20 HTP.TABLEDATA(HTF.FORMTEXT(‘deptNo’, ‘5’, ‘5’, NULL, ‘ONCHANGE=”checkNum(this)”’));
21 HTP.TABLEROWCLOSE;
22 HTP.TABLEROWOPEN(‘LEFT’,’TOP’);
23 HTP.TABLEHEADER(‘Department Name:’,’RIGHT’);
24 HTP.TABLEDATA(HTF.FORMTEXT(‘deptName’, ‘20’, ‘20’, NULL, ‘ONCHANGE=”this.value = this.value.toUpperCase()”’));
25 HTP.TABLEROWCLOSE;
26 HTP.TABLEROWOPEN(‘LEFT’,’TOP’);
27 HTP.TABLEDATA(HTF.FORMSUBMIT(‘addBtn’,’ Add ‘, NULL), ‘RIGHT’);
28 HTP.TABLEDATA(HTF.FORMRESET(‘ Clear ‘, ‘ONCLICK=”this.form.addBtn.value = ‘’ Add ‘’”’));
29 HTP.TABLEROWCLOSE;
30 HTP.TABLECLOSE;
31 HTP.FORMCLOSE;
32 HTP.BODYCLOSE;
33 HTP.HTMLCLOSE;
34 END MENU17;
35 /
JavaScript code is embedded within the generated HTML document. The HTP.SCRIPT procedure embodies the complete script. The second parameter specifies the language for the script. You can also use other scripting languages like JScript or VBScript as long your browser is capable of understanding the script.
checkNum() is a simple number validation function used to demonstrate how JavaScript can be fruitfully used to do petty validation checks at the client end, thus saving a trip to the server. In line 16, a form tag is specified. The name of procedure for processing the form when submitted is the first parameter. The second parameter is the REQUEST_METHOD CGI variable—GET or POST; we use POST for versatility. The third parameter specifies the target when the form is posted; the _top means that the any output generated by the action taking procedure will be targeted to the top container of the frame rather than the frame itself. You can specify the name of a frame as target, such as editFrame or selectFrame, if that is what you intend to do. The last parameter specifies a name for the form so that it can be referenced by name in JavaScript.
If a name is not specified for a frame or form, it can still be referred in JavaScript as frames[n] or forms[n], where n is the n occurrence of the frame within the frameset or the nth occurrence of the form within a frame, respectively.
Note - the use of the Onchange event handler in line 20 to invoke the checkNum() JavaScript function that does simple numeric validation. In line 24, the built-in JavaScript function toUpperCase() is used to automatically change the text to uppercase upon data entry. Line 27 creates a submit button for posting the form and line 28 creates a reset button to clear field contents.
4. Run the CHP17_16.SQL file in SQL*Plus. The procedure is used to generate dynamic HTML for the second frame in the frameset.
SQL> START CHP17_16
SQL> CREATE OR REPLACE PROCEDURE QUERY17 AS
2 CURSOR DEPT_CURS IS
3 SELECT DEPT_NO, DEPT_NAME
4 FROM DEPT17;
5 BEGIN
6 HTP.HTMLOPEN;
7 HTP.SCRIPT(‘function pickItem(d_no, d_name)
8 {
9 top.editFrame.document.editForm.deptNo.value = d_no
10 top.editFrame.document.editForm.deptName.value = d_name
11 top.editFrame.document.editForm.addBtn.value = “Update”
12 }’, ‘JavaScript’);
13 HTP.BODYOPEN;
14 HTP.FORMOPEN(‘QUERY17’, ‘POST’, ‘selectFrame’);
15 HTP.TABLEOPEN(‘BORDER’);
16 HTP.TABLEROWOPEN(‘LEFT’, ‘TOP’);
17 HTP.TABLEHEADER(‘Sel’, ‘CENTER’);
18 HTP.TABLEHEADER(‘No:’, ‘CENTER’);
17 HTP.TABLEHEADER(‘Name:’, ‘CENTER’);
20 HTP.TABLEROWCLOSE;
21 FOR DEPT_REC IN DEPT_CURS LOOP
22 HTP.TABLEROWOPEN(‘LEFT’, ‘TOP’);
23 HTP.TABLEDATA(‘ 24 ‘ONCLICK=”pickItem(‘’’ ||
25 DEPT_REC.DEPT_NO || ‘’’,’’’ ||
26 DEPT_REC.DEPT_NAME || ‘’’)”>’);
27 HTP.TABLEDATA(DEPT_REC.DEPT_NO,‘CENTER’);
28 HTP.TABLEDATA(DEPT_REC.DEPT_NAME, ‘LEFT’);
29 HTP.TABLEROWCLOSE;
30 END LOOP;
31 HTP.TABLECLOSE;
32 HTP.FORMCLOSE;
33 HTP.BODYCLOSE;
34 HTP.HTMLCLOSE;
35 END QUERY17;
36 /
JavaScript is a scripting language with a horde of powerful features. The script provided in lines 7 through 12 in the body of the HTP.SCRIPT procedure dynamically changes the values of fields in the first frame when an item is selected from the second frame.
A form is opened in line 14 as we have a few buttons in this document. A common mistake is to not open a form before creating form elements. Although the form is never posted, note the recursive use of the same procedure name as the processing procedure when the form is posted. This can be useful in certain complex applications because the procedure is self-sufficient as it generates and processes the same form without the need for having two separate procedures.
Line 15 specifies the tag for a bordered HTML table. In lines 23 to 26, a button is created for each record in the DEPT17 table, and when clicked it causes the DEPT_NO and DEPT_NAME column values from that row to be sent to the form fields in the first frame. Note how PL/SQL variables are passed to a JavaScript function.
Passing JavaScript variables to PL/SQL gets a bit trickier, as you must use the GET method after creating dynamic URL to invoke a particular stored procedure. JavaScript poses the same difficulties as HTML with special characters.
Using JavaScript, URLs can by dynamically constructed, and using the GET method, targeted into a window or a frame for execution. This approach is powerful in having great control over the behavior of the application, but as mentioned earlier suffers a setback due to GET limitations.
While HTML uses entity references for escaping characters, JavaScript uses URL character encodings. Thus, any ISO Latin-1 character when escaped is represented by %hh where hh is the hex code corresponding to the character. URL character encoding is a superior escaping mechanism than entity references, but this leaves us with two different escaping schemes that have to be catered for separately depending on whether the data is embedded into HTML or handled by JavaScript. For instance, spaces have to be translated into plusses when doing a GET through HTML as in the FRAME tag, whereas spaces would be translated to 20 percent (20 is hex code for space) when using a dynamic URL to do a GET using JavaScript.
In JavaScript, a handy JavaScript function escape() does the escaping, while unescape() is used to restore original data. There are cases in which this escaping has to use PL/SQL, like when passing PL/SQL parameters to an embedded event handler JavaScript function. For this, a simple PL/SQL function needs to be used that replaces all special characters into the corresponding %hh code, where hh is the hex code corresponding to that character. Again, dynamic URLs to do a GET should be used only as a last resort, and care should be taken to ensure that the URL size does not exceed the operating system limit imposed on maximum size of environment variables.
When using GET, the URL is actually stored in a standard CGI environment variable called QUERY_STRING. For example, on Windows NT 4 the maximum size for environment variables is 1,024 bytes, and a URL longer than that will impose a bottleneck if data is such that the URL size exceeds the 1,024 byte limit. 5. Run the CHP17_17.SQL file in SQL*Plus, as shown in Figure 17.33. The procedure within the file processes the form is created in Step 1.
Lines 1 through 3 create a stored procedure with parameters corresponding with the fields contained in the form created in Step 1. Whenever the Add or Update button is clicked, this stored procedure is invoked to process the form.
The IF statement in line 5 checks if the Add button was clicked and inserts a record in the DEPT17 table using the form field values. The button name is changed to Update if a record was selected from the selectFrame. So if the name of the button is Update, the record is updated in lines 8 to 10. Because the target was specified to be _TOP in the form, the complete frameset is redrawn by calling FRAMES17 in line 12. If you specified the target to be editFrame then you would simply call MENU17 to redraw the first frame. The complete frameset is redrawn as we want the inserted or updated record to be appear in the selectFrame.
6. Open the http://localhost/owa_waite/plsql/frames17 URL in your browser. The two frames are displayed within Netscape Navigator. Enter a value for the form fields as shown in Figure 17.34 and press the Add button. The new screen is shown in Figure 17.35.
Select a record from the second frame. The Add button changes to Update. Change the values for the form fields as shown in Figure 17.36 and press the Update button. The new screen is shown in Figure 17.37.
How It Works
The HTP.SCRIPT procedure generates the <SCRIPT> and </SCRIPT> tags, which contain a script written in languages such as JavaScript, JScript, or VBScript. The text that makes up the script is provided as the first parameter and the language in which the script is written is provided as the second parameter. If this parameter is omitted, the user’s browser determines the scripting language. In this How-To, JavaScript is embedded in the generated HTML to handle events related to form fields. This enables you to create web pages that react directly to user interaction with form elements, and data to be preprocessed on the client before submission to the server.
Step 1 creates a sample table which is the source of data in a later step. Step 2 creates a frameset with two frames and directs an URL to each frame. In Step 3, a data entry form is created. The sample table created in Step 1 is queried by a cursor in the stored procedure created in Step 4 to display an HTML table in the second frame using the contents of the sample table. When the query is executed, a row is created along with a select button to transfer that record to the first frame. Step 5 creates a procedure to process form variables after the form is posted. Step 6 displays the two frames in a browser along with the add/update operation. Comments
Although the procedures are created separately in this How-To, it would be a better idea to create a stored package to tie all procedures related to creation and processing of forms in a web document.
JavaScript allows for client-side scripting to take some of the processing load from the server, but bugs are waiting to be removed in the next versions of browsers. Indiscriminate use of JavaScript has pitfalls for the unwary. Currently, heavy use of JavaScript or Java leads to memory leaks, causing Netscape and MSIE on Windows 3.1 and Windows 95 to crash; on Windows NT the browser survives, but the buttons and other form elements stop working.
If you are planning to use Microsoft Internet Explorer and JScript, you also need to know how JavaScript and JScript relate. JScript remains a subset of JavaScript functions plus a few extensions. Netscape understands JavaScript functions that are not yet understood by MSIE, but few functions do exist in JScript that are not understood by Netscape. Some functions defined in both JavaScript and JScript may not work in one or the other browser, either due to a bug in the implementation of that function in a particular browser, or due to a browser-specific constraint on the way that function is expected to be called. Code written to be handled by JScript, and which is also standard JavaScript, will work in both browsers but should always be tested for both browsers.
JavaScript features that are lacking in JScript include the following:Despite the fact that bugs remain in JavaScript and JScript, as long as those features are not used or are used with workarounds, scripting languages are very useful while developing interactive weblications. If you are using Designer/2000 1.3W, it automatically generates JavaScript code to do client side validation.The capability of supporting dynamic image objects to change the source file of an image.
External scripts that allow JavaScript code to be stored in an external file.
Dynamic select objects, to add, delete, or change options in a select list on-the-fly.
The javaEnabled(), split(), scroll(), and reset() methods.
JavaScript features that are modified in JScript include the following:
The capability to create a new window and modify its properties; this works partially and frames have to referred as frames[n] instead of framename.
history.go() accepts integers and not strings. JScript also needs casting of objects in alert().
onAbort() event handler is absent, and onMouseMove() and onMouseOut() work differently. The onClick() handler should also be followed by LANGUAGE=“JavaScript”.
MAILTO: and ABOUT:BLANK protocols do not work; DOCUMENT.REFERER does not work; changing LOCATION.HREF and LINK.HREF does not work.
JScript is case-insensitive, JavaScript is not.
JScript has added new FOR and EVENT attributes to the SCRIPT tag.