Sharing, Collaborating and Learning.

jQuery in XPages #2 – Labelify.js – How does it work?

This article is in support of the jQuery in XPages article #2 – Labelify.js and goes into depth as to how the functionality was created and how you can modify it yourself. How does this work?The Labelify example is made up of the following elements:

The xLabelify.js library is based on the original labelify.js created by Stuart Langridge. I have made a couple of modifications to the library to make it work in our XPages environment. Here is the breakdown of how it works……..

Extending jQuery

For more information on extending jQuery check out the (website). But in a nutshell, jQuery was envisioned to be extended so that anyone could use the basic jQuery selector to create a collection of objects in the DOM and then process them however they wanted.

(If we wanted to be more specific and wanted to select only the labels within a certain panel (rather than the whole page) or if we wanted to select individual elements by name/id we could do that too). The example below (using x$) selects just the fruit4 field.

I have annotated the code so that you can understand what makes this effect work. It is also a nice short look inside a jQuery extension. It is really quite elegant in its simplicity.

//extend jQuery with the function name "labelify" and it will receive the array called "settings"
jQuery.fn.labelify = function(settings) {
//"settings" will contain the variable title and labeledClass which will be created as jQuery objects
//by default the text: value will be title (if nothing is passed in at all)
settings = jQuery.extend({
text: "title",
labelledClass: ""
}, settings);
//lookups will be the default values pulled from the document model (DOM) at startup
var lookups = {
//The title: value will be taken from the title attribute of the input field being processed
title: function(input) {
return $(input).attr("title");
},
//The label: value will be taken from the label with a "for" attribute matching the id of the input field being processed
label: function(input) {
return $('label[for="' + input.id + '"]').text();
}
};
var lookup;
//$(this) refers to "this jQuery object" as opposed to "this" which in javascript is the current DOM element.
var jQuery_labellified_elements = $(this);
//for each labelified element ($(this).each)
//we are setting the lookup variable for each of the elements which are within "lookups"
//the value of lookup is going to be the text we are going to display within the field
//that has come from either the LABEL or the title attribute
return $(this).each(function() {
if (typeof settings.text === "string") {
lookup = lookups[settings.text]; // what if not there?
} else {
lookup = settings.text; // what if not a fn?
};
// bail if lookup isn't a function or if it returns undefined
if (typeof lookup !== "function") {
return;
}
var lookupval = lookup(this);
if (!lookupval) {
return;
}
// need to strip newlines because the browser strips them
// if you set textbox.value to a string containing them
$(this).data("label", lookup(this).replace(/\n/g, ''));
//When the user clicks or tabs into the field (focus event)
$(this).focus(function() {
//if the jQuery data (more on this further down) "label" is exactly the same type and value (===)
//Then the jQuery item (this).value equals the jQuery item defaultValue
//then remove the labelledClass
//in simple terms - if the user clicks into the field and the "labellify" text (e.g. enter a value) is displayed
//Then remove it from the field, hide it and put the defaultValue into the field (normally blank)
if (this.value === $(this).data("label")) {
this.value = this.defaultValue;
$(this).removeClass(settings.labelledClass);
}
//When the user clicks or tabs out of the field (blur event)
//IF the current value = the defaultValue (usually blank)
//Then put the labelified value (e.g. enter a name) back into the field
//Add the labelledClass (make it gray)
}).blur(function() {
if (this.value === this.defaultValue) {
this.value = $(this).data("label");
$(this).addClass(settings.labelledClass);
}
});
//This function removes all the labelify information from the field
//This function is called onSumit and means that if the deveoper validates for ""
//the field will contain "" onSubmit and will therefore not be validated as (enter a name)
var removeValuesOnExit = function() {
jQuery_labellified_elements.each(function() {
if (this.value === $(this).data("label")) {
this.value = this.defaultValue;
$(this).removeClass(settings.labelledClass);
}
})
};
//if the form is being submitted or the user has clicked onto another window (unload)
//then call the remove labelify function above and remove all traces of labelify from the field (jQuery $(this))
$(this).parents("form").submit(removeValuesOnExit);
$(window).unload(removeValuesOnExit);
if (this.value !== this.defaultValue) {
// user already started typing; don't overwrite their work!
return;
}
//MDR Mar 2012
//else clause added to resolve existing default values
//The function sets up the labelified field and says if the field is blank to start with insert the labelified information
if (this.defaultValue == "") {
// actually set the value
this.value = $(this).data("label");
$(this).addClass(settings.labelledClass);
} else {
//however in our XPages environment we often have default values in the field
//we might want to see the labelified version if the user blanks out the field
//but without this else clause the labelified value would be forced ontop of the default value
//so if the field is not blank then
//get the field value ("Cabbage" in our example")
var sTemp = this.value
//set the default value to blank (the code assumes Cabbage is the defaultValue)
this.defaultValue = ""
//create the data label within the jQuery object to be the labelify text (Enter a Vegetable)
$(this).data("label", lookup(this).replace(/\n/g, ''));
//put Cabbage back into the field
this.value = sTemp
}
});
};

jQuery object data

For more indepth information on jQuery.data check out the jQuery (site). But one of the constructs within the jQuery object is the .data capability. What jQuery does is it assigns data attributes to the DOM element and it keeps track of them with internal jQuery pointers. What this allows us to do programmatically is push and pull “data” from the jQuery object $(this) without having to define more DOM elements to contains the data. Creating DOM elements is an expensive transaction and pushing/pulling data from existing DOM elements is much more efficient. Although less of a transaction bonus, but still more efficient is that fact that we do not have to create new variables for each value either. This is not exactly what happens but you can imagine it conceptually – e.g. <input type=”text” value=”Marky” /> becomes <input type=”text” value=”Marky” data-label=”Enter a Vegetable” />

NOTE

Some interesting jsPerf tests have shown that in this case in some browsers jQuery is actually slower than setting attributes in the DOM manually. Still both are more efficient than creating variables in memory or new DOM elements via document.createElement.

Where does the label come from then?

When we create our XPage fields by dragging and dropping from the data source, the XPage created the following HTML automagically

The full demo includes more specific examples of labelifying specific fields and or sections and not others. To be able to reference specific panels the demo also includes x$ (link) which is provided as a JavaScript library in the sample database.