jQuery: The science of beautiful design

Discover the art of interactive page design with a collection of essential UI techniques for perfect page chemistry


JavaScript is the language that provides the platform for a host of siblings, including the web designer’s library of choice, jQuery. This wasn’t always the case – it was inconsistent and unwieldy – but the introduction of jQuery took the language to a new level.

The library simplified the art of coding and brought it closer to web designers who wanted to create attractive and dynamic page elements without the need for an extensive knowledge of the language, or the use of Flash. The beauty of jQuery lies in its simplicity and flexibility. The language may still seem like a maze of unfathomable code for many. But without it, the task of creating interactive UI elements that populate the best sites on the web would be a far harder task.
jQuery can bring design to life, but still ensure that its associated elements are functional and effective. Accordions, tabs and date pickers can all be powered to add dynamism to simple blocks of code. With the code in place designers simply need to bring their core CSS skills into the equation to start styling the functional to match a chosen design. jQuery provides the base, while CSS and imagination provide the beauty.

What is it that helps simplify the process? What is the secret power behind the language web designers and developers love? Plug-ins. These are effectively self-contained blocks of code that perform a specific task. And, the best bit is there are thousands of them waiting to help you create desired UI effects in double-quick time. To fully embrace jQuery, discovering how to develop a plug-in really is the icing on the cake.

There is no doubt that jQuery has levelled the playing field by tucking workarounds for the various browser quirks that still exist into its API. Launch Trigger and $.ajax, and let jQuery do the hard part. Creating beautiful interactive interfaces and page elements has never been easier thanks to the jQuery library. Now is the time to delve into the mysteries of jQuery and discover how to start turning the science into beautiful design.


Don’t Use jQuery

Much like Ruby on Rails, many developers’ first introduction to JavaScript was through jQuery. This lead to a common cycle: learn jQuery, fall in love, dig into vanilla JavaScript and level up. While there’s certainly nothing wrong with this cycle, it did pave the way for countless articles, which recommended that users do not use jQuery in various situations, due to ‘performance issues’.
It wouldn’t be uncommon to read that it’s better to use vanilla for loops over $.each. Or, at some point or another, you might have read that it’s best practice to use document.getElementsByClassName over jQuery’s Sizzle engine, because it’s faster. The problem with tips like this is that they take the idea of pre-optimisation to an extreme, and don’t account for various browser inconsistencies – the things that jQuery fixed for us! Running a test and observing a saving of a few milliseconds over thousands of repetitions is not a reason to abandon jQuery and its elegant syntax. Your time is much better invested in tweaking parts of your application that will actually make a difference, such as the size of your images, for instance.

Multiple jQuery Objects

This second anti-pattern, again, was the result of the community (including yours truly at one point) not fully understanding what was taking place under the jQuery hood. As such, you likely came across (or wrote yourself) code, which wrapped an element in the jQuery object countless times within a function.

001 $(‘button.confirm’).on(‘click’, function() {
002 // Do it once
003 $(‘.modal’).modal();
004 // And once more
005 $(‘.modal’).addClass(‘active’);
006 // And again for good measure
007 $(‘modal’).css(...);
008 });

While this code might, at first, appear to be harmless (and truthfully is, in the grand scheme of things), we’re following the bad practice of creating multiple instances of the jQuery object. Every time that we refer to $(‘.modal’), a new jQuery object is being generated. Is that smart? Think of the DOM as a pool: every time you call $(‘.modal’), jQuery is diving into the pool, and hunting down the associated coins (or elements).
When you repeatedly query the DOM for the same selector, you’re essentially throwing those coins back into the water, only to jump in and find them all over again!
Always chain selectors if you intend to use them more than once. The previous code snippet can be refactored to:

001 $('button.confirm').on('click', function() 
002 { 
003 $('.modal')
004 .modal()
005 .addClass('active')
006 .css(…);
007 });
008 Alternatively, use “caching.”
009 $('button.confirm').on('click', function() 
010 {
011 // Do it ONLY once
012 var modal = $('.modal');
013 modal.modal();
014 modal.addClass('active');
015 modal.css(…);
016 });

With this technique, jQuery jumps into the DOM pool once, rather than three times.

Handlebars is a fantastic templating engine that is easy to use. Try it out

Selector Performance

While not as ubiquitous these days, not too long ago the web was bombarded by countless articles on optimising selector performance in jQuery. For example, is it better to use $(‘div p’) or $(‘div’).find(‘p’)?
Ready for the truth? It doesn’t really matter. It’s certainly a good idea to have a basic understanding of the way that jQuery’s Sizzle engine parses your selector queries from right to left (meaning that it’s better to be more specific at the end of your selector, rather than the very beginning).
And of course, the more specific you can be, the better. Clearly, $(‘a.button’) is better for performance than $(‘.button’), due to the fact that, with the former, jQuery is able to limit the search to only the anchor elements on the page, rather than all elements.
Beyond that, however, too much attention is paid to selector performance. When in doubt, put your trust in the fact that the jQuery team is comprised of the finest JavaScript developers in the industry. If there is a performance boost to be achieved in the library, they will have discovered it.
And if not them, one of the thousands of community members that make up this great online hub of design and development will submit a pull request.
With this in mind, be aware of your selectors, but don’t concern yourself too much with performance implications, unless you yourself can verbalise why doing so is necessary.

Callback Hell

