blob: 9fbe3aee9827771aa155c65f2b2cf386a816d306 [file] [log] [blame]
<!-- $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>
&lt;script language="JavaScript" src="..."&gt;&lt;/script&gt; <co id="script.body.include"/>
&lt;script language="JavaScript"&gt;&lt;!-- <co id="script.body.script"/>
...
function tapestry_onLoad() <co id="script.body.onload"/>
{
}
// --&gt; &lt;/script&gt;
&lt;body onload="javascript:tapestry_onLoad();"&gt; <co id="script.body.tag"/>
... <co id="script.body.wrapped"/>
&lt;/body&gt;
</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>