Dynamic TabPanel height with Dojo TabContainer in XPages

In this article I will discuss how to make your Tab Panels dynamically resize to the size of their content. I will use the doLayout property of the dijit control, exposed through the extension library control properties in XPages Designer.

Introduction

The Extension Library provides us with many easy to use Dojo “containers” including the TabContainer and TabPanel. What is sometimes difficult to decifer though is how to configure the dojo container to bend to your will and meet your requirements.

The Problem

My problem was that I have a container with multiple tabs and some are really large and some are small. The out of box the box functionality provides the ability to size the TabContainer and it forces that size on all the panels. This leaves me with two options (see the demo):

  • Make all the boxes large enough to cover the largest panel
    • which looks daft on the panels with very little content
  • Make the overflow of the larger panels use a scroll bar
    • style=”overflow-y: auto”
    • This is “ok” but with a lot of scrolling it makes the panel difficult to use

The solution

So I was searching around Google and I could not find an answer to “dojo TabContainer dynamic resize” which was very frustrating – so I came upon the dojo documentation for the TabContainer. http://dojotoolkit.org/reference-guide/1.7/dijit/layout/TabContainer.html And experience has told me I am wasting my time with the Dojo documentation but I figured what the hey and clicked in……

Now I would rather stab my eyes out than read the dojo API documentation – but I have to hold my hand up and give them props for this one – it is right there in the demonstrations on the first page “A TabContainer with flexible height” – Brilliant!

The trick is to add doLayout=”false” to your dojo container – simple – and a working demonstration as well – well done sirs!!!

So then I went to the XPages designer to look and see if the property was there on the custom control – and yes it was – brilliant – and how absolutely 100% USELESS the help was for it……How is this helpful???

XPages designer help for the doLayout property of the TabContainer control
XPages designer help for the doLayout property of the TabContainer control

It would have been much more helpful to say “default=true – forces all the Tab Panels to be the same height as the TabContainer. Set to false to allow for flexible tab Panel height!”

The dojo API documentation says “If true, change the size of my currently displayed child to match my size” – written by closet programmers and not by real people, apparently.

Demo

Here is the simple demonstration of the difference – quite striking really

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

Conclusion

Dojo reference guide actually helped………and I need to use it more often apparently.

(I still refuse to waste my time on the API documentation though)

The Moral

I think for me it is imperative to understand more about how the dojo containers work in their original environment – the CSJS library before attempting to use them in XPages. I realize this is contrary to the idea that we don’t “have” to understand them to use them in XPages – but actually – I now think it is essential

For more information check out the Dijit information – reference guide – real people speak!

http://dojotoolkit.org/reference-guide/1.7/dijit/info.html#dijit-info

HTML5 drag and drop demonstration in an XPage

I saw an article on HTML5 drag and drop and wondered how hard it would be in XPages.

Demonstration

Here is a link to the working demo of HTML5 drag and drop in an XPage – this is FF and Chrome only as drag/drop is not supported until IE10

How does it work?

I created an XPage and added a simple data table

Data Table
XPages Data Table

Going off the text in the article I tried to add the events to the Fruit label column – unfortunately you cannot add custom attributes to an xp:text

Cannot add custom attributes to an <xp:text
Cannot add custom attributes to an <xp:text

So once again – back to using dojo selectors to get what we want.

The HTML generated by the XPage creates all the fruit labels (Oranges, Bananas, Apples) with an id=blah:_1blah ending in “someFruit”

<tr><td class="xspColumn"><span id="view:_id1:_id2:_id31:viewPanel1:0:column3:someFruit" class="xspTextComputedField">Oranges</span></td>
<td class="xspColumn"><span id="view:_id1:_id2:_id31:viewPanel1:0:column1:computedField2" class="xspTextComputedField">2.0</span></td>
<td class="xspColumn"><span id="view:_id1:_id2:_id31:viewPanel1:0:column2:computedField3" class="xspTextComputedField">4</span></td>
</tr>
<tr><td class="xspColumn"><span id="view:_id1:_id2:_id31:viewPanel1:1:column3:someFruit" class="xspTextComputedField">Bananas</span></td>
<td class="xspColumn"><span id="view:_id1:_id2:_id31:viewPanel1:1:column1:computedField2" class="xspTextComputedField">1.0</span></td>
<td class="xspColumn"><span id="view:_id1:_id2:_id31:viewPanel1:1:column2:computedField3" class="xspTextComputedField">2</span></td>
</tr>