jQuery has encouraged widespread use of callback functions, which can certainly provide a nice convenience. Rather than declaring a function, simply use a callback function. For example:

001 $(‘a.external’).on(‘click’, function() {
002 // this callback function is triggered
003 // when .external is clicked
004 });

You’ve certainly written plenty of code that looks just like this; I know I have! When used sparingly, anonymous callback functions serve as helpful conveniences. The rub occurs down the line, when we enter (trigger thunderbolt sound…) callback hell! Callback hell is when your code indents itself numerous times, as you continue nesting callback functions.
Consider the foll
owing, quite common, code below:

001 $(‘’).on(‘click’, function() {
002 var anchor = $(this);
003 $(this).fadeOut(400, function() {
004 $.ajax({
005 // …
006 success: function(data) {
007 anchor.fadeIn(400, function() {
008 // you’ve just entered callback hell
009 });
010 }
011 });
012 });
013 });

As a basic rule of thumb, the more indented your code is, the more likely there’s a code smell. Or better yet, ask yourself, does my code look like the Mighty Ducks’ Flying V?
When refactoring code such as this, the key is to ask yourself, ‘how could this be tested?’ Within this seemingly simple bit of code, an event listener is bound to a link, the element fades out, an AJAX call is being performed – upon success, the element fades back in, and presumably, the resulting data will be appended somewhere. That sure is a lot to test!
Wouldn’t it be better to split this code into more manageable and testable pieces? Certainly. Though the following can be optimised further, a first step to improving this code might be:

001 var updatePage = function(el, data) {
002 // append fetched data to DOM
003 };
004 var fetch = function(ajaxOptions) {
005 ajaxOptions = ajaxOptions || {
006 // url: ...
007 // dataType: ...
008 success: updatePage
009 };
010 return $.ajax(ajaxOptions);
011 };
012 $(‘’).on(‘click’, function() {
013 $(this).fadeOut(400, fetch);
014 });

Even better, if you have a variety of actions to trigger, contain the relevant methods within an object.

Think about how in a fast-food restaurant, such as McDonalds, each worker is responsible for one task. Joe does the fries, Karen registers customers, and Mike grills burgers. If all three members of staff did everything – not just their own jobs – this would introduce a variety of maintainability problems.
When changes need to be implemented, we have to meet with each person to discuss them. However, if we, for example, keep Joe exclusively focused on the fries, should we need to adjust the instructions for preparing fries, we only need to speak with Joe and no one else. You should take a similar approach to your code; each function is responsible for one task.

In the code above, the fetch function merely triggers an AJAX call to the specified URL. The updatePage function accepts some data, and appends it to the
DOM. Now, if we want to test one of these functions to ensure it’s working, eg the updatePage method, we can mock the data object, and send it through to
the function.

Reinventing the Wheel

It’s important to remember that the jQuery ecosystem has matured greatly over the last several years. Chances are, if you have a need for a particular component, then
someone else has already built it. Certainly, continue building plug-ins to increase your understanding of
the jQuery library (in fact, we’ll write one in this
article), but, for real-world usage, refer to any potential existing plug-ins before reinventing the wheel. As
an example, need a date picker for a form? Save yourself the leg-work, and instead take advantage of
the community-driven – and highly tested – jQuery
UI library.

Once you reference the necessary jQuery UI library and associated stylesheet, the process of adding a date picker to an input is as easy as doing:

001 < input id="”myDateInput”" type="”text”/" />
002 < script type="text/javascript">// < ![CDATA[ 
003 $(“#myDateInput”).datepicker({
004 dateFormat: ‘yy-mm-dd’
005 });
006 // Demo:
007 // ]]>< /script>

Or what about an accordion? Sure, you could write that functionality yourself, or instead, once again, take advantage of jQuery UI. Simply create the necessary markup for your project.

001 < div id="”accordion”">
002 < h3>Chapter 1
003 < div>Some text.
004 < h3>Chapter 2
005 < div>Some text.
006 < h3>Chapter 3
007 < div>Some text.
008 < h3>Section 4
009 < div>Some text.

Then, automagically turn it into an accordion. 011 $(function() {
012 $(“#accordion”).accordion(); });

What if you could create tabs in thirty seconds?

 013 < div id="”tabs”">
014 < ul>
015 < li>About Us
016 < li>Our Mission
017 < li>Get in Touch
018 < /ul>
019 < div id="”tabs-1”">
020 About us text.
021 < /div>
022 < div id="”tabs-2”">
023 Our mission text.
024 < /div>
025 < div id="”tabs-3”">
026 Get in touch text.
027 < /div>
028 < /div>

And activate the plug-in. 029 $(function() {
030 $(“#tabs”).tabs();
031 });

Done! It doesn’t even require any notable understanding of JavaScript.


Over the course of building the sample MessageBox plug-in, a variety of best practices have emerged, such as avoiding callback hell, writing testable code, making the default options available to the plug-in user, and ensuring that each method is responsible for exactly one task, and one task alone.
While one could certainly achieve the same effect by embedding countless callback functions within $.message, doing so is rarely a good idea, and is even considered an anti-pattern. Remember the three keys to maintainable code and flexible plug-ins and scripts:
1. Could I test this? If not, you must refactor and split the code into chunks.
2. Have I, at any point, offered the ability to override my default settings?
3. Am I following any practices that are generally accepted to be bad, or making assumptions?