You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Tapestry and JavaScript

Tapestry 5 has had a interesting mix of characteristics.

On the one hand, it has had a large number of features that work, and work well, right out of the box, with no special configuration or setup. This includes client-side validation, dynamic content updates, simple animations, progressive enhancement, and other standard Ajax and DHTML use cases.

In addition, Tapestry has evolved, from Tapestry 5.0 through 5.3, into a quite capable provisioning framework:

  • JavaScript libraries may be combined into stacks that are combined (in production) into a single virtual file
  • JavaScript libraries and CSS files may be minified
  • Libraries, stacks, and other resources are exposed to the browser with a versioned URL and far-future expires header, to support aggressive client-caching
  • Resources, including JavaScript and CSS, can be distributed inside JARs (as part of reusable component libraries)
  • Compressible resources will be automatically GZip compressed if the client supports it

Tapestry JavaScript Limitations (through 5.3)

Dependence on Prototype/Scriptaculous

Tapestry made an early choice to embrace Prototype and Scriptaculous at a time when this made sense, circa 2006-2007.

The goal was to have Tapestry provide a client-side API, the Tapestry namespace, that in turn would delegate complex behaviors (including DOM element selection, event management, and XmlHttpRequest processing) to a foundational framework. The goal was to isolate all the direct dependencies on Prototype in such a way that it would be possible, in the future, to swap out for a different foundational framework, such as jQuery or ExtJS. Unfortunately, expediency has proven to make this goal even less reachable!

Lack of Documentation

There has not, to date, been an adequate documentation of the Tapestry namespaces, beyond the code itself.

Lack of Module Structure

Beyond the basic use of namespaces, Tapestry has not embraced modern JavaScript usage; specifically, it makes limited use of hygenic functions to form modules. Hygenic functions are JavaScript functions that exist as a way to encapsulate private properties and functions. Tapestry 5.3 makes more use of this pattern than previous releases.

Complex Initialization

Many users are perplexed by how Tapestry performs initialization: in a typical application, the developer will create a <script> block at the bottom of the page, and do initializations there. In Tapestry, it can be much more complex:

  • A JavaScript library, containing one or more initialization functions, is created
  • The initialization functions must be monkey patched into the Tapestry.Initializers namespace
  • The JavaScriptSupport environmental must be used to invoke the function, by name, passing it a JSONObject to configure itself (the "specification")

This often feels like overkill. It is necessary for a number of desirable characteristics:

  • Initialization code occurs in a single <script> block at the end of the page (just before the </body> tag)
  • There is limited support for structuring the order of initialization
  • The mechanism works transparently in both full-page render (traditional) and partial-page renders (Ajax)

Despite this, the Tapestry approach can feel very "heavy". In a bespoke 1 page, initialization takes the form of a single event handler, often attached to the <body> element, that caches events that bubble up from much lower in the DOM. The single handler function identifies the applicable elements using CSS selectors, including those that are based on HTML5 data- attributes. Additional data- attributes will define additional behavior ... for example, a URL for a triggered request. This is "light" because:

  • There's a single event handler function
  • The event handler may be anonymous (there's no name, or possibility of collision)
  • Elements are identified by DOM structure and CSS rather than their unique id
  • Additional additional necessary configuration is directly attached to the element
  • As the page is dynamically updated, there is no extra "bookkeeping" for added or removed elements; new elements inserted into the DOM dynamically are recognized as easily as those that were present on the initial render

By contrast, Tapestry is heavy:

  • The initialization must have a unique name
  • The element must have a unique id
  • The event handlers are attached directly to the element
  • Duplicated elements will have duplicated event handlers
  • Additional behavior is specified as a JSON object passed to the initialization function
  • Injecting new elements into the DOM requires invoking initialization functions to wire up the necessary event handlers
  • On older versions of Internet Explorer, removing elements may leave memory leaks as JavaScript objects retain references to DOM objects

JavaScript Evolution (5.0 to 5.3)

JavaScript Improvements for 5.4


  1. By "bespoke", we mean a non-component-based, manually created page; a standalone static HTML page.

  • No labels