library.onLoad – what does it do and who loads last?

In this article I will demonstrate that there multiple methods for determining the “onLoad” event of you page and discuss which method you should use and when.

Introduction

Once upon a time when the world was young and we did not have JavaScript libraries we had the onLoad event of the document.

	<body onload="alert('Hi Marky')">

This caused some issues in itself because it indicated that the HTML was loaded – but did not have any knowledge of the images on the page and they might not have finished downloading – so this was not a reliable way of determining if the page was really loaded.

Then came the Library

If you are including functionality from a Library there is a high probability that the Library is doing some sort of Document Model (DOM) manipulation when the page loads – it has to do this to provide the functionality you are asking it to do.

Take your XPage as a prime example – you are including (well ok the XPage is including) the dojo Library and with that the XSP library built to provide XPage functionality. If you look at the source of an XPage containing a date control you will see this

<input id="view:_id1:_id2:_id39:inputText2" class="xspInputFieldDateTimePicker" type="text" name="view:_id1:_id2:_id39:inputText2" />

But if you look at the HTML created AFTER dojo and the XSP have done their magic the field looks like this!!

<span class="xspInputFieldDateTimePicker" style="display: inline-block;">
</span>
<div id="widget_view:_id1:_id2:_id39:inputText2" class="dijit dijitReset dijitInlineTable dijitLeft xspInputFieldDateTimePicker dijitTextBox">
	<div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" tabindex="-1" type="text" value="? " readonly="readonly" />
	</div>
	<div class="dijitReset dijitInputField dijitInputContainer"><input id="view:_id1:_id2:_id39:inputText2" class="dijitReset dijitInputInner" tabindex="0" type="text" value="" />
		<input style="display: none;" type="text" name="view:_id1:_id2:_id39:inputText2" />
	</div>
</div>

<span id="view:_id1:_id2:_id39:inputText2_Container" class="xspInputFieldDateTimePicker" style="display: inline-block;">
	<span class="dijit dijitReset dijitInline dijitButton xspInputFieldDateTimePicker">
		<span class="dijitReset dijitInline dijitButtonNode">
			<span id="dijit_form_Button_0" class="dijitReset dijitStretch dijitButtonContents" style="background-image: none; margin: 0px; border: none; padding-left: 0px; background-position: 0% 0%; background-repeat: initial initial;" title="">
				<span class="dijitReset dijitInline dijitIcon xspInputFieldDatePickerIcon">
				</span>
				<span class="dijitReset dijitToggleButtonIconChar">?</span>
				<span id="dijit_form_Button_0_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">
				</span>
			</span>
		</span>
		<input class="dijitOffScreen" tabindex="-1" type="button" value="" />
	</span>
</span>

And that doesn’t happen in an instant – it takes time……and if we want to programmatically interact with the “new” date picker code we have to wait for the dojo and XSP to do their thang.

Library.onLoad
This is where library.onLoad comes into play – it is an event triggered when the library believes that the page is safe to interact with – i.e. everything it needs to do is complete.

dojo.addOnLoad

The dojo.addOnLoad page explains better than I can

dojo.addOnLoad is a fundamental aspect of using Dojo. Passing addOnLoad a function will register the function to run when the Dom is ready. This differs slightly from document.ready and body.onload in that addOnLoad waits until all dojo.require() (and their recursive dependencies) have loaded before firing.

XSP.addOnLoad

The XSP is a library built on top of a library and has dependencies on the dojo library.

In our XPage environment we have the onClientLoad event accessible from the XPage event section – this is actually the GUI equivalent to XSP.addOnLoad which is a programmatic function we have available through the XSP library.

onClientLoad
onClientLoad

jQuery – $(‘document’).ready()

jQuery’s version of this onLoad capability is described as follows:

While JavaScript provides the load event for executing code when a page is rendered, this event does not get triggered until all assets such as images have been completely received. In most cases, the script can be run as soon as the DOM hierarchy has been fully constructed. The handler passed to .ready() is guaranteed to be executed after the DOM is ready, so this is usually the best place to attach all other event handlers and run other jQuery code. When using scripts that rely on the value of CSS style properties, it’s important to reference external stylesheets or embed style elements before referencing the scripts.

These people are so much more eloquent that I 🙂

So if everyone fires when the DOM is ready then they should all fire at the same time…right?