We can get a handle on these labels using dojo.query(“[id$=someFruit]”) which will get all elements with an id attribute ending in “someFruit”

Once we have all of those elements we can add a draggable and ondragstart attribute to them easily using dojo.attr

			dojo.query("[id$=someFruit]").forEach( function(node){
				 dojo.attr(node, "draggable", "true");
				 dojo.attr(node, "ondragstart", "dragIt(event);");
			});

Update – Thanks to Tim Tripcony for pointing this out
If you are using 8.5.3 you can add the custom attributes to the field in the designer client. This removes the need to use the dojo.query

<xp:text escape="true" id="someFruit" value="#{colname1.Fruit}" style="color:rgb(0,0,255)">
	<xp:this.attrs>
		<xp:attr name="ondragstart" value="dragIt(event);"></xp:attr>
		<xp:attr name="draggable" value="true"></xp:attr>
	</xp:this.attrs>
</xp:text>

Then we just need somewhere to drop them…
I added the same DIV elements from the example to the XPage

				<xp:td>
					<div id="place1" ondrop="dropIt(event);"
						ondragover="event.preventDefault();">
					</div>
				</xp:td>
				<xp:td>
					<div id="place2" ondrop="dropIt(event, true);"
						ondragover="event.preventDefault();">
					</div>
				</xp:td>

and added the javascript functions to a scriptblock at the bottom of the page

	//function called when drag starts
		function dragIt(theEvent) {
			//tell the browser what to drag
			theEvent.dataTransfer.setData("Text", theEvent.target.id);
		}

		//function called when element drops
		function dropIt(theEvent, keepMe) {
			//get a reference to the element being dragged
			var theData = theEvent.dataTransfer.getData("Text");
			//get the element
			var theDraggedElement = document.getElementById(theData);

			//add it to the drop element
			if (keepMe){
				//Add a clone of the element to the field - rather than move it
				var newObj=dojo.clone(theDraggedElement)
				theEvent.target.appendChild(newObj);
			} else {
				theEvent.target.appendChild(theDraggedElement);
			}
			//Add a new line for visual line up
			var theBR=document.createElement("br")
			theEvent.target.appendChild(theBR);
			//instruct the browser to allow the drop
			theEvent.preventDefault();
		}

I changed the example slightly to add a clone capability rather than just drag and drop

Drag and Drop example 1
Drag and Drop example 1 (Move)

Dragging to the first box “moves” the label

Drag and Drop example 2 (Clone)
Drag and Drop example 2 (Clone)

Dragging the label to the second box create a copy of it using dojo.clone(node)

Conclusion

This is merely a prototype but demonstrates nicely the new and exiting capabilities of HTML5 without having the need for a dojo or jQuery library to do the drag/drop for you

Demonstration (again)

Here is a link to the working demo of HTML5 drag and drop in an XPage

Don’t believe the HTML – timing is everything in XPages CSJS

So having made v2.0 of my TypeAhead control I am rapidly moving towards the next iteration making it easier to implement and not have to add functions to the fields in the designer. This becomes especially arduous if you have multiple TypeAheads in the same form and you want to add this capability to all of them.

The plan is to use a jQuery or dojo selector to get a handle on all the TypeAhead fields on the form and add the necessary function calls to each of them in one simple script library. (Because this is currently a capability without jQuery I am sticking with dojo, more verbose as it is)

So I created multiple TypeAhead fields (Copy/Paste) on the same XPage and looked at HTML to find out the selector I would need to create. I right click on the web page and view source and the out of the box HTML looks like this (pretty simple right..?):

<span
	id="view:_id1:_id2:_id30:_id43"
	dojoType="ibm.xsp.widget.layout.data.TypeAheadReadStore"
	jsId="view__id1__id2__id30__id43" mode="full">
</span>
<input type="text"
	id="view:_id1:_id2:_id30:inputText1"
	name="view:_id1:_id2:_id30:inputText1"
	class="xspInputFieldEditBox"
	dojoType="ibm.xsp.widget.layout.TypeAhead"
	store="view__id1__id2__id30__id43">
<br>
<span
	id="view:_id1:_id2:_id30:_id48"
	dojoType="ibm.xsp.widget.layout.data.TypeAheadReadStore"
	jsId="view__id1__id2__id30__id48" mode="full">
</span>
<input type="text"
	id="view:_id1:_id2:_id30:inputText2"
	name="view:_id1:_id2:_id30:inputText2"
	class="xspInputFieldEditBox"
	dojoType="ibm.xsp.widget.layout.TypeAhead"
	store="view__id1__id2__id30__id48">

