Uploaded image for project: 'Tapestry 5'
  1. Tapestry 5
  2. TAP5-544

Improve JavaScript handling to prevent the user from clicking Ajax-oriented links and forms before the page is "ready"



    • Improvement
    • Status: Closed
    • Major
    • Resolution: Fixed
    • tapestry-core
    • None


      I think it's well time to open a discussion about Tapestry 5 and JavaScript. I think Tapestry is getting a large number of things right, but also a small number of things wrong. This is not a discussion about jQuery vs. Prototype (that's a separate area), but more about the behavior of Tapestry JavaScript, within the constrains of the browser.

      In standard usage, the JavaScript for a page is collected together and moved to the bottom of the page: first a series of <script> tags to load JavaScript libraries, then a single block of code to perform all kinds of initialization; this block executes, ultimately, when the page is fully loaded: after all HTML and JavaScript (but, depending on the browser, before all images have fully loaded).

      This is good and bad; the good part is that we are following Yahoo's performance guidelines: JavaScript at the bottom of the page, so it doesn't slow down rendering of the markup. However, this means that common practices, such as using the "javascript:" psuedo-scheme (i.e. <a src="javascript:...">) are not possible, since the referenced JavaScript would not have been loaded yet. In fact, many users must configure Tapestry to move the scripts back up to the top of the page (inside the <head>) to meet external demands (of third-party URL trackers and advertising solutions).

      Further, on a page that loads slowly (one that has a large number of external scripts, is accsessed via a slow-bandwidth pipe, or has a very complex layout), this means that JavaScript event handlers are not wired up until all JavaScript has been downloaded and parsed. The end result it that you can "outrace" Tapestry, click a link that should update a zone and get incorrect behavior or even a runtime exception. I actually see this when using Formo's time-tracking application from home through a slow pipe, exacerbated by HTTPS, where I get the error that "Block is not a valid response type". What should have been an Ajax request was processed instead as a traditional page render request, because the JavaScript was not ready.

      An earlier version of Tapestry 5 approached this problem by disabling the link components when the zone parameter was in use; that is, the href parameter was written out a "#", so the only way the link would be active is via an attached onclick event handler.

      This solution was weak, because there was no graceful degradation: clients without JavaScript would have a non-functioning application. Thus it was changed to render the href normally AND add an onclick event handler, which leads to the race conditions described above.

      What I really would like to see is the following:

      The page renders normally. If a user submits a form or clicks a link before all initialization code has executed, then a popup dialog will appear to inform the user that the page is still loading. When the load is complete, the message changes and the dialog fades out.

      Possibly, when a page is loading a more subtle floating "Loading ..." dialog would appear and disappear once the page is, in fact, loaded.

      What would it take to accomplish this?

      Firstly, JavaScript libraries would have to move (back) to the <head>, permanently, no configuration (well, short of replacing some internal services). We can't have inline javascript unless the javascript being referenced loads first. Hopefully, we'll be able to make up the performance difference (if measurable) with the future plans to minimize and combine JavaScript for the page. JavaScript initialization would still occur at the bottom.

      Next, Ajax-y links and forms would have something like onclick="javascript:Tapestry.waitForPageLoad();" written into their HTML; this would be the logic that would raise the dialog if you clicked a link too early.

      Once the Prototype dom:loaded event is fired, and all the normal JavaScript initialization takes place (from what I gather about IE, it's still a good idea to wait for dom:loaded, rather than simply putting the code at the bottom of the page), part of the process would be to a) fade out the dialog if showing and b) remove the onclick handler for any elements. This is tricky, because the initialization code often adds onclick event handlers. Perhaps the handlers can stay, but will be inactive because the page has loaded.

      Anyway, this is how I think we should proceed.

      I think this would keep compatibility with existing applications.

      The cost would be:

      • Slightly uglier HTML output (use of the "javascript:" psuedo-scheme)
      • Possibly slower render of page, as the browser waits for JavaScript to load
      • Increment increase in size of tapestry.js

      The advantage is less confusion on the client side and server side between normal requests and Ajax partial render requests. The user can't "outrace" the application anymore.

      I'd like to see what people think of this plan and collect any comments or observations I've missed.




            hlship Howard Lewis Ship
            hlship Howard Lewis Ship
            0 Vote for this issue
            1 Start watching this issue