My latest blog entries.

Form Widgets with jQuery UI

jquery-ui

Hello!  In this tutorial we’re going to (finally!) finish the HTML5 contact form we’ve been developing.  Just a quick recap – in part one we went over the HTML markup and discussed the new types of input element;  in part two we prettied the form up a little using CSS3; in part three we wrote a JavaScript function to detect whether the user’s browser supports any of the new UI widgets; and in part four we used the jQuery Validate plugin to enable client side form validation.  In this tutorial, we’re going to use jQuery UI to enable form widgets if they aren’t supported natively.

Check out where we’re at thus far: Demo
You can view the finished product here

Before we start, I’m going to make a quick change to the form – I’m going to change the input type of the ‘project-budget’ field from ‘number’ to ‘range’, and remove validation for this input.  Now we have an extra UI widget to work with.

For this tutorial, we don’t need all of jQuery UI – we only need the core and a couple of the widgets.  So do we serve a custom jQuery UI file, which will be considerably smaller in size, or load the whole of jQuery UI from the Google CDN?  From a performance perspective, both options have pros and cons.  On the one hand, you have the smaller file size of a custom build, but on the other you have content being downloaded concurrently from a CDN, with the possibility that it’s already cached in the user’s browser. If the custom build was very small I would go for that option, but for the purposes of this tutorial, the simplicity of loading jQuery UI from Google’s CDN is just easier for me.  You should always consider each case on it’s own merit to get the best performance possible; but if you decide to use files on an external CDN, make sure that you always put a fallback in place in case the CDN is unavailable.

We need to include a reference to the jQuery UI base theme in the head of our page.

	<link rel="stylesheet" href="screen.css">
	<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/base/jquery-ui.css">

We already have some script files referenced in our html -

	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
	<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
	<script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
	<script src="global.js"></script>

Lets change the version number on the jQuery core to 1.5, and import jQuery UI. Paste the following lines of JavaScript code into the bottom of the form HTML, just before the last body tag.

	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
	<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
	<script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
	<script src="global.js"></script>

Why do we put them at the bottom of the page?  We put them here so that they don’t block the loading and rendering of page content.  If you place a script in the head of a page, especially if it’s a large one, the browser retrieves and parses this script before flowing the content into the page.  So your user will be faced with a big fat blank page until the script has been fetched and parsed, which could be a considerable amount of time depending on how large your script is.  Not very user friendly!  Notice also that we’re linking to a specific version number, not the shorthand ‘latest’.  If you link to the ‘latest’ version, you don’t get the benefits of caching mentioned above, and run the (small) risk of backwards compatibility issues with your code when the next version of jQuery is released.

Below is the (refactored) support detection script that we wrote in part three.

