Escaping from the jQuery-style development
The World Wide Web is a fairly simple environment; all the content available is accessible using a web browser. Also, web browsers are conceptually simple applications. They ask for a web resource, a server sends that resource, and they display what's inside the received data. A web resource is nothing else than a series of files that represents different types of content:
- User interface structures: HTML markup
- User interface graphic styles: CSS
- User interface programs and executables: JavaScript
- Data: JSON or XML
Every browser supports these four types of content even if there are some interpretation differences (global standardization has not yet been reached).
We can say that JavaScript is the programming language of the Web, but its native DOM API is somewhat rudimentary. We have to write a lot of code to manage and transform HTML markup to bring the UI to life with some dynamic user interaction. Also, full standardization does not mean that the same code will work differently (or work at all) in different browsers.
Over the past few years, developers decided to resolve this situation; JavaScript libraries such as Prototype, jQuery, and Dojo have come to light.
jQuery is one of the most known open source JavaScript libraries, published in 2006 for the first time. Its huge success is mainly due to the following reasons:
- A simple and detailed API that allows us to manage HTML DOM elements
- Cross-browser support
- Simple and effective extensibility
Since its appearance, it has been used by thousands of developers as the foundation library. A large amount of JavaScript code all around the world has been built with jQuery in mind. A jQuery ecosystem grew up very quickly, and nowadays there are plenty of jQuery plugins that implement virtually everything related to web development.
A typical jQuery-based web application is something like the following sample page:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>jQuery Sample</title> <style type="text/css"> … </style> </head> <body> <h4>jQuery Sample</h4> <form id="inputForm"> <label for="firstName">Name: </label> <br> <input type="text" id="firstName" name="firstName"> <br> <label for="lastName">Last Name: </label> <br> <input type="text" id="lastName" name="lastName"> <br> <fieldset> <legend>Nationality:</legend> <input type="radio" name="nat" value="it" checked>Italian <br> <input type="radio" name="nat" value="foreign">Foreign <br> <select id="foreignNat" disabled="disabled"> <option value="">Select an item...</option> <option value="us">American</option> <option value="uk">British</option> <option value="fr">French</option> <option value="other">Other</option> </select> </fieldset> <input type="submit" value="Submit"> <br> <p id="resultMessage"></p>c </form> <script type="text/JavaScript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script type="text/JavaScript"> //JavaScript Code </script> </body> </html>
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
This is a simple HTML page with a single form and bunch of inputs:
If we want to validate this form before submission, we can add the following code in the script
tag:
function showMessage (message, isError) { jQuery("#resultMessage").text(message); if (isError) { jQuery("#resultMessage").css("color", "red"); } else { jQuery("#resultMessage").css("color", "green"); } jQuery("#resultMessage").css("visibility", "visible"); } function hideMessage () { jQuery("#resultMessage").css("visibility", "hidden"); } hideMessage(); jQuery(document).ready(function () { jQuery("#inputForm input:radio").on("click", function () { if (jQuery(this).val() === "it") { jQuery("#foreignNat option").removeAttr("selected"); jQuery("#foreignNat").attr("disabled", "disabled"); } else { jQuery("#foreignNat").removeAttr("disabled"); } }); jQuery("form").submit(function (e) { e.preventDefault(); hideMessage(); jQuery("#resultMessage").text("Please fill First Name"); if (!jQuery("#firstName").val()) { showMessage("Please fill First Name", true); return; } if (!jQuery("#lastName").val()) { showMessage("Please fill Last Name", true); return; } if (!jQuery("input[name=nat]:checked").val()) { showMessage("Please select Nationality", true); return; } if ((jQuery("input[name=nat]:checked").val() === "foreign") && (!jQuery("#foreignNat option:selected").val())){ showMessage("Please select Foreign Nationality", true); return; } //form submission, for example with an AJAX call showMessage("Form submitted", false); }); });
The preceding code is a typical example of jQuery-style programming. Nearly every line contains a jQuery call, from pure tag content management to event handling. It works and is also a simple API to learn; an average developer becomes productive in a very short period of time.
In the preceding code, we defined two helper functions (showMessage
and hideMessage
) to show and hide the alert message. Then, when the page is fully loaded (jQuery(document).ready(…)
), we defined two event handler functions: one for the radio button click (jQuery("#inputForm input:radio").on("click", …)
) and the other for the form submission (jQuery("form").submit(…)
).
In the first one, we activate or disable the drop-down menu, whether or not the Foreign radio option is checked:
In the second event handler function, we make some input validation and proceed with form submission only if all the inputs are properly filled, providing a specific status message.
The preceding example is a simple one: there are a few HTML tags, the UI logic is quite simple (just a message that appears/disappears and drop-down menu that activates/deactivates), and the validation is pretty straightforward.
Yet, despite its simplicity, all of the code that we just wrote is virtually untestable. There are two main reasons:
- User interface items are tightly coupled with the user interface logic
- The user interface logic spans inside the event handler callback functions
The real problem here is that everything passes through a jQuery reference, that is, a jQuery("something")
call. This means that we always need a live reference of the HTML page, otherwise those calls fail, and this is also true for a unit test case. We can't think of testing a piece of user interface logic running an entire web application!
Consider a real-case scenario, not this super simple example of a complex e-commerce website, and we need to test a specific page. We need to reach this page first (maybe we also need to enter some user credentials because it's a private page), provide some input data, and then start our test case. Think of repeating this journey as many times as different input data combinations are possible, or try to automate this process in someway. A real nightmare!
Worse than this, you have to repeat the entire process if you simply change a single HTML tag ID and nothing else. The UI logic is unchanged, but we need to assure that no jQuery tag reference breaks if we change something in the HTML markup. This is really too much work to do. We will soon realize that we spend more time running unit tests than developing the application.
On the other hand, large jQuery applications tend to be monolithic because jQuery itself allows the callback function nesting very easily and doesn't really promote any particular design strategy. The result is often a spaghetti code.
jQuery is a good option if you want to develop a specific custom plugin. We will also continue to use this library for pure user interface effects and animations, but we need something different to maintain a large web application's logic.