Mika Tähtinen


Wednesday 17.2.2010 21.50

Prototype patch for buffered document events

While moving towards event driven JavaScript development in IRC-Galleria we’ve faced some problems where events get fired before their observers have been loaded. For instance a link on the user profile page fires an event which should open the mod browser:

document.fire('modbrowser:init', { imageId: 666 });

The observer for this event is loaded at the end of an external JavaScript file:

Event.observe(document, 'modbrowser:init', 
    ModBrowser.onInit.bindAsEventListener(ModBrowser));

While trying to keep the page load times low, we try to link external JavaScript files at the end of the document instead of the head tag. This means that the observer starts listening to the event fairly late, especially if there’s some laggy pictures or JavaScript files linked. So it’s pretty common that user manages to click a link that fires an event before the observer is ready.

Another issue concerning events, and being solved at the same, is the usage of document.dom:loaded (or window.load on IE browsers) event with AJAX requests. For instance when a view is printed there may be some need to do something to the DOM tree. As commonly known IE6 and IE7 will fail if the DOM tree is manipulated before the page has been loaded. So when this kind of view is printed using a non-AJAX request, we need to wait for load events. But sometimes the same view may be printed using AJAX and then there’s no use on waiting for load events that have already been fired.

So, we created a patch for Prototype’s document events. The patch is fairly simple, when document.fire is called, the events are pushed to document.bufferedEvents and when document.observe is called, the bufferedEvents is checked for matching events, which, if found, will be fired to the new observer at once.

Object.extend(document, {
    bufferedEvents: new Hash(),

    fire: document.fire.wrap(function(proceed, eventName, memo) {
        var a = document.bufferedEvents.get(eventName);

        if (!a) {
            a = new Array();
            document.bufferedEvents.set(eventName, a);
        }

        a.push(proceed(eventName, memo));
    }),

    observe: document.observe.wrap(function(proceed, eventName, handler) {
        var a = document.bufferedEvents.get(eventName);

        if (a) {
            a.each(function(event) {
                handler(event)
            });
        }

        proceed(eventName, handler);
    })
});

Now observers get their events, even if they missed the time when the event was fired, win. :)

javascriptprototypeirc-galleriaduunigaltsudev