(function() {
	var inputs = ['url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
		input = document.createElement('input')
	    len = inputs.length,
	    uiSupport = {},
		i = 0;

	for (; i < len; i++) {
		input.setAttribute('type', inputs[i]);

		if (input.type === 'text') {
			uiSupport[inputs[i]] = false;
		} else {
			input.value = 'testing';
			(input.value === 'testing') ? uiSupport[inputs[i]] = false : uiSupport[inputs[i]] = true;
		}
	}
})();

After this function has executed (i.e. immediately), the uiSupport object will contain the name of each input type, and a boolean value (true or false) describing whether it is supported in the browser.  Next we’re going to create an object to contain all of our widget methods.  We’re going to use the slider and the datepicker from jQuery UI.

...
		i = 0,
		widgets = {
			date : function(elem) {
				elem.datepicker({
					beforeShow : function(input, inst) {
				    	inst.dpDiv.css({
							fontSize : '14px',
							marginLeft : 215,
							marginTop : -22
						});
				    }
				});
			},
			range : function(elem) {
				elem
					.after('<div></div><span class="slider-val">1500</span>')
					.next()
					.slider({
						value : 1500,
						min : 500,
						max : 4000,
						step : 500,
						slide: function(event, ui) {
							$(this).next().text(ui.value);
						}
					})
					.end()
					.remove();
			},
			altrange : function(elem) {
				elem
					.addClass('ui-slider')
					.after('<span class="slider-val">1500</span>')
					.change(function() {
						$(this).next().text($(this).val());
					});
			}
		};

Then after the for loop, we’re going to use for…in to iterate over the uiSupport object to check support and call the relevant method:

	for (var prop in uiSupport) {
		if (prop === 'range') {
			widgets[(uiSupport[prop] ? 'alt' : '') + prop]($('input[type=' + prop + ']', 'form'));
		}
		if (prop === 'date' && !uiSupport[prop]) {
			widgets[prop]($('input[type=' + prop + ']', 'form'));
		}
	}

The last thing we need to do is to disable the ‘contact time’ field, unless the ‘phone’ radio button has been clicked.  Our complete function looks like this:

$.contactForm = (function() {

	var inputs = ['url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
		input = document.createElement('input')
	    len = inputs.length,
	    uiSupport = {},
		i = 0,
		widgets = {
			date : function(elem) {
				elem.datepicker({
					beforeShow : function(input, inst) {
				    	inst.dpDiv.css({
							fontSize : '14px',
							marginLeft : 215,
							marginTop : -22
						});
				    }
				});
			},
			range : function(elem) {
				elem
					.after('<div></div><span class="slider-val">1500</span>')
					.next()
					.slider({
						value : 1500,
						min : 500,
						max : 4000,
						step : 500,
						slide: function(event, ui) {
							$(this).next().text(ui.value);
						}
					})
					.end()
					.remove();
			},
			altrange : function(elem) {
				elem
					.addClass('ui-slider')
					.after('<span class="slider-val">1500</span>')
					.change(function() {
						$(this).next().text($(this).val());
					});
			}
		};

	for (; i < len; i++) {
		input.setAttribute('type', inputs[i]);

		if (input.type === 'text') {
			uiSupport[inputs[i]] = false;
		} else {
			input.value = 'testing';
			(input.value === 'testing') ? uiSupport[inputs[i]] = false : uiSupport[inputs[i]] = true;
		}
	}

	for (var prop in uiSupport) {
		if (prop === 'range') {
			widgets[(uiSupport[prop] ? 'alt' : '') + prop]($('input[type=' + prop + ']', 'form'));
		}
		if (prop === 'date' && !uiSupport[prop]) {
			widgets[prop]($('input[type=' + prop + ']', 'form'));
		}
	}

	$('input[type=time]').attr('disabled','disabled');
	$('input[type=radio]').click(function() {
		if ($(this).val() === 'Phone') {
			$('input[type=time]').removeAttr('disabled');
		} else {
			$('input[type=time]').attr('disabled','disabled');
		}
	});

})();

If we have a look at our form now, we can see that in Firefox, where none of the UI widgets are currently supported, we have the jQuery UI slider and datepicker:

In Chrome, where the range slider is supported but the datepicker isn’t, we see this:

And in Opera, where both the range slider and datepicker are supported natively, we see this:
You can view the final product at the link below.
Demo

I hope you’ve enjoyed this series of tutorials.  Please leave a comment below if you have any questions or suggestions.

The following two tabs change content below.
Nicola Hibbert is a freelance front end developer based in Guildford, Surrey. She writes articles about front end development and HTML5 games, maintains a suite of jQuery and WordPress plugins over at Stitch UI, and is the creator of the PlayZap mobile games portal.

8 Comments

Got something to say? I'd love to hear from you!
Leave a Comment

  1. George says:

    Thanks for looking at this for me. The code that you uncommented was when I was playing with the form and Dojo but at the end you can see that I was running yor global.js which I was expecting to create the same form but it never was creating the form so I was stuck on determining why global.js wasn’t doing what the HTML page looks like now.

  2. George says:

    OK, here’s a link to the code but I have to warn you that it’s a mess because I’ve been testing and commenting!

    http://jsfiddle.net/GeorgeIoak/M4zar/

  3. George says:

    Yes I did. For some reason my code pastes did not come threw on my first post so I’ll try again:

    which is at the bottom of my page before

    In case the code doesn’t come through again I’m using Google’s CDN and pulling jquery.min.js version 1.6.2 and jquery-ui.min.js version 1.8.15

  4. George says:

    I like your article and followed along with it but I can’t seem to get it to create the form on the web page and I’m a little confused as to why because I can step through the code and see it go through global.js (I downloaded global.js, screen.css, reset.css from your website) but it doesn’t trow an error in console and the form does not appear.

    at the top of my HTML page I have:

    then just some minor HTML code to add something to the test page. and at the bottom right before the end of the body I have:

    I’m running the newer versions of all the files but it seems like that shouldn’t completely break the code? I tested it on a Windows machine with both IE9 and FF5

Leave a Comment

If you have any thoughts
or feedback, please
leave a comment.