JQuery is used extensively in OX App Suite. In this tutorial we will look at how to select elements how to listen to events and how to create and manipulate DOM structures.

Who should read this document?

When developing the OX App Suite frontend we chose to forego more complex widget based libraries in favor of direct DOM manipulations and in favor of plain old HTML and CSS. This gives us a lot of flexibility and since, philosophically, OX App Suite aims to be a citizen of the web, and not simulate desktop apps, it seems wise to stay close to web technologies. This means, to create the visuals of any piece of the OX App Suite frontend, you need to manipulate the DOM structure. You'll need a good understanding of HTML and CSS as well as a good understanding of JQuery, since we use this pretty much everywhere, and so will you. Also, knowing this has never hurt anyones resumé. If you've already worked with JQuery you can probably skip this tutorial, everyone else, please stay around for a whirlwind tour of JQuery DOM manipulation, element navigation and event handling.

Creating a structure

To create a dom node in JQuery simply call the $ function with the html text that describes your node:

var theDiv = $('<div>');
var theOtherDiv = $('<div>Hello, I am a new div!</div>'); 

In order for the node to show up on screen it will have to be appended somewhere:

// var parentNode = ...
$('<div>Hello, I am a new div!</div>').appendTo(parentNode);

Attention Make sure to never leave off the closing tag or the > at the end of the html snippet. Some browsers will just silently ignore the element you are trying to create and you will ask yourself why your element doesn't show up anywhere. Some browsers will at least blurp out an error message into the console.

 $('<div class="someClass"').text("Hello").appendTo(parentNode);

Though this may look a bit unusual on first glance, sometimes the node that you should draw stuff into will be passed to you as the this variable, so, reading through our codebase you will often find something like this:

$('<div>Hello, I am a new div!</div>').appendTo(this);

To build more complex structures, liberally use the #append method:

 this.append(
	$("<div>").append(
		$('<span class="label label-info">').text("A Label"),
		$('<span>').text(" The value")
	) // End of div
);

You can indeed build very complex nested structures this way. Just make sure, that at least you still understand what is going on, if you write a block like this:

this.append(
	$("<h4>").text("Behold a more complex structure!"),
	$('<div class="row">').append(
		$('<span class="span3">').append(
			$("<strong>").text("Lo and behold")
		), // end of span
		$('<span class="span9">').text("This is twitter bootstraps grid system in action.")
	), // end of row
	$('<div class="row">').append(
		$('<span class="span12">').text("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. ")
	) // end of row
).addClass("container");// end of this
Hint Right-click on the result of these experiments and choose Inspect element (or similar) to look at the resulting DOM structure.
Hint Modify the code shown above and rerun the examples to experiment yourself!

Some recommendations

Always use ampersands instead of quotes for your snippet string
This way you can directly specify attributes for the element. $('<div class="myClass">') is much more readable than $("<div class=\"someClass\">")
Specify the classes immediately in the html snippet
Since specifying classes is such a common operation – especially considering twitter bootstrap likes to work with classes a lot – don't bother with #addClass or the #attr method. Just specify them directly:
$('<div class="myClass">')
Avoid inline styles
While jQuery allows developers to specify css style information with the #css method, in the interest of branding it is usually better to stow them away in a proper .css file. How css files are loaded is discussed in the RequireJS tutorial.
Use the #text method for content
Instead of specifying text for elements inline, or, worse, concatenating a value you've been passed with strings to obtain a snippet ( $('<div>' + myValue + '</div>')), use the #text method. This won't leave you open to cross-site-scripting attacks.
$("<div>").text(myValue);

Further reading

JQuery Manipulation API
We've only glanced at one method used for manipulation of the DOM #append. There are lot's more. Reading through the whole of the JQuery Manipultation API is certainly time well spent.
Twitter Bootstrap
Twitter bootstrap is the lightweight frontend framework used in OX App Suite. It provides beautiful and useful css and html based utilities for building good looking apps. Read through the documentation at least once so you won't need to invent something we already have available via this nice framework.

Event handling

Being able to draw form controls (like buttons and input fields) is only half the fun. Doing something when a button is clicked or text is changed is the other half. To register an event handler, use jQuerys #on method:

this.append(
	$('<button class="btn btn-primary">').text('Open alert').on("click", function () {
		alert("Button was clicked!");
	})
);

You can also reuse the event handler. This might come in handy when you want to react to events on very many elements:

function showAlert() {
	alert("Button was clicked!");
}
$('<button class="btn">').text('Button 1').on("click", showAlert).appendTo(this);
$("<br>").appendTo(this);
$("<br>").appendTo(this);
$('<button class="btn">').text('Button 2').on("click", showAlert).appendTo(this);

You may also customize what an event handler does by passing an object to the #on call:

function showAlert(e) {
	alert("Button was clicked: " + e.data.message);
}

