Interactive Form Functionality on the Client-Side Using jQuery

Restaurant Menu and Order Form Preview

Restaurant Menu / Order Form Preview

Introduction

Many of us have used ASP.NET Web Forms for years, combined more recently with ASP.NET AJAX, to build robust web-solutions for our clients. Although Web Forms are not going away, it is also not the only technology available to ASP.NET developers to build web-solutions, or necessarily always the best. A developer’s ability to understand and implement multiple development technologies is critical to ensuring the best solution for the client.

Recently, the popularity of serious client-side development with JavaScript, jQuery, and AJAX has exploded. Much of the server-side processing required with ASP.NET Web Forms can easily be moved to the client-side with the help of increasingly sophisticated scripting tools such as jQuery and Ajax. The following article demonstrates and discusses a simple client order form, built using HTML, JavaScript, jQuery, AJAX, XML, and CSS. This example demonstrates many basic as well as some advanced capabilities of jQuery, including:

  • Asynchronous HTTP (Ajax) request to populate a drop-down menu with XML data
  • jQuery animation and CSS manipulation to enhance the client UI experience
  • Use of jQuery plug-ins, specifically FormatCurrency to format text
  • JavaScript and CSS minification to increase performance and obfuscate client-side code
  • Use of Content Delivery Networks (CDN) to further optimize performance through web caching

In this example, a user individually chooses products from a drop-down menu, inputs the desired quantity, and adds the selection to their order. The selections along with a subtotal of their costs are displayed in the order table. Items can be removed from the order and additional items added. The order’s total cost is updated and displayed as items are added and removed. All events are handled on the client-side, without any server-side processing. A working example of this form can be accessed here.

About the Code

The files which make up the web directory of the order form example are as follows: (2) versions of the HTML order form, (1) XML data file with menu items, (6) JavaScript files, and (2) versions of the Style Sheets. Shown below is the directory of those files as seen in Visual Studio 2008. All code for this article is available for download at on The Code Project.

Visual Studio 2008 Solution View

Visual Studio 2008 Solution View

The order form example comes in two flavors – an easy-to-understand, development-oriented copy (order_dev.htm), and a production-oriented copy (order_prd.htm), optimized for faster web-serving. The development version has all my JavaScript left in the bottom of the HTML file. The Style Sheet, jQuery library and FormatCurrency jQuery plug-in scripts are externally linked to non-minified sources. Conversely, the production version has the Style Sheet and all JavaScript externally-linked to minified files. I created two versions of the order form in order to compare the effects of optimization techniques on web-serving performance.

Code Optimization and Obfuscation

Using CSS Drive’s CSS Compressor online utility, I decreased the size of my externally-linked Style Sheet file by 26%. I selected the ‘Super Compact’ and “Strip ALL Comments’ options. Using Google’s Closure Compiler online utility, I decreased the size of my JavaScript by 43%. I selected the ‘Simple’ Optimization option. The more aggressive ‘Advanced’ option resulted in JavaScript errors. I did not select a Formatting option. According to the results from Firefox using Yahoo! YSlow, externally linking to minified copies of my Style Sheet and JavaScript files reduced the total size of the information sent to the browser from 175.8K to 79.9K, a savings of nearly 55%.

You can further test page performance by replacing the local link to the jQuery script file with a link to the minified copy of jQuery on Google’s Content Delivery Network (CDN). The current link is commented out within order-prd.htm. For an explanation of the advantages and disadvantages of using a CDN, I recommend Dave Ward’s post on Encosia.com, entitled 3 reasons why you should let Google host jQuery for you.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

jQuery IntelliSense in Visual Studio 2008

The obvious advantage of keeping the JavaScript in the HTML page, at least during development, is the ability to take advantage of IntelliSense in Visual Studio 2008 with jQuery. IntelliSense makes the jQuery learning process much quicker! See a good post on this topic, jQuery Intellisense in VS 2008, by Scott Guthrie, at ScottGu’s Blog. Note, as of the date of publication of this article, the latest version of jQuery to have the necessary ‘-vsdoc’ file available for use with IntelliSense was version 1.4.1. I used this for the development version of the example. The production copy uses a later, minified version of jQuery 1.4.2, which is notably faster than 1.4.1.

Placing the Order

The form contains a button to place the final order. In this example, pressing the button returns a simple JavaScript alert(), depending on the contents of the order. In actual production, the order page could submit form data to a secondary page or code-behind class (ASP.NET Web Forms) for order processing. Alternatively, data could be formatted and sent directly back to an XML file or to a database using Ajax. Order processing could be done on the client- or server-side, depending on the technology implemented.

Using the Code

The order page contains two HTML tables. One table holds the menu selection elements and the other table displays the current order. Since jQuery so eloquently handles all interactions within the UI, there is very little HTML code to write.

<table id="select">
    <caption>
        Menu</caption>
    <tr>
        <td>
            Qnt.:
        </td>
        <td>
            <input id="select_quantity" type="text" /> (*1-99)
        </td>
        <td>
            <select id="select_item">
                <option selected="selected">Select an Item...</option>
            </select>
        </td>
        <td>
            <input id="add_btn" type="button" value="Add" />
        </td>
    </tr>
