| <!-- $Id$ --> |
| <!-- |
| Copyright 2004 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <chapter id="script"> |
| <title>Tapestry and JavaScript</title> |
| <para> |
| Building cutting edge Web applications is not entirely about the server side. |
| A significant amount of work must be done on the client side to support truly dynamic |
| user experiences. Typically, this scripting is done using the JavaScript language embedded |
| into major web browsers such as Internet Explorer and Netscape Navigator. |
| </para> |
| <para> |
| These effects range from simple effects such as image rollovers (changing |
| the icon used for a link when the cursor is over it) to more involved |
| patterns such as client side validation of forms or even complex animations. |
| </para> |
| <para> |
| In traditional, static web page development, the HTML producer (the person creating |
| the static HTML page) |
| is completely responsible |
| for this aspect of development, usually aided by a web page authoring tool, such as |
| Dreamweaver. Ultimately, though, the HTML producer assigns unique names or ids to |
| various elements on the page, and attaches JavaScript event handlers to |
| the elements.</para> |
| <example> |
| <title>Traditional JavaScript usage</title> |
| <programlisting><![CDATA[ |
| |
| var preload = new Array(); |
| preload[0] = new Image(); |
| preload[0].src = "/images/button.gif"; |
| preload[1] = new Image(); |
| preload[1].src = "/images/button-highlight.gif"; |
| |
| function rollover(image, index) |
| { |
| image.src = preload[index].src; |
| } |
| |
| . |
| . |
| . |
| <a href="..." |
| onMouseOver="javascript:rollover(document.button, 1);" |
| onMouseOut="javascript:rollover(document.button, 0);"> |
| <img name="button" src="/images/button.gif"> |
| </a> |
| |
| ]]></programlisting> |
| </example> |
| <para> |
| The preloading business is all about forcing the browser to load the image <emphasis>before</emphasis> |
| it is needed, so that it is already in memory when the mouseover event handler |
| needs it. |
| </para> |
| <para> |
| From here, adding additional rollovers means extending the <varname>preload</varname> array, providing |
| names for the additional <sgmltag class="starttag">img</sgmltag> elements and writing the additional |
| event handlers for the <sgmltag class="starttag">a</sgmltag> elements. |
| </para> |
| <para> |
| Now, envision a running Tapestry application. With everything so dynamic (especially when you account for things like the |
| &Foreach; component), it's all but impossible to even know how many links and buttons will be on the page, never |
| mind what they'll |
| all be named. At first glance, it may appear that Tapestry prevents the use of this kind of scripting. |
| </para> |
| <para> |
| In fact, Tapestry is structured to enhance this kind of scripting. This is faciliated by the &Body; component, which replaces |
| the <sgmltag class="starttag">body</sgmltag> element of the page. |
| The <link linkend="script.body">next section</link> described the services |
| the &Body; component povides to facilitate complex client-side scripting. |
| </para> |
| <section id="script.body"> |
| <title>The Body component</title> |
| <para> |
| The &Body; component provides a number of services to the components it wraps. It handles preloading of |
| images. It provides the ability to add arbitrary JavaScript to the page, to include an external static |
| JavaScript document, or to add JavaScript |
| to the |
| <sgmltag class="starttag">body</sgmltag> element's onload event handler. Finally, it provides an easy way |
| to generate unique identifiers needed for things like image and function names. |
| </para> |
| <para> |
| When the &Body; component renders, it registers itself as an attribute of the &IRequestCycle;. This |
| allows components wrapped by the &Body; component, directly or indirectly, to locate it and invoke methods |
| on it. These methods are used to define preloaded images, and add JavaScript code to the response HTML. |
| </para> |
| <figure> |
| <title>Body Component Rendering Sequence</title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/Body-sequence.gif" format="GIF"/> |
| </imageobject> |
| </mediaobject> |
| </figure> |
| <para> |
| When rendering is complete, the &Body; component will have produced four distinct portions of the |
| HTML response: |
| </para> |
| <informalexample> |
| <programlisting> |
| <script language="JavaScript" src="..."></script> <co id="script.body.include"/> |
| <script language="JavaScript"><!-- <co id="script.body.script"/> |
| |
| ... |
| |
| function tapestry_onLoad() <co id="script.body.onload"/> |
| { |
| } |
| |
| // --> </script> |
| <body onload="javascript:tapestry_onLoad();"> <co id="script.body.tag"/> |
| |
| ... <co id="script.body.wrapped"/> |
| |
| </body> |
| |
| </programlisting> |
| </informalexample> |
| <para> |
| <calloutlist> |
| <callout arearefs="script.body.include"> |
| <para>Any number of included static scripts may be added to the page.</para> |
| </callout> |
| <callout arearefs="script.body.script"> |
| <para> |
| This script block is only emitted when necessary; that is, because some component |
| needed to generate scripting or initialization (or preloaded images). The block |
| is properly "commented" so that older browsers, those that fail to support scripting, |
| will not be confused by the JavaScript code. |
| </para> |
| </callout> |
| <callout arearefs="script.body.onload"> |
| <para> |
| The onload event handler function is only generated if some component requests some |
| onload initialization. |
| </para> |
| </callout> |
| <callout arearefs="script.body.tag"> |
| <para> |
| The <sgmltag class="starttag">body</sgmltag> tag only specfies a <varname>onload</varname> |
| event handler function if one is needed. |
| </para> |
| </callout> |
| <callout arearefs="script.body.wrapped"> |
| <para> |
| The content of the <sgmltag class="starttag">body</sgmltag> element is defined by the |
| Tapestry components it wraps. Importantly, the rollovers, JavaScript, event handlers and |
| the content are all generated in parallel (the &Body; component uses buffering so that |
| the JavaScript portion is written out first). |
| </para> |
| </callout> |
| </calloutlist> |
| </para> |
| </section> |
| |
| <section id="script.spec"> |
| <title>Script Specifications and Script Components</title> |
| |
| <para> |
| The &Body; component only lays the foundation for client-side JavaScript support in Tapestry. |
| Tapestry includes its own, XML-based language for |
| create dynamic JavaScript. |
| </para> |
| |
| <para> |
| A Tapestry Script Specification takes as input a number of |
| <emphasis>symbols</emphasis>, each of which is a named object. These |
| input symbols are combined to form additional symbols. Additional XML tags |
| allow a script to place JavaScript into the main script body, or into the |
| initialization. |
| </para> |
| |
| <para> |
| The most common use for script specifications is to add client-side behavior to |
| form elements. The input symbol is a form component, |
| from this, the name of the element and containing form are determined. Next, |
| the name of one or more event handler functions are defined. |
| </para> |
| |
| <para> |
| In the body, the functions are actually created. In the initialization, |
| the event handlers are wired to the form and form elements. |
| </para> |
| |
| <para> |
| In some cases, a script specification may produce usable output symbols (commonly, the |
| names of a JavaScript function that should be tied to some component's event |
| handler). |
| </para> |
| |
| <note> |
| <para>A detailed example is coming. |
| </para> |
| </note> |
| |
| |
| </section> <!-- script.spec --> |
| |
| </chapter> |