$('<button class="btn">').text('Button 1').on("click", {message: 'Hello'}, showAlert).appendTo(this);
$("<br>").appendTo(this);
$("<br>").appendTo(this);
$('<button class="btn">').text('Button 2').on("click", {message: 'Namaste'}, showAlert).appendTo(this);

Sometimes it is also useful to suppress the action the browser would normally take on a certain event. Typically this is used to prevent the browser from following links, when you do something else once the user clicks:

$('<a href="#">').text("A link").on('click', function (e) {
	e.preventDefault(); // Do not follow the link
	alert("What trickery is this? The link was clicked, but not followed!");
}).appendTo(this);

Further Reading

JQuery Event API
The complete list of events and event related methods in the jQuery documentation certainly warrants some quality time spent on it.
The on() method
Find out everything else the #on method can do (and that's a lot). Wield events with confidence.

Selecting elements

Traversing the DOM structure and finding nodes to work on is traditionally one of the most cumbersome tasks in JavaScript. jQuery, on the other hand, makes this almost ridiculously convenient and easy. It accomplishes this by allowing you to run selectors on either the entire DOM or a subtree (Always do the latter!). These selectors work the same way CSS selectors do. Consider the following snippet of CSS:

.highlighted {
	background-color: yellow
}
					
The .highlighted essentially means: "Select all nodes that have a class attribute containing 'highlighted'". These CSS selectors (more specifically CSS3 selectors) work with jQuery as well. Let us look at a few examples, that will all look very familliar if you know your CSS:

You can select elements by their ID:

Frank
Max
Ginnie
Note We're running the selector only on the subtree that is created here. This is good practice, since you can never know what kinds of DOM structures other apps may use internally, and when running your selector globally an overeager selector may also select elements in other parts of the program (we're all working in the same DOM tree).

As in our CSS example, you can also select elements by their class attribute:

Frank
Max
Samantha
Ginnie
Try changing the selector to select the 'others' instead!

Perhaps less usefully – but true to CSS – you can select all elements of a given type:

This is a Text with some elements that are marked
					

More usefully you can (as per CSS3) select elements in which a certain attribute has a certain value:

Frank
Max
Samantha
Ginnie
Try selecting all the ladies!

Or a value begins with a certain string

Frank
Max
Samantha
Ginnie

Or contains a certain string

Frank
Max
Samantha
Ginnie

Or contains a space delimited string. All of these are especially powerful when combined (like here) with data attributes:

Frank
Max
Samantha
Ginnie
Select the cognac drinkers!

jQuery also adds a few pseudo-classes that operate on previous sets: Like selecting the first div

Frank
Max
Samantha
Ginnie

... or the last div

Frank
Max
Samantha
Ginnie

or odd divs

Frank
Max
Samantha
Ginnie

or even divs

Frank
Max
Samantha
Ginnie