</table>
<br />
<br />
<table id="order_cart">
    <caption>
        Order</caption>
    <thead>
        <tr>
            <th>
                Qnt.
            </th>
            <th>
                ID
            </th>
            <th>
                Description
            </th>
            <th>
                Price
            </th>
            <th>
                Subtotal
            </th>
            <th>
                Remove
            </th>
        </tr>
    </thead>
    <tbody>
    </tbody>
    <tfoot>
        <tr>
            <th colspan="4">
                Total:
            </th>
            <th id="order_total">
                $0.00
            </th>
            <th>
                <input id="order_btn" type="button" value="Order!" />
            </th>
        </tr>
    </tfoot>
</table>

<script src="scripts/jquery-1.4.1.js" type="text/javascript"></script>

<script src="scripts/jquery.formatCurrency-1.3.0.js" type="text/javascript"></script>

The JavaScript contained in order_dev.htm immediately precedes the closing </body> tag. Keeping the JavaScript at the bottom of the page whenever possible allows the CSS and DOM elements to load first. I have included a large number of comments detailing much of the functionality contained in each part of the JavaScript.

<script type="text/javascript">
    //Retrieve XML document and loop for each item
    jQuery(function($) { //just like $(document).ready()
        $.ajax({
            type: "GET",
            url: "data/menu.xml",
            dataType: "xml",
            error: function() {
                $("<p>Error loading XML file...</p>")
                .replaceAll("#order_form")
            },
            success: function(xml) {
                $(xml).find("item").each(fWriteXML); //must call function as var
            }
        });
    });

    //Populate drop-down box with XML contents
    var fWriteXML = function writeXML() {
        var id = $(this).attr("id");
        var cost = $(this).attr("cost");
        var item = $(this).text();
        $("#select_item")
        .append($("<option></option>")
        .val(id) //same as .attr("value", id))
        .html(item)
        .attr("title", cost));
    };

    //Add selected item to order
    $(function() {
        $("#add_btn").click(function() {
            var order_item_selected_quantity = $("#select_quantity").val()
            var selected_item = $("#select_item option:selected");
            var order_item_selected_id = selected_item.val();
            var order_item_selected_name = selected_item.text();
            var order_item_selected_cost = selected_item.attr("title");

            var pattern = new RegExp("^[1-9][0-9]?$"); //Select between 1-99 items
            //Do not proceed if input is incorrect
            if (pattern.test(order_item_selected_quantity) &&
                order_item_selected_cost != "") {

                //Calculate subtotal
                var order_item_selected_subtotal =
                    parseFloat(order_item_selected_cost) *
                    parseInt(order_item_selected_quantity);

                $("<tr class='order_row'></tr>").html("<td>"
                    + order_item_selected_quantity + "</td><td>"
                    + order_item_selected_id + "</td><td class='order_item_name'>"
                    + order_item_selected_name + "</td><td class='order_item_cost'>"
                    + order_item_selected_cost +
                    "</td><td class='order_item_subtotal'>"
                    + order_item_selected_subtotal + "</td><td>"
                    + "<input type='button' value='remove' /></td>")
                .appendTo("#order_cart").hide();

                $("#order_cart tr.order_row:last").fadeIn("medium", function() {
                    orderTotal(); //Callback once animation is complete
                });

                //Format new order item values to currency
                $("#order_cart td.order_item_cost:last").formatCurrency();
                $("#order_cart td.order_item_subtotal:last").formatCurrency();

                clickRemove();
                clearForm();
            }
        });
    });

    //Bind a click event to the correct remove button
    function clickRemove() {
        $("#order_cart tr.order_row:last input").click(function() {
            $(this).parent().parent().children().fadeOut("fast", function() {
                $(this).parent().slideUp("slow", function() {   //the row (tr)
                    $(this).remove();   //the row (tr)
                    orderTotal();
                });
            });
        });
    };

    //Clear order input form and re-focus cursor
    function clearForm() {
        $("#select_quantity").val("");
        $("#select_item option:first-child").attr("selected", "selected");
        $("#select_quantity").focus();
    };

    //Calculate new order total
    function orderTotal() {
        var order_total = 0;
        $("#order_cart td.order_item_subtotal").each(function() {
            var amount = ($(this).html()).replace("$", "");
            order_total += parseFloat(amount);
        });

        $("#order_total").text(order_total).formatCurrency();

        //Create alternating colored rows in order table
        $("#order_cart tr.order_row:odd").css("background-color", "#F0F0F6");
        $("#order_cart tr.order_row:even").css("background-color", "#FFF");
    };

    //Pretend to place order if it contains items
    $(function() {
        $("#order_btn").click(function() {
            if ($("#order_cart tr.order_row:last").length == 0) {
                alert("No items selected...");
            }
            else {
                alert("Order placed...");
            }
        });
    });
</script>

Additional Resources

I highly recommend the following resources to both beginner and intermediate jQuery developers who want to learn more about this great client-side development tool:

, , , , , , , ,

  1. #1 by Gary A. Stafford on June 16, 2013 - 10:30 am

    What gives you the right to spam my blog? Wow what an effective marketing strategy (for a three year old)!

  1. Consuming Cross-Domain WCF REST Services with jQuery using JSONP « Programmatic Ponderings

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: