Update Panel .NET

Exploring Microsoft ASP.NET AJAX and jQuery

Sys.Application initialization steps

leave a comment »

I think the most important lesson in building client-side components using the Microsoft AJAX Library, is to learn about the client-side lifecycle and the events that gets raised at each stage, so I looked into MicrosoftAjax.debug.js and noted down my observations:

When MicrosoftAjax.js is loaded, the Sys.Application (singleton) object is created:

Sys.Application = new Sys._Application();

Inside the constructor function, window load and unload events are hooked up:

Sys._Application = function Sys$_Application() {
    Sys._Application.initializeBase(this);
    ...
    this._unloadHandlerDelegate = Function.createDelegate(this, this._unloadHandler);
    this._loadHandlerDelegate = Function.createDelegate(this, this._loadHandler);
    Sys.UI.DomEvent.addHandler(window, "unload", this._unloadHandlerDelegate);
    Sys.UI.DomEvent.addHandler(window, "load", this._loadHandlerDelegate);
}

The _loadHandler method first unhooks the window load event handler, then invokes the initialize method:

function Sys$_Application$_loadHandler() {
    if (this._loadHandlerDelegate) {
        Sys.UI.DomEvent.removeHandler(window, "load", this._loadHandlerDelegate);
        this._loadHandlerDelegate = null;
    }
    this.initialize();
}

The initialize method defers invoking _doInitialize until the browser yields, using the setTimeout trick:

function Sys$_Application$initialize() {
    if(!this._initialized && !this._initializing) {
        this._initializing = true;
        window.setTimeout(Function.createDelegate(this, this._doInitialize), 0);
    }
}

Then inside the _doInitialize method:

  • Sys.Component.initialize() is called. (Sys.Component is the base class of Sys._Application)
  • init event handlers are invoked.
  • calls raiseLoad.
function Sys$_Application$_doInitialize() {
    Sys._Application.callBaseMethod(this, 'initialize');
    var handler = this.get_events().getHandler("init");
    if (handler) {
        this.beginCreateComponents();
        handler(this, Sys.EventArgs.Empty);
        this.endCreateComponents();
    }
    this.raiseLoad();
    this._initializing = false;
}

In raiseLoad method:

  • load event handlers are invoked.
  • pageLoad() function is invoked. (If one exists!)
function Sys$_Application$raiseLoad() {
    var h = this.get_events().getHandler("load");
    var args = new Sys.ApplicationLoadEventArgs(Array.clone(this._createdComponents), !this._initializing);
    if (h) {
        h(this, args);
    }
    if (window.pageLoad) {
        window.pageLoad(this, args);
    }
    this._createdComponents = [];
}

Problem!

Sys.Application.initialize uses the setTimeout trick to defer initialisation until the DOM is ready, but many have discovered and documented (see links at the end of the post) that using setTimeout cannot guarantee DOM readiness!

Calling initialize prematurely (i.e. before the DOM is ready), means that DOM elements may not have completely initialized and have their states restored (IE/Safari), this can lead to a whole load of issues when manipulating those elements, especially after a user clicks on the browser’s back button.

If Sys.Application.initialize() was not explicitly called, but invoked when the window.onload event is raised, DOM elements would have initialized and their (selection) states restored. But the window.onload event is only raised after all resources have been loaded, which is not ideal.

Now that Microsoft is shipping and supporting jQuery, I suggest using the “document is ready” event:

YourControl.prototype = {
    initialize: function() {
        $(document).ready(Function.createDelegate(this, this._onDocumentReady));
    },
    _onDocumentReady: function() {
        ... your initialize code ...
    },
    ... rest of your code ...
}

After a quick search, heaven knows why I didn’t do this first, I found these related posts that also highlight the problems and offer possible solutions:

Written by tzkuei

October 17, 2008 at 10:42 pm

Leave a Reply