or the second div (Note: we're counting 0 based)

Frank
Max
Samantha
Ginnie

or every div starting with the second

Frank
Max
Samantha
Ginnie

you can even combine selectors. For example like this (the second gin drinker)

Frank
Max
Samantha
Ginnie

or like this

Frank
Max
Samantha
Ginnie

or like this

Frank
Max
Samantha
Ginnie

here: have some room to run you own experiments or play around with the ones above

				

The are even more options than this. I suggest you read up on them in the jQuery documentation

Operating on selections

Once you've selected a bunch of DOM elements you probably want to do something with them. The nice thing about jQuery is, that whenever you call a method on the entire set, the method in turn will be called of every element of the stack. Let's select three elements and then change their content with jQueries #append method. To tie this in with the event example, we'll do this only after a button was clicked:

// First let's construct a few elements
this.append(
	$('<div data-drinks="vodka gin cognac">').text("Frank"),
	$('<div data-drinks="whiskey vodka">').text("Max"),
	$('<div data-drinks="absinthe gin">').text("Samantha"),
	$('<div data-drinks="cognac wine">').text("Ginnie"),
	$('<button>').text("Mark gin drinkers")
);
// Attention! 'this' is reassigned in callback methods to something completely different
// So we keep a reference lying around
var self = this;
this.find('button').on("click", function () {
	// Find all gin drinkers and a append ' likes gin!'
	// Since two nodes will be found, the append method is called on both nodes
	// No need for fancy loops!
	self.find("[data-drinks~=gin]").append(" likes gin!");
	self.find("div:not([data-drinks~=gin])").append(" does not like gin!");

});

This is also a good place to talk about method chaining. In jQuery (and other libraries) you can usually string together a sequence of method calls. So instead of writing:

self.find("[data-drinks~=gin]").append(" likes gin");
self.find("[data-drinks~=gin]").addClass("text-success");
self.find("[data-drinks~=gin]").css({fontWeight: "bold"});
you can simlpy chain the three method calls:
self.find("[data-drinks~=gin]").append(" likes gin").addClass("text-information").css({fontWeight: "bold"});
or, for readability:
self.find("[data-drinks~=gin]")
	.append(" likes gin")
	.addClass("text-success")
	.css({fontWeight: "bold"});
Let's use this in our example:
// First let's construct a few elements
this.append(
	$('<div data-drinks="vodka gin cognac">').text("Frank"),
	$('<div data-drinks="whiskey vodka">').text("Max"),
	$('<div data-drinks="absinthe gin">').text("Samantha"),
	$('<div data-drinks="cognac wine">').text("Ginnie"),
	$('<button>').text("Mark gin drinkers")
);
// Attention! 'this' is reassigned in callback methods to something completely different
// So we keep a reference lying around
var self = this;
this.find('button').on("click", function () {
	// Find all gin drinkers and a append ' likes gin!'
	// Since two nodes will be found, the append method is called on both nodes
	// No need for fancy loops!
	self.find("[data-drinks~=gin]")
		.append(" likes gin!")
		.addClass("text-success")
		.css({fontWeight: "bold"});

	self.find("div:not([data-drinks~=gin])")
		.append(" does not like gin!")
		.addClass("text-error");

});

Remembering nodes when assembling the dom structure

Alternatively to assembling the DOM structure in total and then using jQuery to look up nodes it might be useful to save a reference to a node so you can then directly call methods on it. As a rule of thumb, if you want to do similar things to many elements use a selector, if you want to do specific things to a certain element, try to hold onto a reference. In our above example, the button is a good candidate for keeping a reference to:

// First let's construct a few elements
var button;
this.append(
	$('<div data-drinks="vodka gin cognac">').text("Frank"),
	$('<div data-drinks="whiskey vodka">').text("Max"),
	$('<div data-drinks="absinthe gin">').text("Samantha"),
	$('<div data-drinks="cognac wine">').text("Ginnie"),
	button = $('<button>').text("Mark gin drinkers")
);
// Attention! 'this' is reassigned in callback methods to something completely different
// So we keep a reference lying around
var self = this;
button.on("click", function () {
	// Find all gin drinkers and a append ' likes gin!'
	// Since two nodes will be found, the append method is called on both nodes
	// No need for fancy loops!
	self.find("[data-drinks~=gin]")
		.append(" likes gin!")
		.addClass("text-success")
		.css({fontWeight: "bold"});

	self.find("div:not([data-drinks~=gin])")
		.append(" does not like gin!")
		.addClass("text-error");

});

Similarly, let's add a sentence that shows the number of gin drinkers. Again, since this is a special treatment of a special node, we opt for keeping a reference:

// First let's construct a few elements
var button, numberSpan;
this.append(
	$('<div data-drinks="vodka gin cognac">').text("Frank"),
	$('<div data-drinks="whiskey vodka">').text("Max"),
	$('<div data-drinks="absinthe gin">').text("Samantha"),
	$('<div data-drinks="cognac wine">').text("Ginnie"),
	button = $('<button>').text("Mark gin drinkers"),
	$("<br>"),
	numberSpan = $("<span>").hide()
);
// Attention! 'this' is reassigned in callback methods to something completely different
// So we keep a reference lying around
var self = this;
button.on("click", function () {
	// Find all gin drinkers and a append ' likes gin!'
	// Since two nodes will be found, the append method is called on both nodes
	// No need for fancy loops!
	var number = self.find("[data-drinks~=gin]")
		.append(" likes gin!")
		.addClass("text-success")
		.css({fontWeight: "bold"}).length;
	numberSpan.text(number + ' people like gin!').show();

	self.find("div:not([data-drinks~=gin])")
		.append(" does not like gin!")
		.addClass("text-error");

});

Putting it all together

As a final exercise try to modify the above code so that instead of a button, a pulldown list of drinks is shown that highlights the corresponding people as soon as a value is selected.

// First let's construct a few elements
var button, numberSpan;
this.append(
	$('<div data-drinks="vodka gin cognac">').text("Frank"),
	$('<div data-drinks="whiskey vodka">').text("Max"),
	$('<div data-drinks="absinthe gin">').text("Samantha"),
	$('<div data-drinks="cognac wine">').text("Ginnie"),
	button = $('<button>').text("Mark gin drinkers"),
	$("<br>"),
	numberSpan = $("<span>").hide()
);
// Attention! 'this' is reassigned in callback methods to something completely different
// So we keep a reference lying around
var self = this;
button.on("click", function () {
	// Find all gin drinkers and a append ' likes gin!'
	// Since two nodes will be found, the append method is called on both nodes
	// No need for fancy loops!
	var number = self.find("[data-drinks~=gin]")
		.append(" likes gin!")
		.addClass("text-success")
		.css({fontWeight: "bold"}).length;
	numberSpan.text(number + ' people like gin!').show();

	self.find("div:not([data-drinks~=gin])")
		.append(" does not like gin!")
		.addClass("text-error");

});

This concludes our whirlwind tour of jQuery. As you can see, jQuery is a powerful library for writing very concise and browser-portable UI code. A minute spent learning to wield this library is a minute well spent, so, at the risk of sounding like a broken record, I urge you to thoroughly read through jQueries documentation.