Introduction to Complex UIs Using jQuery UI
Ralph Whitbeck | May 4, 2011
Building a web application with jQuery makes the task easier. Similarly, building a web application with a complex UI is made easier with jQuery’s sister project jQuery UI. With jQuery UI we have a collection of interactions, widgets and a theme builder at our fingertips that allows us to make a cohesive UI.
What is jQuery UI?
The jQuery UI project is an official project of the jQuery Project, that provides abstractions for low-level interactions and animations, advanced effects and high-level, themeable widgets, all built on top of the jQuery JavaScript Library, that you can use to build highly interactive web applications.
There are a number of interactions within jQuery UI that allow you to build complex behaviors. The library provides support for dragging, dropping, resizing, selecting, and sorting elements on a page.
It also provides many prebuilt widgets that you can drop onto your UI with the ease of any jQuery plug-in. Widgets are full-featured UI controls each having a full range of options. These widgets, as of jQuery UI (1.8.12), include Accordion, Autocomplete, Button, Datepicker, Dialog, Progressbar, Slider, and Tabs. There are also a number of effects that can be utilized and additionally a low-level position utility method that existing widgets are taking advantage of that you can use for your UI’s.
A ThemeRoller tool is available at https://jqueryui.com/themeroller/. The jQuery UI ThemeRoller allows developers and designers to pick from an existing theme in the gallery or create a custom theme using an existing theme as a base. These themes are built using the jQuery UI CSS Framework. The framework is a set of classes that cover a wide range of user interface needs, and can be manipulated by the ThemeRoller tool. The widgets in jQuery UI are built using the robust CSS framework to ensure shared markup conventions and ease of code integration across all widgets. We can also use the CSS Framework within our own markup to make a seamless UI across your custom jQuery UI widgets.
You may be thinking to yourself, “But my UI doesn’t need all of these interactions/widgets. Can I just install the parts of jQuery UI that my UI actually needs?” The answer to that question is yes; jQuery UI provides a download builder that will package only the interactions and widgets you need. It also handles dependencies that you’ll need for your chosen items so you don’t download a widget without all the dependant files for that widget. The download builder will also bundle custom themes from the ThemeRoller so you can start by building your theme then pick the components of jQuery UI you need and download the zip file.
Let’s Build Something
To show the most amount of features of jQuery UI that come together in a complex UI we’re going to build a To-do List application. The requirements of the to-do list are as follows:
- Ability to login
- Ability to keep track of to-do’s in multiple projects
- Add/Remove Projects
- Add/Complete To-do items.
- Sort To-do items.
- To-do items will have a title, description and due date
- The UI should share a common theme
Note: We are only working with the UI for this application. We are not going to persist data or tie in with a back-end system. So we will be faking some backend data tasks. I also heavily commented the jQuery code so that it’ll be easy to understand.
Login
We’ll start with a login form. Very simply we’ll start out with a username textbox and a password field and a submit button. Building on top of that we’ll use the jQuery UI CSS Framework to enhance our form to make it themed using one of the prebuilt themes that come with jQuery UI. We’ll also use an effect to signify a login error has occurred. Let’s look at the markup (index.html).
HTML
<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="UTF-8" />
<title>To-do List</title>
<!-- Declare our jQuery UI Theme CSS, this came from the download builder -->
<link href="css/ui-lightness/ui-lightness.css" rel="stylesheet" type="text/css" />
<!-- Our styles -->
<link href="css/layout.css" rel="stylesheet" type="text/css" />
</head>
<body id="login">
<div id="content">
<!--
Our login form container, we define this as the widget container
with ui-widget, and make the containers corners rounded
with the ui-corner-all class.
-->
<section class="ui-widget ui-corner-all">
<!--
A header for the widget container, defined by ui-widget-header
and we are expecting only the top corners to be rounded
with the ui-corner-top class.
-->
<header class="ui-widget-header ui-corner-top">
To-do Login</header>
<!--
Define the start of the widget's content with ui-widget-content
Make the bottom of the content container corner rounded
with the ui-corner-bottom class.
-->
<div class="ui-widget-content ui-corner-bottom">
<p>Please use the login form below to login to your to-do list.</p>
<!--
The CSS Framework provides a ui-state-error class to help style
error messages. We'll use this class to style an error message
on an incorrect login attempt
-->
<p class="ui-state-error">Login username/password incorrect.</p>
<!-- Our login form -->
<form>
<div>
<label for="username">Username:</label>
<input id="username" type="text"> </div>
<div>
<label for="password">Password:</label>
<input id="password" type="password"> </div>
<div>
<input id="btnLogin" type="submit" value="Login"> </div>
</form>
</div>
</section>
</div>
<!--
An element that's used to create a overlay effect for the login widget.
The framework class ui-widget-overlay defines the style.
-->
<div class="ui-widget-overlay" style="z-index: 1002;">
</div>
<!-- include jQuery, jQuery UI, and our script file -->
<script src="js/lib/jquery-1.5.2.min.js"></script>
<script src="js/lib/jquery-ui-1.8.12.min.js"></script>
<script src="js/login.js"></script>
</body>
</html>
In our markup here, we’re using the CSS Framework to create a simplified UI widget. This will have the same look as other UI widgets because it’ll use the same classes that are defined via the theme CSS file at the top of the markup. Let’s look at the login.js file and what that holds.
JavaScript
// Make sure that the error state element is hidden.
$(".ui-state-error").hide();
// Call the button widget method on the login button to format it.
$("#btnLogin").button()
// Now bind a click event to handle the login of the form.
.bind ("click", function(){
// Test our form for a valid login.
// Our form only works with:
// username: script
// password: junkie
if ( $("#username").val() != "script"
&& $("#password").val() != "junkie") {
// If the login credentials are not correct,
// show our error state element.
$(".ui-state-error").show();
// Add an jQuery UI effect that shakes the whole login form
// like as if it's shaking its head no.
$("#login section").effect("shake", 150 );
} else {
// If the login credentials are correct, go to the todo page.
document.location = 'todo.html';
}
// return false to cancel normal form submit event methods.
return false;
});
We start by doing some simple set-up, making sure the error message in the markup is hidden and then we style our submit button by calling the button widget on it (you can see more info in the Script Junkie Article Shape Up Your Buttons with jQuery UI.
Next, we bind a click event on our submit button. This will handle testing to make sure the login button is valid and move us to the next page. If the login is invalid it’ll call a jQuery UI effect that makes the entire login form shake like it’s shaking its head no at you to signify the login is incorrect. We’ll also show our error state message to the user.
I’ve set up a jsFiddle so you can experiement with the login form.
Figure 1: Login form with jQuery UI Theme applied
To-do List
Now that we provided a way to login let’s tackle the UI for the to-do list. We need the ability to make many to-do lists based on a number of projects. That may seem complicated but if we tackle each part and then work to integrate them together it’s rather quite simple.
Figure 2 – The to-do application we are building
Project Tabs
Let’s start by creating project tabs. A themeable Tabs widget is provided with jQuery UI in which we’ll use to create tabs that can be added dynamically and also removed dynamically.
Let’s add the markup we’ll need for the tabs.
HTML
<div id="tabs">
<ul>
<li><a href="#project1">Project 1</a</li> </li>
</ul>
<div id="project1">
<!--tab content goes here --></div>
</div>
JavaScript
$(“#tabs”).tabs();
Let’s understand the code above. In the HTML, the div with the id of #tabs is our tabs container. The unordered list defines our tabs and the hyperlink links to the id of the div that will contain the content for the tab, which is below the unordered list but still within our tabs container.
To make this code become a tab widget all we need to do is call a single line of jQuery UI. $(“#tabs”).tabs(); this activates the tabs container HTML into a tabs widget.
This is the easiest way to create tabs within your UI as it’ll create tabs for the HTML that is loaded onto the page. But as we are creating a to-do application the functionality of the UI will require that we be able to add and remove tabs to the UI. So we’ll need a button that when triggered will allow us add a new tab by opening a jQuery UI Dialog which will contain a form to capture the name of the new project. A jQuery UI Dialog provides us a UI element that will lie on top of the rest of the UI making it easy to ask for more information without readjusting the current UI.
Figure 3 – A jQuery UI Dialog
Let’s start by first redefining our $(“#tabs”).tabs(); call. We’ll need to configure options and events when calling the tabs plugin that will make it possible to add new tabs dynamically. All jQuery UI components are easily extended by a similar set of options, events and methods that can be set.
We’ll define the template to use to create a new tab by setting the tabTemplate option and we’ll use Microsoft’s jQuery Template plug-in within the tabs add event to be able to provide the markup template needed for the contents of the tab. We’ll be using the jQuery UI Accordion widget for the individual to-do items. In the HTML template below we construct the accordion container that will hold our individual to-do items, we’ll get into that later.
HTML
<script id="newProjectTabTemplate" type="text/x-jquery-tmpl">
<div>
<div class="accordion">
</div>
<button class="AddToDo">Add</button>
</div>
</script>
JavaScript
// Setup our tabs and cache the jQuery Object to be reused.
var $tabs = $("#tabs").tabs({
// tabTemplate: HTML template from which a new tab is created and added.
// The placeholders #{href} and #{label} are replaced with the
// url and tab label that are passed as arguments to
// the add method.
tabTemplate: "<li><a href='#{href}'>#{label}</a>\
<span class='ui-icon ui-icon-close'>Remove Tab</span></li>",
// add (event): This event is triggered when a tab is added.
add: function( event, ui ) {
// Setup a tab based on a jQuery Template defined in the HTML.
$("#newProjectTabTemplate")
.tmpl()
// append the template results to the selected contents.
.appendTo(ui.panel);
}
});
As stated above we’ll need some other widgets to be able to gather the data to add a new project to the tabs container. Let’s get those together now. We’ll use a button and call the button widget on it so that we can use jQuery UI Theme to style the button and we’ll add the markup for the “Add a project item” dialog and call the dialog widget on it.
HTML
<!—Button mark-up-->
<button id=”AddProject”>Add a project</button>
<!--Dialog mark-up -->
<div id="AddProjectItem" title="Add a project">
<p>Use the form below to add a project.</p>
<div><label for="project">Project name:</label> <input type="text" id="project"></div>
</div>
JavaScript
// Setup a dialog widget and cache the results so we can call it easily.
// This will ask the user for the name of the project.
var addProjectItem = $("#AddProjectItem").dialog({
// modal: If set to true, the dialog will have modal behavior; other items
// on the page will be disabled (i.e. cannot be interacted with). Modal
// dialogs create an overlay below the dialog but above
// other page elements.
modal: true,
// autoOpen: When autoOpen is true the dialog will open automatically when
// dialog is called. If false it will stay hidden until
// .dialog("open") is called on it.
autoOpen: false,
// buttons: Specifies which buttons should be displayed on the dialog.
// The property key is the text of the button. The value is the
// callback function for when the button is clicked. The context
// of the callback is the dialog element; if you need access to
// the button, it is available as the target of the event object.
buttons : {
// creates a button to handle adding a new tab.
"Add new project" : function() {
// using the date to create a unique tab id.
var foo = new Date();
$tabs.tabs("add", "#project-" + foo.getTime(), $("#project").val()) // Select the new tab to make it active.
.tabs("select", $tabs.tabs("length") - 1);
// Close the dialog
$(this).dialog("close");
// Clear the value on the form field in the dialog.
$("#project").val("");
},
// Creates a button to cancel the dialog.
"Cancel" : function() {
// Close the dialog
$(this).dialog("close");
// Clear any values that may have been entered.
$("#project").val("");
}
}
});
// Bind a click event on the Add a Project button.
$("#AddProject").bind("click", function(){
// Opens the dialog to add a new Project tab.
addProjectItem.dialog('open');
// Call button widget to format the Add a Project button.
}).button();
The code starts out by setting up and caching a dialog into the variable addProjectItem so that we can easily call it later in the code. We are also binding a click event to the Add a project button that will open our cached dialog.
We’ve completed adding a new project tab but what if we wanted to remove a project? We’ve already started by adding the mark-up that’ll be the link to remove the tab in the tabTemplate option as we set up the tabs widget.
<span class='ui-icon ui-icon-close'>Remove Tab</span>
We set the span to use the jQuery UI CSS Framework classes ‘ui-icon’ and ‘ui-icon-close’ to the element and jQuery UI will make this element use the close icon instead of the ‘Remove Tab’ text. The only code we need to write is a bind event that will remove the tab from the tabs container.
JavaScript
// Setup a click event to remove a tab from the tab bar.
$( "#tabs span.ui-icon-close" ).live( "click", function() {
// Remove (method): Remove a tab. The second argument is the zero-based
// index of the tab to be removed.
$tabs.tabs('remove', $(this).closest('li').index());
});
To-do items
Now that we’ve got the project tabs programs, let’s tackle the actual to-to list for each project. The to-do list itself will be constructed with an accordion widget. The header of each accordion item will contain the to-do task while its content panel will contain the details, due date and a checkbox to complete the to-do item. We’ll also incorporate the sortable interaction to be able to reorder the to-do items.
Let’s start by looking at the to-do item template. We’ll use this each time we create a new to-do item. Again this is using the Microsoft jQuery Template plug-in.
HTML
<script id="ToDoItemTemplate" type="text/x-jquery-tmpl">
<div>
<h3><a href="#">${task}</a></h3>
<div>
<input type="checkbox" class="completed"> <strong>${task}</strong>
<div>
${description}
</div>
<div>
<strong>Date due:</strong> ${duedate}
</div>
</div>
</div>
</script>
The H3 is the header and will contain the task itself. Below that is the content panel div that is initially hidden but will be shown when the header is clicked. The content panel contains a checkbox, a div for the description and a div for the due date.
Let’s look at adding a new to-do item to the project. We’ll need to add a button to the mark-up and then bind it so that it opens a dialog and collects the data needed. We’ll use the same process for opening a dialog as we did for adding a new project tab.
HTML
<button class="AddToDo">Add</button>
<!-- Dialog for adding a new item -->
<div id="AddToDoItem" title="Add To Do Item">
<p>Use the form below to add a to do item to the list.</p>
<div><label for="task">Task:</label> <input type="text" id="task"></div>
<div><label for="description">Description:</label> <textarea id="description"></textarea></div>
<div ><label for="duedate">Date due:</label> <input type="text" id="duedate"></div>
</div>
JavaScript
//Add a to do item
// Define the dialog for adding a new todo item. Assign it to a variable so
// that it can be reused easily.
var addToDo = $("#AddToDoItem").dialog({
// Set the dialog to be a modal meaning all other elements on the page will be disabled.
modal: true,
// Set autoOpen to False as we don't want the dialog opening on page load.
autoOpen: false,
// Define the dialogs buttons
buttons : {
// Button for adding a to-do item
"Add to do item": function() {
//create a JSON object to pass the data from the form to the template.
var newItem = [{
task: $("#task").val(),
description: $("#description").val(),
duedate: $("#duedate").val()
}],
// select and cache the jQuery Object for the currently visible accordion.
$accordion = $tabs.find(".ui-tabs-panel:visible .accordion");
// Select the template, render it with the JSON data above and append it to
// the visible accordion.
$("#ToDoItemTemplate")
.tmpl(newItem)
.appendTo($accordion);
// Call the refreshAccordion to add the new item to the accordion
$accordion.refreshAccordion();
// Close the dialog
$(this).dialog("close");
// Clear the fields in the dialog
$("#task, #description, #duedate").val("");
},
// Button for cancelling adding a new to do item
"Cancel": function(){
// close the dialog
$(this).dialog("close");
// Clear the field in the dialog
$("#task, #description, #duedate").val("");
}
}
});
// Define a live click event that will open the dialog to add a new todo list.
// We use live instead of just binding the click event because we will be
// dynamically adding new Add To Do buttons with each new tab.
$(".AddToDo").live("click", function(){
// call our variable that defines our Add To-Do Dialog and
addToDo.dialog('open');
});
// Define the datepicker widget for the due date field.
$("#duedate").datepicker();
Each to-do has a checkbox that will complete the task. Functionally all we are doing is closing and removing the element to do that. Let’s create a click event to handle this interaction.
JavaScript
// Define a live click event that will complete a task and remove it from the list.
// We use live instead of just binding the click even because the todo event was
// added dynamically and live will catch the event on newly created elements that are
//created after page load.
$("input[type=checkbox]").live("click", function(){
$(this)
// Search up through the DOM tree to find the first instance of the selection.
// We are looking for the first parent item with a data attribute of sortable-item.
// This data attribute is set from the sortable interaction widget.
.closest(':data(sortable-item)')
// find the H3 element and trigger a click event to activate and close
// the accordion item.
.find('h3').click()
// return the set of matched elements to it's previous state.
.end()
// Hide the item we are completing with a sliding motion. upon completion
// of the animation fire the callback function.
.slideUp(function () {
// remove this todo item from the DOM.
$(this).remove();
});
});
Let’s look at the actions that will be taken on a checkbox click. First we find the closest element that has a data attribute of ‘sortable-item’. This is set by the sortable interaction that we set on the to-do items earlier. We then find the h3 element and trigger a click event to collapse the accordion item. The .end() method is called to take us back to the original jQuery object and then we use jQuery animation event slideUp to hide the to-do item and finally we remove it from the DOM.
At this point we have a working to-do list where we can add Projects, lists of to-do’s per project, complete an item and remove projects. Again, I’ve setup a jsFiddle so you can experiment with the UI.
Going Farther
In this article we look at just the UI components to make a to-do application. We still need to go farther to make this a complete application. Ultimately we’ll need to hook the UI up to a back end system. You are not limited to any particular server technology. Server-side technologies such as ASP.NET, ASP.NET MVC, PHP, and Ruby on Rail can all be used but each would have it own tutorial in setting it up.
What remain common in whatever server-side backend you choose is the Ajax calls to get and send data back to the server. But that is saved for another time.
Conclusion
I have demonstrated how easy it is to get started and build a complex UI with jQuery UI. The robustness of jQuery UI makes it easy to extend it to the needs of your application. The prepackaged themes that are available make it so that all your UI elements have a similar look and feel. The ThemeRoller that is available on jQueyUI.com can be used to make custom themes that you can use for your application.
Download the article source code here.
About the Author
Ralph is a senior web application engineer at BrandLogic Corporation where he builds web applications with jQuery for Fortune 500 companies. Ralph is also on the jQuery team in various roles his most visible is as co-host of the Official jQuery Podcast which interviews key members of the jQuery community.
Find Ralph on:
- Twitter - @redwolves