Powered by Blogger.

Wednesday, August 18, 2010

The Total Newbie’s Guide to jQuery: Select Elements and Manipulate CSS with jQuery

This article, as well as a follow-up article coming next week, are excerpts from Chapter 2 of the new SitePoint book, jQuery: Novice to Ninja, by Earle Castledine and Craig Sharkie. You can grab the entirety of Chapter 2, as well as Chapters 1 and 7 and the complete code archive for the entire book for free here. Together, these two articles constitute an introduction to jQuery for designers who’ve only ever worked with CSS and HTML.
If you’ve been wanting to learn the basics of jQuery and start adding some dynamic interactions to your website, this is the place to start. If you’d like to follow along with the code in this article, download the sample, which includes all of the code examples from the book.

The Scenario

“In phase two, we are going to want to harness the social and enable Web 2.0 community-based, crowd-sourced, Ajax, um, interactions,” says our new client. “But for now we just need some basic stuff changed on our site.
Our client is launching a startup called StarTrackr! It uses GPS and RFID technology to track popular celebrities’ exact physical location—then sells that information to fans. It’s been going great guns operating out of a friend’s local store, but now they’re taking the venture online.
“Can you do it? Here’s a list that needs to be live by Friday, close of business.”
You survey the list. By amazing coincidence you notice that all of the requests can be implemented using jQuery. You look at your calendar. It’s Friday morning. Let’s get started!
The first task on the list is to add a simple JavaScript alert when the existing site loads. This is to let visitors know that StarTrackr! is not currently being sued for invasion of privacy (which was recently implied in a local newspaper).
Sure, we could use plain old JavaScript to do it, but we know that using jQuery will make our lives a lot easier—plus we can learn a new technology as we go along! We already saw the anatomy of a jQuery statement in Chapter 1; now let’s look at the steps required to put jQuery into action: we wait until the page is ready, select our target, and then change it.
You may have probably guessed that jQuery can be more complicated than this—but only a little! Even advanced effects will rely on this basic formula, with multiple iterations of the last two steps, and perhaps a bit of JavaScript know-how. For now, let’s start nice and easy.

Making Sure the Page Is Ready

Before we can interact with HTML elements on a page, those elements need to have been loaded: we can only change them once they’re already there. In the old days of JavaScript, the only reliable way to do this was to wait for the entire page (including images) to finish loading before we ran any scripts.
Fortunately for us, jQuery has a very cool built-in event that executes our magic as soon as possible. Because of this, our pages and applications appear to load much faster to the end user:

Example 1. chapter_02/01_document_ready/script.js
$(document).ready(function() {
  alert('Welcome to StarTrackr! Now no longer under police …');
});


The important bits here (highlighted in bold) say, “When our document is ready, run our function.” This is one of the most common snippets of jQuery you’re likely to see. It’s usually a good idea to do a simple alert test like this to ensure you’ve properly included the jQuery library—and that nothing funny is going on.
important: You’ll Be Seeing $(document).ready() a Lot!
Almost everything you do in jQuery will need to be done after the document is ready—so we’ll be using this action a lot. It will be referred to as the document-ready event from now on. Every example that follows in this book, unless otherwise stated, needs to be run from inside the document-ready event. You should only need to declare it once per page though.
The document-ready idiom is so common, in fact, that there’s a shortcut version of it:
$(function() { alert('Ready to do your bidding!'); });
If you’d like to use the shortcut method, go ahead! The expanded version is arguably a better example of self-documenting code; it’s much easier to see at a glance exactly what’s going on—especially if it’s buried in a page of another developer’s JavaScript!
At a cursory glance, the document-ready event looks much removed from the structure we encountered back in our jQuery anatomy class, but on closer inspection we can see that the requisite parts are all accounted for: the selector is document; the action is ready; and the parameter is a function that runs some code (our alert).

Selecting: The Core of jQuery

Time is ticking, and deadlines wait for no one. The client has noted that people have been having quoting incorrect celebrity IDs from the web site. This is because the celebrities’ names are all laid out in one big table and it’s difficult for users to line up a celebrity with the correct reference ID. Our client tells us that he wants every other row to be a light gray color so the users can easily find their favorite celebrity.
We have jQuery ready to do our bidding—it just needs us to choose a target for it. Selecting the elements you want to modify on the page is really the art of jQuery. One of the biggest differences between being a novice and ninja is the amount of time it takes you to grab the elements you want to play with!
You might remember from our jQuery anatomy class that all of our selectors are wrapped in the jQuery function:
jQuery()
Or the alias:
$()
We’ll be using the shortcut alias for the remainder of the book—it’s much more convenient. As we mentioned earlier, there’s no real reason to use the full jQuery name unless you’re having conflict issues with other libraries (see ???).

Simple Selecting

Our task is to select alternate table rows on the celebrity table. How do we do this? When selecting with jQuery, your goal should be to be only as specific as required: you want to find out the most concise selector that returns exactly what you want to change. Let’s start by taking a look at the markup of the Celebrities table, shown in Figure 1, “class and id attributes in the HTML page”.
Figure 1. class and id attributes in the HTML page
class and id attributes in the HTML page

We could start by selecting every table row element on the entire page. To select by element type, you simply pass the element’s HTML name as a string parameter to the $ function. To select all table row elements (which are marked up with the tag), you would simply write:
$('tr')
important: Nothing Happens!
If you run this command, nothing will happen on the page. This is expected—after all, we’re just selecting elements. But there’s no need to worry; soon enough we’ll be modifying our selections in all sorts of weird and wonderful ways.
Similarly, if we wanted to select every paragraph, div element, h1 heading, or input box on the page, we would use these selectors accordingly:
$('p')
$('div')
$('h1')
$('input')
But we don’t want to change every table row on the celebrity page: just the rows in the table that have the celebrity data. We need to be a bit more specific, and select first the containing element that holds the list of celebrities. If you have a look at the HTML and at Figure 1, “class and id attributes in the HTML page”, you can see that the div that contains our celebrity table has an id of celebs, while the table itself has a class of data. We could use either of these to select the table.
jQuery borrows the conventions from CSS for referring to id and class names. To select by id, use the hash symbol (#) followed by the element’s id, and pass this as a string to the jQuery function:
$('#celebs')
You should note that the string we pass to the jQuery function is exactly the same format as a CSS idids should be unique, we expect this to only return one element. jQuery now holds a reference to this element. selector. Because
Similarly, we can use a CSS class selector to select by class. We pass a string consisting of a period (.) followed by the element’s class name to the jQuery function:
$('.data')
Both of these statements will select the table but, as mentioned earlier when we talked about the DOM, a class can be shared by multiple elements—and jQuery will happily select as many elements as we point it to. If there were multiple tables (or any other elements for that matter) that also had the class data, they’d all be selected. For that reason, we’ll stick to using the id for this one!
tip: Can You Be More Specific?
Just like with CSS, we can select either $('.data') or the more specific $('table.data'). By specifying an element type in addition to the class, the selector will only return table elements with the class data—rather than all elements with the class data. Also, like CSS, you can add parent container selectors to narrow your selection even further.

Narrowing Down Our Selection

We’ve selected the table successfully, though the table itself is of no interest to us—we want every other rowinside the containing table. To do this, we put a space between the ancestor and the descendant: inside it. We’ve selected the containing element, and from that containing element we want to pick out all the descendants that are table rows: that is, we want to specify all table rows
$('#celebs tr')
You can use this construct to drill down to the elements that you’re looking for, but for clarity’s sake try to keep your selectors as succinct as possible.
Let’s take this idea a step further. Say we wanted to select all span elements inside of p elements, which are themselves inside div elements—but only if those divs happen to have a class of fancy. We would use the selector:
$('div.fancy p span')
If you can follow this, you’re ready to select just about anything!

Testing Our Selection

Right, back to our task at hand. It feels like we’re getting closer, but so far we’ve just been selecting blindly with no way of knowing if we’re on the right path. We need a way of confirming that we’re selecting the correct elements. A simple way to achieve this is to take advantage of the length property. length returns the number of elements currently matched by the selector. We can combine this with the good ol’ trusty alert statement to ensure that our elements have been selected:
Example 2. chapter_02/02_selecting/script.js
$(document).ready(function() {
  alert($('#celebs tr').length + ' elements!');
});

This will alert the length of the selection—7 elements—for the celebrity table. This result might be different from what you’d expect, as there are only six celebrities in the table! If you have a look at the HTML, you’ll see where our problem lies: the table header is also a tr, so there are seven rows in total. A quick fix involves narrowing down our selector to find only table rows that lie inside the tbody element:
Example 3. chapter_02/03_narrowing_selection/script.js
$(document).ready(function() {
  alert($('#celebs tbody tr').length + ' elements!');
});

This will alert the correct length of 6 elements—the jQuery object is now holding our six celebrity table row elements.
If the alert shows 0, you’ll know there’s a mistake in your selector. A good way to troubleshoot this sort of issue is to reduce your selector to the smallest, simplest one possible.
In our example, we could simply write $('#celebs'), which would select just the table element and alert a length of 1. From here you can make your selectors more specific, and check that you’re selecting the correct number of elements as you go.

Filters

With the knowledge that we’ve successfully selected all of the table rows, narrowing our selection down to every other row is simple—because jQuery has a filter to do it. A filter removes certain items, and keeps only the ones we want. You’ll acquire a feel for what can be filtered as we work through some more examples, but for now we’ll just jump straight to the filter we need for our zebra stripes:
Example 4. chapter_02/04_filters/script.js
$(document).ready(function() {
  alert($('#celebs tbody tr:even').length + ' elements!');
});

Filters are attached to the item you want to filter (in this case, the table rows) and are defined by a colon, followed by the filter name. The :even filter used here keeps every even-indexed element in the selection and removes the rest, which is what we want. When we alert the selection length now, we see 3, as expected. All of our odd-numbered rows have been filtered out of the selection. There is a wide array of jQuery selector filters available to us: :odd (as you might expect), :first, :last, :eq() (for selecting, for example, the third element), and more. We’ll look at each of these in more detail as we need them throughout the book.

Selecting Multiple Elements

One last trick for basic selecting is the ability to select multiple elements in a single statement. This is very useful, as we’ll often want to apply the same action to several elements in unrelated parts of the page. Separating the selector strings with commas allows you to do this. For example, if we wanted to select every paragraph, div element, h1 heading, and input box on the page, we’d use this selector:
$('p,div,h1,input')
Learning how to use all these different selectors together to access exactly the page elements you want is a big part of mastering jQuery. It’s also one of the most satisfying parts of using jQuery, since you can pack some fairly complex selection logic into a single short line of code!

Becoming a Good Selector

Selecting may seem quite easy and, up to a point, it is. But what we’ve covered so far has only just scratched the surface of selecting. In most cases the basics are all you’ll need: if you’re simply trying to target an element or a bunch of related elements, the element name, id, and class are the most efficient and easiest ways to achieve this.
When moving around the DOM from a given element, the situation becomes a little trickier. jQuery provides a myriad of selectors and actions for traversing the DOM. Traversing means traveling up and down the page hierarchy, through parent and child elements. You can add and remove elements as you go, applying different actions at each step—which lets you perform some mind-bogglingly complex actions in a single jQuery statement!
If you’re a wiz at CSS, you’ll already be familiar with a lot of the statements; they’re mostly borrowed directly from the CSS specification. But there are probably a few that you’re unfamiliar with, especially if you’ve yet to spend much time learning CSS3 selectors. Of course, we’ll be covering and learning advanced selection techniques as we implement them in our examples and demos. For this reason, any time you want to find out more about all the jQuery selectors available, you can just head over to the online documentation and browse away!

Decorating: CSS with jQuery

Selecting elements in jQuery is the hard part. Everything else is both easy and fun. After we have selected our targets, we are able to manipulate them to build effects or interfaces. In this section we will cover a series of jQuery actions relating to CSS: adding and removing styles, classes, and more. The actions we execute will be applied individually to every element we’ve selected, letting us bend the page to our will!

Reading CSS Properties

Before we try changing CSS properties, let’s look first into how we can simply access them. jQuery lets us do this with the css function. Try this:
Example 5. chapter_02/05_reading_css_properties/script.js
$(document).ready(function() {
  var fontSize = $('#celebs tbody tr:first').css('font-size');
  alert(fontSize);
});

This code will alert the font size of the first element matched by the selector (as you’ve likely guessed, the :first filter will return the first element among those matched by the selector).
tip: CSS Properties of Multiple Elements
You can ask for a CSS property after selecting multiple elements, but this is almost always a bad idea: a function can only return a single result, so you’ll still only obtain the property for the first matched element.
The nifty aspect about retrieving CSS properties with this method is that jQuery gives you the element’s calculated style. This means that you’ll receive the value that’s been rendered in the user’s browser, rather than the value entered in the CSS definition. So, if you gave a div a height of, say, 200 pixels in the CSS file, but the content inside it pushed the height over 200 pixels, jQuery would provide you with the actual height of the element, rather than the 200 pixels you’d specified.
We’ll see why that’s really important when we come to implement some funky tricks a bit later.

Setting CSS Properties

So far we’ve yet to see jQuery actually do anything, and it’s high time to remedy that. We know the page is ready (since we popped up an alert), and we’re fairly sure we’ve selected the elements we’re interested in. Let’s check that we really have:
Example 6. chapter_02/06_zebra_striping/script.js
$(document).ready(function() {
  $('#celebs tbody tr:even').css('background-color','#dddddd');
});

You probably saw that coming! This is the same css function we used to read a CSS property, but now it’s being passed an extra parameter: the value we wish to set for that property. We’ve used the action to set the background-color to the value #dddddd (a light gray). Open the file from the code archive in your browser and test that it’s working correctly. You can see the result in Figure 2, “Zebra striping implemented with jQuery”.
Figure 2. Zebra striping implemented with jQuery
Zebra striping implemented with jQuery

important: Were You Ready?
As mentioned previously, this command must be issued from within our document-ready function. If we run the command before the DOM is ready, the selector will go looking for the #celebs element, but will find nothing that matches. At this point it will give up; it won’t even look for the tr elements, let alone change the background style.
This is true for all of the examples that follow, so remember to wrap your code in the document-ready function.
It’s looking good! But perhaps we should add a little extra to it—after all, more is more! What about a shade lighter font color to really define our stripes? There are a few ways we could add a second CSS property. The simplest way is to repeat the entire jQuery statement with our new values:
Example 7. chapter_02/07_multiple_properties_1/script.js (excerpt)
$('#celebs tbody tr:even').css('background-color','#dddddd');
$('#celebs tbody tr:even').css('color', '#666666');

These lines are executed one after the other. Though the end result is correct, it will become quite messy and inefficient if we have to change a whole slew of properties. Thankfully, jQuery provides us with a nice way to set multiple properties at the same time, using an object literal. Object literals are a JavaScript concept beyond the scope of this book, but for our purposes, all you need to know is that they provide an easy way of grouping together key/value pairs. For CSS, object literals allow us to match up our CSS properties (the keys) with the matching CSS values (the values) in a neat package:
Example 8. chapter_02/08_multiple_properties_2/script.js (excerpt)
$('#celebs tbody tr:even').css( 
  {'background-color': '#dddddd', 'color': '#666666'}
);

The object literal is wrapped in curly braces, with each key separated from its corresponding value by a colon, and each key/value pair separated by a comma. It’s passed as a single parameter to the css function. Using this method you can specify as many key/value pairs as you like—just separate them with commas. It’s a good idea to lay out your key/value pairs in a readable manner so you can easily see what’s going on when you come back to your code later. This is especially helpful if you need to set a larger number of properties. As an example:
Example 9. chapter_02/09_multiple_properties_3/script.js (excerpt)
$('#celebs tbody tr:even').css({
  'background-color': '#dddddd', 
  'color': '#666666',
  'font-size': '11pt',
  'line-height': '2.5em' 
});

note: To Quote or Not to Quote
In general, when dealing with JavaScript objects, it’s unnecessary for the keys to be in quotes. However, for jQuery to work properly, any key that contains a hyphen (as our background-color and font-size examples do) must be placed in quotes, or written in camel case (like backgroundColor).
Additionally, any key that’s already a keyword in the JavaScript language (such as float and class) must also be written in quotes.
It can be confusing trying to remember which keys need to be quoted and which don’t, so it’s to be recommended that you just put all object keys in quotes each time.

Classes

Excellent! We’ve already struck two tasks off the client’s list, and we have some funky jQuery happening. But if you stop and have a look at our last solution, you might notice something a little fishy. If you were to inspect the zebra-striped rows in a development tool such as Firebug, you’d notice that the CSS properties have been added to the paragraphs inline, as illustrated in Figure 3, “Inline styles viewed with Firebug”.
Figure 3. Inline styles viewed with Firebug
Inline styles viewed with Firebug

tip: Firebug
Firebug is a particularly useful tool for examining the DOM in your browser, as well as monitoring and editing CSS, HTML, and JavaScript (including jQuery). A debugger’s Swiss Army knife for the Web, it will save you hours by helping you see exactly what your browser thinks is going on. It’s available as a Mozilla Firefox extension, or as a stand-alone JavaScript file that you can include in your projects if you develop using another browser.
Inline styles are a big no-no in HTML/CSS best practice, right? That’s quite true, and this also applies in jQuery: to keep your code clear and maintainable, it makes more sense for all the styling information to be in the same place, in your CSS files. Then, as we’ll soon see, you can simply toggle those styles by attaching or removing class attributes to your HTML tags.
There are times when it is a good idea to use the css jQuery method in the way we’ve just seen. The most common application is when quickly debugging code: if you just want to outline an element in red to make sure you’ve selected it correctly, switching to your CSS file to add a new rule seems like a waste of time.

Adding and Removing Classes

If we need to remove the CSS from inline style rules, where should we put it? In a separate style sheet, of course! We can put the styles we want in a rule in our CSS that’s targeted to a given class, and use jQuery to add or remove that class from targeted elements in the HTML. Perhaps unsurprisingly, jQuery provides some handy methods for manipulating the class attributes of DOM elements. We’ll use the most common of these, addClass, to move our zebra stripe styles into the CSS file where they belong.
The addClass function accepts a string containing a class name as a parameter. You can also add multiple classes at the same time by separating the class names with a space, just as you do when writing HTML:
$('div').addClass('class_name');
$('div').addClass('class_name1 class_name2 class_name3');
We only want to add one class name, though, which we’ll call zebra. First, we’ll add the rule to a new CSS file (including it with a link tag in our HTML page):
Example 10. chapter_02/10_adding_classes/zebra.css
.zebra { 
  background-color: #dddddd;
  color: #666666;
}

Then, back in our JavaScript file, we’ll modify the selector to use jQuery’s addClass method rather than css:
Example 11. chapter_02/10_adding_classes/script.js
$('#celebs tr:even').addClass('zebra');

The result is exactly the same, but now when we inspect the table in Firebug, we’ll see that the inline styles are gone—replaced by our new class definition. This is shown in Figure 4, “Adding classes to table rows”.
Figure 4. Adding classes to table rows
Adding classes to table rows

That’s much better. Now, if we want to change the appearance of the zebra stripes in the future, we can simply modify the CSS file; this will save us hunting through our jQuery code (potentially in multiple locations) to change the values.
There’ll also be times when we want to remove class names from elements (we’ll see an example of when this is necessary very soon). The action to remove a class is conveniently known as removeClass. This function is used in exactly the same way as addClass; we just pass the (un)desired class name as a parameter:
$('#celebs tr.zebra').removeClass('zebra');
It’s also possible to manipulate the id attribute, or any other attribute for that matter, using jQuery’s attr method. We’ll cover this method in more detail later in the book.

What’s Next?

Next week we’ll bring you another introductory jQuery article. We’ll be looking at modifying the DOM itself using jQuery: adding, removing, and changing elements. We’ll also learn some of the basics of jQuery animation and the theory behind the concept of progressive enhancement.
If you can’t wait till next week, you can read the full contents of both articles, as well as over 100 more pages of jQuery content, by downloading the sample PDF of jQuery: Novice to Ninja. It also includes the full code archive for the entire book.

No comments:

Post a Comment

  ©Template by Dicas Blogger.

TOPO