So foolishly I thought – we need a simple attribute selector which will select attribute dojoType is like TypeAhead.

(I always start with changing the CSS as an easy indicator that I have a handle on the element)

	dojo.query('input[dojoType$=TypeAhead]').forEach(function(node){
   		dojo.style(node, {"border" : "2px solid red"})
 	 });

The Problem

I get a big fat nothing..

The most boring picture in blog history
Probably the most boring picture in blog history

So then I pulled up firebug to make sure I wasn’t being stupid, and sure enough I was…..it is not like I have already written about this before or anything….In firebug the actual DOM looks like this:

Right Click on the field and view in firebug
Right Click on the field and Inspect Element with firebug

 

Looking at the DOM within Firebug reveals Marky's stupidity
Looking at the DOM within Firebug reveals Marky's stupidity

Oh DUH – it is a dojo widget (remember) and once the page is loaded, dojo does some major DOM manipulation to create the functionality. This is actually a really nice demonstration of how XPages and dojo work in tandem. XPages creates some fairly innocuous HTML and dojo tranforms it into considerably more complex and voluminous HTML.

So looking at the DOM we have a problem because the final field which is created (that I want to get a handle on) does not have anything indicating that it is a TypeAhead and how can I “select” this out without adding client side events in the XPage….

The Answer

The trick is to execute or “selector” before the dojo widget takes over and messes up the DOM. Instead of adding the code to the onClientLoad event (which is also when the dojo widget magic is triggered) we need to add it to a script block inside of the HTML – before the page has finished loading.

This is a subtle yet important timing detail during the rendering of HTML pages. You can add a script tag to an HTML page and have it execute before the page is rendered to the user, but it can only take action on what has already been “loaded”…this is also why dojo and jQuery are usually triggered when the HTML document is finally loaded – to make sure all the necessary fields are “loaded” on the page.

In this case we are going to add the script block right at the end of the XPage to make sure that everything we are looking for is “loaded” before we act on it. The script block is added at the bottom of the XPage after all the fields. This will execute before the onClientLoad event.

Adding an in-line script block at the end of the XPage

NOTE

I am being very specific in the selector to only select INPUT (input) with Attribute dojoType ([dojoType$]) which ends in TypeAhead – see here for more information on dojo selectors

And looking at the Sourcecode you can see the script is the last thing on the page before closing </the xp:view>.

	<xp:scriptBlock id="scriptBlock1">
		<xp:this.value>
		<![CDATA[
			dojo.query('input[dojoType$=TypeAhead]').forEach(function(node, index, arr){
   				dojo.style(node, {"border" : "2px solid red"})
 			 });
		]]>
		</xp:this.value>
	</xp:scriptBlock>
	</xp:view>

This time we get a much more satisfactory result:

Selecting the elements before they are manipulated by dojo
Selecting the elements before they are manipulated by dojo

 

But wait – there’s more to this that meets the eye….

What is also interesting to note is that the styling applied to the INPUT field before the dojo widget has it’s way with the HTML is retained….but it is NOT applied to the INPUT anymore..not even close…..

Don't trust the HTML

 

Because of this, I am not sure at this point if I am going to be able to add the timing handlers for the TypeAhead – it will be fun finding out though, watch this space….

 

When should you use jQuery in your XPage?

For full disclosure, I used jQuery with Domino long before v8.5 at a time when the dojo documentation sucked big ones and it was nearly impossible to figure anything out. Today though that is much improved and the dojotoolkit website and the work done by sitepen have done a lot to catch up.

I am more familiar with jQuery syntax and the core library capabilities and I think I will always prefer to use jQuery than dojo given a choice and even playing field. In traditional Domino it is a level playing field as neither library is loaded by default. But that all changed with XPages and I am forcing myself to have to learn dojo because it just doesn’t make sense to use jQuery “just cos dojo sucks”.

jQuery does not conflict with dojo and can be used very successfully in tandem with each other. But there are some things to consider before you do.

jQuery selectors are IMHO easier to read and more intuitive to understand. jQuery uses a combination XPath/CSS query selection process and dojo uses a CSS3 selector paradigm

So why shouldn’t I use jQuery?

If you are going to add jQuery there has to be a good reason/purpose other than “‘cos I like jQuery and dojo sucks”. If you add a jQuery library you are:

  • adding additional overhead to the database
  • adding potential maintenance issues down the road
  • adding to the download size of every XPage which already has the dojo overhead by default.
    • (Yes you can turn dojo off but why would you? If you not going to use the XPage functionality don’t use an XPage design element……)

