devxlogo

An Introduction to jQuery, Part 1

An Introduction to jQuery, Part 1

k, I admit it. For many, many years I hated JavaScript. I hated writing JavaScript code, and I hated the pain that goes along with dealing with different browsers using reams of script code even more. I still hate those same problems, but thanks to a recently-gained better understanding of JavaScript and a small JavaScript client library called jQuery, I no longer dread the days when I have to write client-centric AJAX script code. In fact, I welcome them now! With client logic getting ever more complex and browsers still diverging in features and implementation of features, jQuery and other client libraries provide much needed normalization when working with JavaScript and the HTML DOM.

I started out as a JavaScript neophyte?and stayed that way for a long time. Sure I used JS here and there for validation and few simple manipulation tasks, but it wasn’t until a few years ago when AJAX surfaced that I took a more serious interest. It took me some time after that to slowly get up to speed on modern JavaScript principles I had never even thought about up until then, such as: effective use of closures, the Prototype model, class definitions, and using functions as code?all of which require some getting used to when you deal primarily with static languages such as C#.

But even knowing JavaScript reasonably well is not enough. These days it’d be silly to work with raw JavaScript for DOM programming without using some sort of library to help bridge the browser-specific quirks and provide utility functionality and a browser-agnostic environment. I’ve checked out a number of different libraries over the last couple of years, but the one that really grabbed me is jQuery, a relatively small library based on a few very simple and intuitive principles. This library seems to strike the right balance between size, feature set, and ease of use. Even after using it for only a few days I felt comfortable.

jQuery offers tremendous productivity gains, and it’s easy to learn and work with. It’s one of those tools that has drastically changed how I think about client-side development, and frankly, it has helped improve my skill set significantly. It’s also made me more productive and given me the confidence that I can tackle complex UI and front-end logic in JavaScript reliably.

jQuery is so intuitive, useful, and practical that it feels criminal to write JavaScript code without it.

Key Features of jQuery

To evaluate jQuery, you should look at these key features:

  • DOM Element Selectors: jQuery Selectors let you select DOM elements so that you can apply functionality to them with jQuery’s operational methods. jQuery uses a CSS 3.0 syntax (plus some extensions) to select single or multiple elements in a document. You’re probably already familiar with the CSS syntax from HTML styling. Even if you’re not, it’s fairly easy to pick up the key CSS selector features. I’ll go as far as saying that jQuery is the reason I really started to grok CSS. Using CSS syntax you can select elements by ID, CSS class, attribute filters, or by their relationship to other elements. You can even chain filter conditions together. Look at this simple example, which selects all second-column TD elements in a table using a simple selector: $(“#gdEntries td:nth-child(2)”).
  • The jQuery Object: The Wrapped Set: Selectors return a jQuery object known as the “wrapped set,” which is an array-like structure that contains all the selected DOM elements. You can iterate over the wrapped set like an array or access individual elements via the indexer ($(sel)[0] for example). More importantly, you can also apply jQuery functions against all the selected elements.
  • Wrapped Set Operations: The real power of the wrapped set comes from applying jQuery operations against all selected DOM elements simultaneously. The jQuery.fn object exposes about 100 functions that can operate on wrapped sets, and allows you to manipulate and retrieve information from the selected DOM objects in a batch. For example, you can easily manipulate all alternate rows in a table by adding a CSS class with $(“#gdEntries tr:odd”).addClass(“gridalternate”). jQuery applies the .addClass() function against each of the matched elements with one command. Intuitive methods such as .css() let you get and set CSS styles directly, and include smart logic that accounts for browser differences in assignment types (number and string translations mostly) and values (for example, opacity does the right thing on all browsers, even though different browsers treat it differently). You can set and retrieve attributes with .attr(), or retrieve or set values with .val(), .text(), or .html(). You can clone selected DOM elements or create new elements from HTML text used as a selector and inject them into the document with methods such as .appendTo() and .prependTo(). You can find and use a parent element to which to .append() or .prepend() the new or selected element(s). You can apply some basic but useful effects methods to .show() and .hide() elements in an intelligent manner that checks for opacity, display, and visibility, and adjusts all those values to show or hide elements. Remember, you can do all of this (and much more) against all of the selected elements in a wrapped set.
  • Most wrapped set operations are also chainable; they return the jQuery wrapped set object as a result. This means you can chain many methods together in a single command. Effectively, this means you can select once and subsequently operate many times against the same object. You can even filter or expand the wrapped set with methods such as .find(), .filter(), or .add(). The beauty of many of these functions is that they do things you actually want to do, and they are intuitively overloaded. For example the .val() and .text() methods work for both retrieval (getter) and setter methods. Methods that deal with numeric values can accept either text or numeric values. jQuery automatically fixes CSS assignments to browser-dependent tags. Although the number of functions provided by jQuery is relatively small, many of the functions provide overloaded functionality to perform intuitive behaviors. The end result is that you have a relatively small API to learn, but one that provides a broad range of functionality.
  • Simplified Event Handling: Much of what you do in JavaScript code from DOM manipulation to AJAX calls is asynchronous, and requires using events. Unfortunately, DOM implementations for event handling vary considerably between browsers. jQuery provides an easy mechanism for binding and unbinding events and a normalized event model that makes it easy to handle events and hook up result handlers for all supported browsers. jQuery calls all event handlers in the context of the element that caused the event (i.e., the this pointer). The handlers receive a consistent fixed-up and browser-normalized event object.
  • Small FootprintjQuery is a fairly compact base library, yet it’s packed with features you’ll actually use. During my relatively short time using jQuery, I’ve gone through well over 85% of the jQuery functions with my code, which points at how useful the library is. All this functionality ends up in a compressed size of just around 16 KB (94 KB uncompressed with comments). For that you get selectors, a whole slew of operations that you can perform on the wrapped set, DOM normalization for most browsers, AJAX functionality, a host of utility functions for object/array manipulation, and a number of basic effect functionality. Given my high utilization of jQuery, this 16 KB of script download provides a tremendous amount of “Bang for the Buck.”
  • Easy Plug-in Extensibility: When jQuery’s language and DOM extension library features aren’t enough, jQuery provides a simple plug-in API that has spawned hundreds of plug-ins for almost every conceivable common operation you might think up to perform on a set of DOM elements. jQuery’s API allows extending the core jQuery object’s operations simply by creating a function and passing the jQuery wrapped set as a parameter, which lets plug-ins operate on it and participate in jQuery chaining. This simple but powerful plug-in model is likely the key to both the large number of plug-ins exist and the reason why jQuery has become so popular so quickly. If you need some specialty functionality, chances are that a plug-in already exists with the functionality you’re looking for?and if it doesn’t, it’s easy enough to create it yourself or extend an existing plug-in.

Ok, so now that you have some idea of what jQuery provides, I’ll show you how to take it for a spin. This article introduces the jQuery concepts of document manipulation purely from a client-side perspective. A follow-up article will discuss how to use jQuery in combination with ASP.NET on the server for AJAX callbacks, and how to integrate jQuery with server-side controls and components.

Editor’s Note: This article was first published in the January/February 2009 issue of CoDe Magazine, and is reprinted here by permission.

Adding jQuery to Your Page

jQuery is a client script library and so you have to add a script reference to your page. You can download the latest version of jQuery from the jQuery site.

You can include jQuery in your ASP.NET pages in a number of ways:

  • Reference a local copy via a
    Author's Note: If you're interested in more info on the pros and cons of the various script inclusion methods, check out this blog post.

    Getting Started with Selectors

    Selectors make jQuery useful. You start by selecting content, and then applying a jQuery function to it. You can select a single element:

       $("#gdEntries")       .css("border", "solid 1px navy");

    Or select by CSS class (.):

       $(".gridalternate")       .css("background", "lightsteelblue ");

    You can also select by multiple CSS selectors separated by commas. The following example matches elements that belong to either of two CSS classes:

       $(".gridrow,.gridalternate")       .attr("tag", "row");

    You can also select elements by tag name (e.g. div, tr, input, etc.) and apply filters to the elements. The following code selects all input elements that are buttons, and attaches a click handler to them:

       $("input:button,input:submit")       .click(function(event){              alert("clicked");        });

    A more complex selector might select all rows in a table:

       $("#gdEntries>tbody>tr")       .css("border", "solid 1px red");

    If you're at all familiar with CSS, all this should look familiar: basically, you can use the same syntax selectors CSS style sheets use (with the exception of the :button filter shown above?jQuery adds a number of custom filters) to select elements. The idea is simple: Use what you already know rather than some custom syntax. CSS element selection is quite powerful and?given a reasonably thoughtful HTML layout?makes selecting just about any element or group of elements in a document easy.

    If you're rusty on CSS, Table 1 shows a few of the common jQuery/CSS selector types that you can apply (see the full list).

    SelectorExampleDescription
    Element$("td")Selects an HTML element tag.
    #id$("#divMessage")Selects an element by its ID.
    .cssclass$(".gridalternate")Selects a CSS style.
    selector,selector$("input:button,input:text")You can combine multiple comma-separated selectors into a single selection.
    ancestor descendant$("#divMessage a")A space between selectors/elements/tags finds nested elements. This syntax is similar to CSS ancestor descendant syntax.
    parent > child> child$("p>b")Matches all immediate children of an element or selector expression that match the right element/selector.
    prev ~ siblings~ siblings$("#row_11:nth-child(2)~td")$("~td")Matches the next siblings at the sample level as the preceding expression. The example matches columns 3-N of a table row. Best used as a find() or filter() against an existing jQuery instance.
    prev + nextsibling+ nextsibling$("#tdMoneyCol+td")$("+td")Matches the following sibling. Works best with find() or filter() against an existing jQuery object.
    :filter$("input:button")The colon (:) applies filters to the query. jQuery supports CSS 3 filters as well as a number of custom filters.Examples: :not, :button, :visible, :hidden, :checked, :first,nth-child(1), :has, :is, :contains, :parent
    [attribute]$("p[id^=pk_"]Selects an attribute of an element= equals string
    ^= startswith
    $= endswith
    *= contains

    Selectors can get quite sophisticated. For example, the following code selects from a table named gdEntries all the cells whose ID begins with pk, and then sets their width to 30:

       $("#gdEntries>tbody>tr>td[id^=Pk]").width(30);

    Cool eh? You get a lot of control over picking up the exact elements you are looking for.

    A More Complete Example

    This next concrete example is an ASP.NET page that uses jQuery on the client side to fix up the page, highlighting some of jQuery's features. The following page contains a GridView to display a plain table loaded from data. I'm going to add jQuery and a bit of code to manipulate the page. You can follow along if you like by downloading the code accompanying this article. Listing 1 shows the abbreviated ASP.NET page (you'll also find a plain HTML file in the sample download).

    This example applies minimal formatting to the GridView, so when you run the page you get a plain black and white grid as shown in Figure 1.

    ?
    Figure 1. Plain Grid: The initial example shows a page with a plain black and white grid.

    Now, you'll see how to use jQuery to select elements to both make the table look a little nicer and add some interactivity. First apply an alternating row effect to the table rows. To do that, add a script tag to the bottom page just above the

    tag like this:

    ?
    Figure 2. jQuery-Applied Striping Effect: These alternating stripes were applied using selectors and .addClass().
     

    Figure 2 shows the alternating row-striping effect.

    The jQuery()/$() Object

    Take a look back at the script block and notice the $ symbol, which is an alias to the jQuery object; you can use the two interchangeably. So the code could have used jQuery(document) or jQuery("#gdEntries") just as easily as $(document) or $("#gdEntries"). The jQuery object takes a selector as a parameter. Typically selectors are expressed as strings, but you can also pass DOM elements or another jQuery object.

    For example, $(document) passes a document DOM element and results in a single element wrapped set that contains the document object. $("#gdEntries") selects the gdEntries table on the page where # is the ID identifier?just as in CSS.

    The $(document).ready() Handler

    Notice the use of the $(document).ready() handler. .ready() is a jQuery event handler, and your first exposure to how to handle events with jQuery. This particular event handler fires when you can access the document and when scripts have completed loading. Including it in the code ensures that your code can access all the page's DOM elements and script code reliably. You can place a .ready() handler anywhere on the page; you can even have multiple .ready() handlers in a single page.

    Although a .ready() handler is optional and not always required, for consistency and reliability's sake it's best to always wrap any script "startup code" into a .ready() handler to ensure consistent operation on all browsers.

    jQuery implements the handler using an anonymous function, which means it declares the handler right inline with the .ready() function call:

       $(document).ready( function {...} );

    This is a common practice with jQuery, because inline functions are an easy way to write the short handler code that is so common in JavaScript. There are other advantages to inline functions, but for now, just note that inline anonymous functions are a common way to write event handlers (you'll see more of them shortly). You can also pass a function pointer instead of an anonymous function:

       $(document).ready( onLoaded );   ... more script here    function onLoaded(e) { ... }

    Selectors Again

    Look back at the grid example code that performs the selection of the rows in the example. $("#gdEntries tbody tr:even") selects all the even rows in the grid view's table output. In plain English, the selector means: select the gdEntries element (#) and find any child tbody elements (a space=child) find any tr elements below those (again, a space=child) and then return a filtered set(:even=filter) that includes only the even elements.

    The selector result is a wrapped set of all even rows in the table. The .addClass("gridalternate") function applies the .gridalternate CSS style to each matched element, which gives it its new highlighted appearance. Finally the .css function applies some custom styling to each matched element. Both .addClass() and .css apply their operations against all the matched elements at once.

    Selectors Often Select More Than You Expect

    If you're following along and running the sample code, you've probably noticed that while the example works to highlight even rows, there's also a funky side effect in the table: If you look at the pager?which ASP.NET renders as a table?you'll find that its alternating rows have also been marked up with the .gridalternate class. The problem is that ASP.NET's GridView rendering is, um?rather imprecise. It doesn't render proper tbody and tfoot tags to delineate table headers and footers, so the selector query is too broad, because it also matches the child elements, which include the child Pager table in the footer.

    There are a number of ways to fix this to make it work properly. First, you can apply a filter to the selection by excluding certain elements:

       $("#gdEntries tbody tr")       .not(":first,:last")       .filter(":even")                   .addClass("gridalternate")       .css("border","solid 1px lightgrey");

    So instead of filtering on all odd rows this code first filters the rows and omits the first and last rows (the header and the footer). It then grabs only the even items out of the remaining rows. Another (even easier) way is to select only direct children using the greater-than (>) operator rather than using the space, which selects all children:

       $("#gdEntries>tbody>tr:even")

    Another common and efficient way to filter elements that offers the developer the most control is via CSS class matching. You can add CSS classes or mere "marker" classes that have no matching CSS class to elements in markup.

    Take another look at the way the GridView's rows are rendered:

        

    This adds a CSS class griddatarow to each row rendered in the HTML. Although the griddatarow CSS class doesn't actually exist, jQuery can still match on it in the selector expression. So the following code works:

       $("#gdEntries tr.griddatarow:odd")

    Or even simpler:

       $(".griddatarow:odd")

    The simpler version works?but the first is preferable even if it is more verbose. It's best to be as specific as possible to optimize jQuery selection in the document. The former finds the gdEntries table and then searches for children only in it, while the latter has to parse the entire document. The fewer elements matched by the first selector filters the fewer elements jQuery has to iterate over. Be precise if possible.

    Suppose you wanted to select the second cell of the third row in the grid:

       $("#gdEntries>tbody>tr:nth-child(3)" +     ">td:nth-child(2)")          .addClass("gridalternate");

    Or maybe all the cells in the second column:

       $("#gdEntries>tbody>tr>td:nth-child(2)")      .addClass("gridalternate")

    Easy! Think about how cool this is: If you had to do this manually via script code this would be a fairly complex task, but using a selector it requires only a single line that is fairly easy to write?and more importantly?easy to understand at a glance.

    The Wrapped Set

    The result of $("#gdEntries>tbody>tr:even") is a jQuery wrapped set object, which is an array-like structure that has a length property and an array containing all the matched elements. You can reference the DOM elements in the wrapped set directly:

       var res = $("#gdEntries>tbody>tr:even");      var len = res.length; // match count. 0 = no matches   var el = res[0];      // first element   el = res.get(0);      // same but 'official' syntax

    The jQuery constructor always returns an object result, so even if the selector finds no matches it returns an object. You can check the .length property to determine whether the selection returned matches.

    After obtaining a set of matches you can iterate through them manually using a for loop or use the wrapped set function .each() to iterate over the list. What's nice about .each() is that it assigns the this pointer to the selected DOM element; in other words, you can do this:

       $("#gdEntries tbody tr:even").each(       function(index) {         alert(this.id + " at index: " + index);       });

    Wrapped Set Functions

    Manual iteration is useful at times, but more commonly you'll use one or more of jQuery's 100+ operational functions on wrapped set elements. The jQuery selector applies both the .addClass() and .css() functions you saw above against all the elements that it matches to those you created with the jQuery() or $() function?in this case each of the rows you selected. You can find all the jQuery functions on the API reference page, or if you're into terse lookup sheets you can check out this printable jQuery cheat sheet.

    Function Chaining

    jQuery lets you use a fluent interface for most function calls, which means that you can chain many jQuery function calls together in a single command. You've already seen some examples:

       $("#gdEntries tbody tr")       .not(":first,:last")       .filter(":odd")        .addClass("gridalternate")       .css("border","solid 1px lightgrey");

    This syntax chaining works because most jQuery functions return a matched set as a result. For example, .addClass and .css both return the original matched set they acted upon. Other functions such as .not and .filter modify the original matched set and return a new matched set. There's also an .end() function that you can use to remove any filters and return to the original matched set that you specified at the beginning of the chain.

    Chaining is a great way to keep code compact?but it's optional. If you prefer, you can write more traditional code, for example:

       var matches = $("#gdEntries>tbody>tr");   matches.addClass("gridheader");   matches.removeClass("gridhighlight");

    Chained statements can sometimes be difficult to debug, because you get a single result value at the end. In contrast, using explicit methods lets you see changes after each step, which can be useful for debugging. For I often break up commands for debugging and chain them back together after they're fully debugged.

    Still, not all jQuery functions can be chained. Functions such as .val(), .text(), and .html() return string values; .width() and .height() return numbers; and .position() returns a position object. If you study the jQuery function list it should be pretty clear what the result types of most functions are. Typically, functions that you would expect to return a void result return a matched set?all others return values or data.

    Event Handling

    Event handling is one of jQuery's nicest aspects because it makes the process easy and consistent across browsers. jQuery provides the high level .bind() and .unbind() functions that generically attach and detach event handlers to and from matched set elements. That makes it easy to attach events to many objects at once?such as the click handler attached to the rows in the example shown earlier. In addition, most common events such as click, keystroke, and mouse events have corresponding dedicated handler functions: .click(), .mousedown(), change(), and .keydown(). jQuery event handlers take a function as a parameter. jQuery tracks these handlers so that it can unbind them later. Coding doesn't get any easier than this. There's even an option to name event handlers uniquely so that you can remove them in a consistent way:

       $("#gdEntries tr")      .bind("click.MyHandler",function(e) {...});      ...   $("#gdEntries tr")      .unbind("click.MyHandler");

    The extra name following the event name in the preceding example lets you specifically identify an individual event handler. In contrast, the default behavior would remove all event handlers for an event that were assigned with .bind() or the various convenience handlers such as .click().

    jQuery also includes:

    • .one(): This fires an event exactly once, and then disconnects the handler.
    • .toggle(): This toggles between alternating clicks
    • .trigger(): This can trigger events on elements through code.

    jQuery's common model for event handlers includes these features:

    • The this pointer always equals the element the event fired on.
    • jQuery always passes an event object as a parameter.
    • The event object is cross-browser normalized.

    Building On the Example

    Suppose you wanted to extend the earlier grid example by highlighting grid rows when a user hovers over them with the mousse?regardless of which browser the client is using. The following code uses jQuery's .hover() event function, which fires repeatedly as a user hovers over an element. The .hover event function takes both entry and exit event function handlers as parameters.

    For example, change the previous selector chain to:

       $("#gdEntries>tbody>tr")       .not(":first,:last")       .hover( function() {          $(this).addClass("gridhighlight");       }, function() {                           $(this).removeClass("gridhighlight");       })      .filter(":even")      .addClass("gridalternate");

    Note that the example re-arranges the sequence of the original operations a little so that it applies the hover behavior to all rows. After that, it filters the list to include only the even rows so it can apply the alternating style.

    The .hover() function is an example of a jQuery event handler. It happens to be a special one, because unlike most it takes two callback functions as parameters. The first callback is for the mouseenter operation, and the second for the mouseout operation. jQuery handles .hover() intelligently, detecting only "real" mouseout events when the mouse leaves the container (unlike the DOM mouseout event which also fires when entering child elements).

    Running the page now causes an orange highlight to appear for each row that the mouse hovers over.

    This next example handles clicks on the row, popping up a dialog that shows the value in the third column, using the following .click() handler:

       $("#gdEntries>tbody>tr")       .not(":first,:last")       .hover( function() {           $(this).addClass("gridhighlight");       },function() {           $(this).removeClass("gridhighlight");       })       .click( function(e) {           alert("Amount: " +                $(this).find("td:nth-child(3)").text());       })      .filter(":even")      .addClass("gridalternate");
    ?
    Figure 3. Interactive Grid: Here's how the grid looks after adding hovering and click event handling.

    This code hooks up a click handler to every row in the matched set. Figure 3 shows what the grid looks like now. Remember, jQuery always calls event handlers in the context of the element that caused the event?so this points at the row DOM element. You can turn this into a jQuery object $(this) and then apply further filtering to this single element to find the third child cell and display its content.

    Displaying the amount in column three is not all that useful though. A more common scenario is for navigation where a click navigates to a different page or performs some AJAX request based on the selected row. Unfortunately the ASP.NET GridView doesn't provide any sort of ID or context on the client side, so it's not easy to take a context-based action. To navigate or post data in some sort of useful context, you have to take matters into your own hands. It's pretty easy to fake GridView context though, knowing that you can use jQuery to easily access document content. You simply embed an ID value into the generated content of one of the cells. The grid already has a template column so I'll change it to:

                   
    <%# Eval("pk") %>
    <%# Eval("Title") %>
    <%# Eval("Description")) %>

    That change embeds the PK value into the generated output. You can then handle the .click() event by retrieving that PK value in the second column, looking at the first

    tag and getting its text:

       .click( function(e) {   var pk = $(this)      .find("td:nth-child(2) div:first-child")      .text();      window.location=         "someotherpage.aspx?id="+ pk;   })

    Using jQuery and its flexible selector syntax, you can easily store data in the HTML and the DOM, putting your state content directly into the HTML, and then getting at it via selectors, as in this example. It also makes it possible to extend even an inflexible component such as the GridView, by making it more dynamic. Approaches like this can completely change the way you think about the purpose of client UI and interactivity. For example, you can do in-place editing of content in the browser and then pick up that content later to post to the server via AJAX or postback. The possibilities are endless.

    Creating New HTML and Embedding It into the DOM

    You're not limited to acting on existing DOM objects. You can also create jQuery objects by passing an HTML string as the parameter to the jQuery object. Creating HTML and adding it to the document is as easy as:

       $("
    ") .attr("id","_statusbar") .addClass("statusbar") .appendTo(document.body)

    You can inject HTML anywhere in the document with the .appendTo() or .prependTo() functions, which insert into the child items of the selector element(s). Or you can use .append() and prepend()to insert after or before the currently selected element instead.

    You can also .clone() existing nodes easily. Try this on the example above:

       $("#gdEntries").clone()      .appendTo(document.body)      .attr("id","gdEntries2");

    When you do that, you'll see the entire GridView duplicated in the page. That may not seem terribly useful, but there are a number of situations where cloning makes sense. For example, you can use cloning for a sort of templating mechanism. To insert a new row with new content in the table above you can use code like this:

       var row =       $("#gdEntries>tbody>tr:nth-child(2)").clone();   row.find("td:first-child")      .text(new Date().formatDate("MMM dd));   row.find("td:nth-child(2)")      .html(new Date().formatDate("hh:mm:ss"));   row.find("td:last-child")      .text( 199.99.formatNumber("c"));   row.insertBefore(      "#gdEntries>tbody>tr:nth-child(2)")      .show();
    Author's Note: The format functions are helper functions and are also provided with the downloadable source. The nice thing with this approach is that you don't have to create HTML on the client-you're only injecting text or data and maybe a little markup rather than HTML strings. The HTML structure comes from the cloned DOM element and you're only "filling in the blanks."

    Content doesn't have to pre-exist either. You can load "template" content from non-visible HTML elements on the page or embedded inside of a script block.

    The example above is simplistic because it simply injects static content, but you could just as easily retrieve the content from an AJAX callback or form input fields and load that into the cloned "template."

    Templating is a powerful topic and you'll see more in the follow-up article, which looks at a couple of different templating tools you can use in combination with jQuery. (If you're eager to check it out now, look at jTemplates or John Resig's cool Micro-Template engine.)

    jQuery Effects and Some UI Features

    One nice jQuery touch is that it includes a few simple yet very effective effects methods. For example, to add a little pizzazz to those new elements added to the document in the last example, instead of calling .show() you could fade the elements in with simple code like this:

       row.insertBefore(      "#gdEntries>tbody>tr:nth-child(2)")      .css("background", "paleyellow")      .hide()      .fadeIn(1000);

    The .fadeIn() and .fadeOut() functions do exactly as their name implies; they provide an easy way to supply a visual clue that something on the page is changing. Other methods include .slideUp() and .slideDown() which slide content from the top or bottom to the current absolute position, with speed indicators. For lower-level animation there's the .animate() function, which allows you to animate numeric CSS properties to a new value over a specified duration. In addition, animation plug-ins are available that provide all sorts of fade and transition effects. It's pretty impressive what you can do with very little effort, but even the basic built-in functions do a lot to liven up a web page. Just remember to keep it simple and not just add noise to the page.

    A Popup Window Example

    jQuery can also help create utilitarian UI features. This next example adds a pop-up window so you can dynamically type selectors that select and highlight matched elements in the entire page. The end result is shown in Figure 4.

    ?
    Figure 4. Pop-Up Window: The figure shows a dynamic pop-up window with some added effects.

    A user pops up the "dialog" by clicking the Show Query Dialog window, which "slides in" the dialog from the top of the page and displays it with a slightly transparent background on top of the page content. You can then type a jQuery selector in the text area to apply it against the document. When you click the button, the selected elements are highlighted in dark red in the document and then the highlight is removed after a few seconds.

    The dialog is stored on the page as a simple styled

    element that looks like this in markup:

       
    Enter a jQuery Expression
    Expression:

    There's also some CSS styling for the dialog that makes it transparent, initially hidden, and absolutely positioned on the page:

       #divjQueryDialog   {     width: 380px;     position: absolute;      left: 100px;      top: 70px;     display: none;   }

    The code that makes the panel visible, applies opacity, and handles the matching and selection is relatively simple (see Listing 2).

    The code initially makes the div visible and applies opacity. It then hooks up a click handler to the button and an anonymous function to handle the click processing. That code captures the input text and then uses jQuery to "evaluate" the selector, applying a special highlight class to the matched set. The code also sets up a timeout that removes the highlight class after 5 seconds.

    I mentioned nesting anonymous functions earlier and the code in Listing 2 shows another example?it's quite asynchronous, yet it's written in a single block of code that keeps context. The inner click handler has access to the dialog without having to reselect it. This is due to the way JavaScript handles nested closures or function blocks, which can see private variables defined higher up in the function definition stack?even if the code is asynchronous. In this example this is trivial, but if the fade out code (or "class") contained more state, it would be quite convenient to have that state passed to the handler merely by virtue of being declared inside of the parent closure. This type of coding can often be easier to read as it keeps related logic together in one place.

    A couple of other things to highlight here: Notice the code to make the dialog transparent:

       dialog.hide()     .slideDown("slow")     .css("opacity",.90);

    jQuery is smart about how it handles certain CSS tags that browsers handle differently, such as opacity. Internet Explorer doesn't support the opacity style (because it's not officially part of the CSS 2.1 standard), but jQuery automatically does the right thing; it applies a filter style for Internet Explorer. jQuery applies similar rules for other CSS properties and certain functions. For example, .text() retrieves text consistently and .scrollLeft/Top retrieves scroll position safely. So jQuery acts as a normalizer between different browser implementations.

    One last enhancement: If you wanted to make the window draggable so users could move it around over the page, that's easy if you use the draggable plug-in from jQuery UI. To use this plug-in, download jQuery UI and dump the library into your scripts folder (relevant portions provided in the sample). Then add the scripts to the page:

      

    Then you can write:

       var dialog = $("#divjQueryDialog");    dialog.draggable();      

    That's it. You've just made your "window" a lot more window-like by adding the draggable plug-in, and writing a couple of lines of code. There are a number of options you can apply to draggable. For example, to set the drag handle to the window's header you can use:

       dialog.draggable({ handle:       $("div.gridheader") });  

    Doing that makes only the header initiate a drag operation, which is more natural window behavior.

    Plug-ins for Everything Else

    Plug-ins are one of the main reasons that jQuery has become so popular. This is partly because jQuery has an extremely simple plug-in API. There is a jQuery.fn property which hosts any operational functions that can operate on the matched set. By simply implementing a new function on jQuery.fn you effectively create a new matched set function that works just like jQuery's built-in functions. Here's a very simple pulse plug-in that pulses an element by fading it out partially and then fading it back to full visibility:

       $.fn.pulse = function(time)    {     if (!time)       time = 2000;     // this == jQuery instance        this.fadeTo(time,0.30, function() {         $(this).fadeTo(time,1);      });     return this; // return jQuery   }

    You can now simply use a jQuery instance and the pulse function on it to pulse matched elements. For example, earlier you added a new item to the grid. You could pulse this item instead of fading it in by writing:

       row.insertBefore(     "#gdEntries>tbody>tr:nth-child(2)")     .css("background", "paleyellow")     .pulse(2000);

    This plug-in also works against multiple matched elements. The following example pulses the even rows in the grid:

       $("#gdEntries>tbody>tr")     .filter(":even")     .addClass("gridalternate");     .pulse(1000);      

    A plug-in receives a this pointer that is the jQuery matched set. So you can apply further functionality to the matched set in the plug-in code. If the plug-in doesn't have a distinct return value you should always return the original jQuery result set to make the function chainable, for example:

       $("#gdEntries>tbody>tr")      .filter(":even")      .addClass("gridalternate");      .pulse(1000)      .addClass("small");      

    This plug-in is simplistic. More commonly, you need a little bit of set-up data as well some logic to loop and manipulate the matched elements individually. The following code shows a slightly more realistic plug-in that centers any elements in the browser window or inside of another element.

       // This jQuery centerInClient plug-in    // centers content in the window   $.fn.centerInClient = function(options) {     var opt = {        container: window,           completed: null     };     $.extend(opt, options);     return this.each(function(i) {       var el = $(this);       var jWin = $(opt.container);       var isWin = opt.container == window;       // have to make absolute       el.css("position", "absolute");       // height is off a bit so fudge it       var hFudge = 2.2;       var x = (isWin ? jWin.width() :           Win.outerWidth()) / 2 - el.outerWidth() / 2;       var y = (isWin ? jWin.height() :           jWin.outerHeight()) / hFudge -           el.outerHeight() / 2;       el.css({ left: x + jWin.scrollLeft(),          top: y + jWin.scrollTop() });       var zi = el.css("zIndex");       if (!zi || zi == "auto")         el.css("zIndex", 1);       // if specified make callback and pass element       if (opt.completed)         opt.completed(this);     });   }

    This plug-in includes a couple of common patterns. First it has a set of option parameters that you can pass in as an object map (i.e., {container: "#divContainer"} ). Notice that opt is defined as a private variable and then extended with the option parameter object. $.extend() is a wonderful utility function that extends an object with the content of another object. This makes parameter processing easy, because you need to pass in only the parameters you're interested in. Most plug-ins accept options parameters in this fashion and it's a good idea to implement options in your own plug-ins.

    The next common plug-in pattern is to return this.each( function() {...}); which ensures that the jQuery matched set gets returned. The .each function iterates through each of the matched DOM elements individually, letting you operate against them one at a time. Inside the .each() function handler, the this pointer is always the DOM element iterated over. You can use $(this) to get a jQuery instance for that element, which is typically what you want to manipulate the element.

    Because it's so easy to create plug-ins, I've ended up creating quite a few myself for both my own and for shared use. The simple plug-in model is a genius concept: It lets jQuery stay small, including only a core set of features while still exposing all the functionality of the core library to any extensions. It's a model more API vendors should embrace and execute as nicely as jQuery has.

    What's Not to Like?

    If all this sounds like a glowing commercial for a tool, you can be assured that I'm just a happy user who stumbled onto this library some time ago and fell in love with it immediately. I tend to be very critical of the tools I use, but I have yet to have any serious complaints about jQuery. This library just works for me and it works the way I like to work.

    However, the jQuery library is not "the perfect tool," and it doesn't solve every possible JavaScript and DOM problem for you. Still, in over a year of using it, I haven't run into a showstopper problem. You may still need a set of a few helper functions to help with non-DOM related functionality. For example, I still use my old JavaScript library to get functionality such as date and number formatting, windowing support, and a host of other features. That's unlikely to ever go away. But I can simply toss out large parts of my old library because jQuery replaces its functionality?in most cases much more elegantly than my own code did.

    To be honest I'm grasping to find fault with jQuery for balance here because my editor mentioned I should mention the downsides. Sorry?you'll have to dig up your own dirt. Good luck with that. A quick search around the web seems to confirm my feelings?there's not a lot of negative content on jQuery. But you can judge for yourself by trying it out.

    Coming Soon: jQuery, AJAX, and ASP.NET Server Integration

    You've seen jQuery's core functionality for DOM manipulation and JavaScript extensions on the client-side, but I haven't covered AJAX and server-side integration with ASP.NET, so that'll be the focus of a follow-up article. You'll see jQuery's AJAX functionality and how to use ASP.NET as a back end to receive AJAX calls in a variety of ways: plain page callbacks, REST calls, and WCF. You'll also see how to integrate ASP.NET controls with jQuery or jQuery plug-ins in combination with server-side components and how to manage jQuery scripts in general in your ASP.NET applications.

    Until then, check out jQuery for yourself. You won't be disappointed.

    devxblackblue

    About Our Editorial Process

    At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

    See our full editorial policy.

    About Our Journalist