Well that is what occurred to me and I wanted to find out – people have told me through the life of this blog that I should always use the XSP.addOnLoad because it fires when the XPage is ready – but if I use jQuery is that ready as well?

Experiment

It is really not all that complicated to test who fires first – what I created was a variable to track when the page started to load and then asked each library to subtract the time when they fired “when the DOM was ready” and write it to the page. This code sample was added to the top of my XPage so the variable “a” is created immediately when the page is loaded and each library then triggers when it is ready.

<script type="text/javascript">// <![CDATA[
		var a = new Date()
		document.write("Started at: "+a)

		$('document').ready(function(){
			// alert('jQuery')
			var b = new Date()
			document.getElementById('divJquery').innerHTML = b-a
			var s=document.createElement('div');
			s.innerHTML = "<div>"+"jQuery - "+(b-a)+"</div>"
			document.getElementById('iAmFinished').appendChild(s)
		})

		XSP.addOnLoad(function(){
			// alert('XSP')
			var c = new Date()
			document.getElementById('divXSP').innerHTML = c-a
			var s=document.createElement('div');
			s.innerHTML = "<div>"+"XSP - "+(c-a)+"</div>"
			document.getElementById('iAmFinished').appendChild(s)
		})

		dojo.ready(function(){
			// alert('dojo')
			var d = new Date()
			document.getElementById('divDojo').innerHTML = d-a
			var s=document.createElement('div');
			s.innerHTML = "<div>"+"dojo - "+(d-a)+"</div>"
			document.getElementById('iAmFinished').appendChild(s)
		})

// ]]></script>

and in the onClientLoad event


		<xp:this.script><![CDATA[
			var e = new Date()
			document.getElementById('divOnClientLoad').innerHTML = e-a
			var s=document.createElement('div');
			s.innerHTML = "<div>"+"onClientLoad - "+(e-a)+"</div>"
			document.getElementById('iAmFinished').appendChild(s)
			]]>
		</xp:this.script>

Demonstration

You can see the page I created to test this experiment here

http://demo.xomino.com/xomino/xPlay.nsf/xOnLoad.xsp

You will see that there are a lot of typeAhead and date picker fields on the page – the reason I did this was to slow down the loading of the page – The libraries were all firing very close together and I wanted to see if I could separate them by delaying the page loading – it didn’t see to make a difference they all fired with the same delay between them.

The results

Well I have to say I was surprised and very glad that I created this experiment because it will change how I determine when the DOM is ready in the future. You can keep testing this for yourself by refreshing the test page above but you will see consistently that the libraries fire in this order:

Library Time to
DOM ready (ms)
Dojo 198
XSP 197
onClientLoad (XSP) 198
jQuery 209

As you refresh the page you will see different load times but they are always grouped XSP, clientOnLoad

results of load order
results of load order

Why are these different?

Each library determines when they believe the DOM is ready differently – each one is assuming that the DOM is ready when everything is loaded for it’s own library (naturally dojo doesn’t care when jQuery is loaded). For some insight check this StackOverflow post out

How does the jQuery ready() function work?

From my experiments I noticed that dojo, XSP and onClient load were always no more than 1 milisecond apart and each one of them sometimes came first – i think there is an aspect of the fact that they are all trying to create a DIV and append it to the page (my test code) and that has some nominal time associated with it and they are all trying to append to the same DOM element – for the sake of statistical accuracy my observation is that dojo is usually the first to record a result but because it is not always first I cannot claim that categorically.

I also have to add that there is a discrepancy with the onClientLoad test because it does not use the same variable as the other libraries – because it is being added to the page in a different fashion it is not an identical test. I am not however going to add the whole of the code to the onClientLoad event because then I would be asking the onClientLoad event to trigger the other libraries which isn’t right.

Conclusion

So here is the important things to remember when you are developing your xPage

  1. Never use dojo.addOnLoad – it fires almost exactly the same time as the XSP but more importantly your xPage XSP library is not ready
  2. If you are using jQuery *always* use $(‘document’).ready()
  3. Otherwise use XSP.addOnLoad/onClientLoad

It is interesting to see that jQuery takes longer to be ready and why that is, is way beyond the comprehension of this developer but there is a clear separation and because almost all of my work uses jQuery in one fashion or another I will always use $(‘document’).ready()

Please feel free to argue my experimental methodology if you think it can be improved 🙂

One thought on “library.onLoad – what does it do and who loads last?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s