So if you are just doing a basic selector to get a page element or a basic animation, there is really nothing dojo can’t do any worse than jQuery. If you want to see a simple comparison between dojo selectors and jQuery selectors check this out

Dojo & jQuery side by side. Part 1: DOM Basics

Dojo & jQuery side by side. Part 2: Animation

So when should I use jQuery?

When you can’t do something you need to in dojo, or just just does not make sense to re-invent the wheel. jQuery is now more popular than Flash in terms of web usage for the most popular sites. dojo isn’t even on the map when it comes to web usage tracking. In terms of community size and usage jQuery dwarfs dojo everything else and because of that there is a lot of libraries to chose from.

In an XPages environment, the real benefit is not so much using jQuery itself, but in taking advantage of the thousands of plugins available which other people have written. There are plugins for everything from:

  • Menus
  • Calendars
  • Embedded video players
  • Photo galleries
  • Page Design libraries
  • Effect libraries
  • MVC Framework design libraries

I think I am going to start a series of weekly jQuery plugin examples (in XPages) to show what can be achieved very easily with the minimum of code. Let’s see how far it takes me before I run out of the over 4000 jQuery plugins and jQuery mobile

TypeAhead.js – making it readable and “why dojo?” I get it!

LOVE THIS

I wanted to break into the TypeAhead.js library which XPages uses to make the type ahead functionality work

looks like this….uuuuurgh

TypeAhead.js minified
TypeAhead.js minified

Quick search online and I found http://jsbeautifier.org/ and when I took the code and pasted it in look what happened – LOVE IT

readable TypeAhead.js
readable TypeAhead.js

And as soon as I look through this I get why IBM went with dojo. It is not about the pretty interface (which jQuery wins hands down) it is about the programming framework.

The provide, declare, hitch capabilities within dojo allow for class object creation with locally scoped variables and many other cool things. jQuery does not provide a framework for this, it doesn’t try to either. You can always create class objects manually and that is what people write around their jQuery. Dojo provides the class framework directly and the TypeAhead.js is a fantastic piece of work. One of these days when I have nothing better to do I will break down how it works.

x$ update for dojo

Problem
dojo selector –  “dojo.query” doesn’t work for elements generated by xpages

Background

This is a follow on to the jQuery selector function for xPages – x$(“#{id:inputText1}”) article I wrote previously.

Solution

Turns out that dojo.query suffers from the same problem:  dojo.query cannot understand elements with colons (:) in it. We have to convert the idTag of the element to escape the colons with a backslash.

  • dojo.query(“view:_id1:inputText1”).style(“border”, “1px solid red”) – will not work
  • dojo.query(“view\:_id1\:inputText1”).style(“border”, “1px solid red”) – will work and is converted using the function

So I have updated my x$ function to cover dojo and jQuery


function x$(idTag, param, jd){ //Updated 28 Feb 2012
      idTag=idTag.replace(/:/gi, "\\:")+(param ? param : "");
      return( jd=="d" ? "#"+idTag : $("#"+idTag));
}

the param and jd parameters are optional (in fact jd is only necessary if you are using dojo)

Interesting Note

For the uninitiated you will note that jQuery returns a $() rather than a string – this is because it is actually returning a jQuery Object. That is a fundamental difference in the way that dojo and jQuery function as code libraries.

Examples

here is an example code of how it would be used in dojo or jQuery


//dojo
dojo.query(x$("#{id:inputText1}", "", "d")).style("border", "1px solid red")

//jQuery equivalent

x$("#{id:inputText1}").css("background-image", "1px solid red")

An example is shown below

<?xml version="1.0" encoding="UTF-8"?>

<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

<xp:br></xp:br>
 <xp:scriptBlock id="scriptBlock1">
 <xp:this.value><![CDATA[
 function x$(idTag, param, jd){ //Updated 28 Feb 2012
 idTag=idTag.replace(/:/gi, "\\:")+(param ? param : "");
 return( jd=="d" ? "#"+idTag : $("#"+idTag));
 }
 ]]>
 </xp:this.value>
 </xp:scriptBlock>
 <xp:inputText id="inputText1"></xp:inputText>
 <xp:br></xp:br>
 <xp:br></xp:br>
 <xp:button value="Change Border (Fail)" id="button2">
 <xp:eventHandler event="onclick" submit="false">
 <xp:this.script><![CDATA[dojo.query("#{id:inputText1}").style("border", "1px solid red")]]></xp:this.script>
 </xp:eventHandler>
 </xp:button>
 <xp:br></xp:br>
 <xp:br></xp:br>
 <xp:button value="Change Border X$" id="button1">
 <xp:eventHandler event="onclick" submit="false">
 <xp:this.script><![CDATA[dojo.query(x$("#{id:inputText1}", "", "d")).style("border", "1px solid red")]]></xp:this.script>
 </xp:eventHandler>
 </xp:button>
</xp:view>

Adding a “working” visual indicator to the XPages TypeAhead

I arrived at this in as much a lesson in learning how the dojo and dijit interface works in the browser. This is a neat little demonstration but I am not sure that it is rock solid in terms of usage. Because it is manipulating the DOM on the front end, it always runs the risk of breaking something in a future release of dojo or XPages. But anyway here we go.

Problem

No user feedback that a type ahead field is looking up data

Background

The normal XPages type ahead functionality looks like this

Normal functionality of an XPages type ahead
Normal functionality of an XPages type ahead

Unfortunately there is no visual feedback to the user, they have no idea if it is a type ahead and they have no idea if anything is happening……This is especially irritating when the network is slow

Breaking down the the HTML

So we are going to add a visual indicator to the field – here’s how we break it down:

The “field” that we see is actually a mixture of divs and fields created by dojo and the Xpages server – breaking it down here with FireBug we can see that the field is made up of 8 (EIGHT!) DIVs and the actual input field. I have highlighted the sections we are interested in.

Breaking down the type ahead field
Breaking down the type ahead field

You may have seen the right hand DIV being used in form validation – a warning icon is displayed at the end of the field – pretty slick modification of the interface really.

You will also see that there is a whole world of style and class pain and suffering going on to make this happen. We really want to stay clear of altering anything more than we have to and that is why we are going to focus in on the right hand DIV in the above picture

Selecting the object to manipulate

The section we are interesting in manipulating is the smaller inside DIV shown above

<div class="dijitReset dijitValidationIcon" style="visibility: hidden;">
<br>
</div>

and this is an element within the <div id=”widget_view:_id1:travelLocator1″ (Highlighted in the picture above)

Using dojo we can “select” this class using the following code

dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d"))

This generates the following code – selecting all elements with the class .dijitValidationIcon, within the widget_view:_id1:travelLocator1 element

dojo.query("#widget_view:_id1:travelLocator1 .dijitValidationIcon")

oh yeah I had to make an update to x$ as well – turns out that dojo can’t query items with colons (:) in it any better than jQuery can. All the code is at the end of this article and I will post another entry showing the update in more detail. Suffice as to say at this point that I have to change widget_view:_id1:travelLocator1 to widget_view\:_id1\:travelLocator1 for the selector to work correctly.

Manipulating the object 

We can now create the following functions to add/remove the visual effect to the field
I added the jQuery equivalent(s) for two reasons, 1) that’s how I figured it out in the first place and 2) it highlights how much easier IMHO selectors are in jQuery).

The passed in parameter idTag will come from the CSJS we are going to add to the events in the designer client. This function selects the DIV we wish to manipulate, changes the background image then finally makes it visible (by default it is hidden)

function addVisual(idTag){

    var newImage="url('/dojo130/dijit/themes/tundra/images/treeExpand_loading.gif')"
    dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", newImage)
    dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "visible")

    //jQuery equivalent
    //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", newImage)
    //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "visible")</pre>
}

The equivalent code to remove the image (when we are finished) is as follows (removing the image and hiding the DIV)

function removeVisual(idTag){

    dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", "")
    dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "hidden")

    //jQuery equivalent
    //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", "")
    //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "hidden")

}

Adding the events to the XPage

The type ahead is triggered on the onkeypress event of the input field and the typeahead results are removed either on the onblur event clicking away from the field) or the onchange event (clicking one of the results).

Adding the image

In the onkeypress event we add the following to our XPage.

    addVisual("#{id:travelLocator1}");
Adding addVisual to the onblur and onchange events
Adding addVisual to the onblur and onchange events

Removing the image 

In the onblur and onchange events we add the following to our XPage

    removeVisual("#{id:travelLocator1}");
Adding removeVisual to the onblur and onchange events
Adding removeVisual to the onblur and onchange events

Final result

This now changes our type ahead (tested in IE8, Firefox 7 and Chrome 17) to look like this. The visual indicator is added to the type ahead 🙂

Type Ahead with Visual Indicator
Type Ahead with Visual Indicator

No live demo 😦

And this is the only kind of occasion where using WordPress blows –  I do not have anywhere to do a live demo of this. Any volunteers to help me host examples like this would be GREATLY appreciated !!!

The code is listed below  – you will have to change the following to work in your database

  • your lookup view (avoiding 64K issues)
  • the location of your dojo images

Known issues

  • There is no indicator that NO results have been returned (that’s for another day)
The code
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xp_1="http://www.ibm.com/xsp/coreex"
 xmlns:jQuery="http://www.jquery.com/xsp/coreex">

<xp:br></xp:br>
 <xp:table>
 <xp:tr>
 <xp:td style="width: 200px">
 Travel Locator
 </xp:td>
 <xp:td>
 <xp:inputText id="travelLocator1" styleClass="title">
 <xp:typeAhead mode="full" minChars="1" ignoreCase="true"
 htmlFilter="identity" preventFiltering="false">
 <xp:this.valueList>
 <![CDATA[#{javascript:@DbColumn(@DbName(), "vwTravel", 1)}]]>
 </xp:this.valueList>
 </xp:typeAhead>

<xp:eventHandler event="onblue" submit="true"
 refreshMode="norefresh" id="eventHandler2">
 <xp:this.script>
 <xp:executeClientScript>
 <xp:this.script>
 <![CDATA[removeVisual("#{id:travelLocator1}");]]>
 </xp:this.script>
 </xp:executeClientScript>
 </xp:this.script>
 </xp:eventHandler>

<xp:eventHandler event="onchange" submit="true"
 refreshMode="norefresh" id="eventHandler1">
 <xp:this.script>
 <xp:executeClientScript>
 <xp:this.script>
 <![CDATA[removeVisual("#{id:travelLocator1}");]]>
 </xp:this.script>
 </xp:executeClientScript>
 </xp:this.script>
 </xp:eventHandler>

<xp:eventHandler event="onkeypress" submit="false">
 <xp:this.script>
 <![CDATA[addVisual("#{id:travelLocator1}")]]>
 </xp:this.script>
 </xp:eventHandler>
 <xp:eventHandler event="onblur" submit="false">
 <xp:this.script>
 <![CDATA[removeVisual("#{id:travelLocator1}");]]>
 </xp:this.script>
 </xp:eventHandler>
 </xp:inputText>
 </xp:td>
 </xp:tr>
 </xp:table>
 <xp:br></xp:br>
 <xp:scriptBlock id="scriptBlock1">

<xp:this.value><![CDATA[
 function x$(idTag, param, jd){ //Updated 28 Feb 2012

 idTag=idTag.replace(/:/gi, "\\:")+(param ? param : "");
 return( jd=="d" ? "#"+idTag : $("#"+idTag));
 }
function addVisual(idTag){

var newImage="url('/dojo130/dijit/themes/tundra/images/treeExpand_loading.gif')" //Change me for your server
 dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", newImage)
 dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "visible")

 //jQuery equivalent
 //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", newImage)
 //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "visible")
}

function removeVisual(idTag){

dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("backgroundImage", "")
 dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d")).style("visibility", "hidden")

//jQuery equivalent
 //x$("widget_"+idTag, " .dijitValidationIcon").css("background-image", "")
 //x$("widget_"+idTag, " .dijitValidationIcon").css("visibility", "hidden")
}

]]></xp:this.value>
 </xp:scriptBlock>
</xp:view>

Update – 5 March 2012
Like I said at the start of the article I did not know how robust this was and low and behold, the code above works in 8.5.2 but not 8.5.3.

Here are the updated addVisual() and removeVisual() which work in 8.5.3 and 8.5.2

function addVisual(idTag){ //8.5.3 version
var newImage="url('/dojo130/dijit/themes/tundra/images/treeExpand_loading.gif')" //Change me for your server
     var inputField=dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d"))
     inputField.style("backgroundImage", newImage)
     inputField.style("visibility", "visible")
     dojo.query(x$("widget_"+idTag, " .dijitValidationContainer", "d")).style("display", "block")
     inputField.attr("value","")

}

function removeVisual(idTag){
     var inputField=dojo.query(x$("widget_"+idTag, " .dijitValidationIcon", "d"))
     inputField.style("backgroundImage", "")
     inputField.style("visibility", "hidden")
     dojo.query(x$("widget_"+idTag, " .dijitValidationContainer", "d")).style("display", "none")

}

Update 22 March 2012

Through the help of Sven Hasselbach I have been able to add a fail icon – here is the article