Our general approach will be to make a list of all the tags the user has selected, then search for any duplicates of those tags that appear in the greater tag list: 1 http://docs.jquery.
Trang 1Licensed to JamesCarlson@aol.com
But the rise of the Rich Internet Application is changing that Clients expect our
web-based systems to rival their desktop applications for usability and design
Thankfully with jQuery by our side, that’s no problem!
Lists
Lists are the real unsung heroes of the post table-based layout period of the Web
As designers were freed from the constraints of the tyrannical table cell, they started
to look for other (semantically correct) ways to recreate common user interface
elements such as menus, navigation panels, tag clouds, and so on And time after
time, as the redundant layout cruft was stripped from the underlying data, all that
was left behind was—a list!
The StarTrackr! site is already home to an extensive array of lists: they form the
basis of our tabs, accordions, menus, image galleries, and more—but there’s far more
we can do to augment and enhance the humble list
jQuery UI Selectables
The ocean of user-generated content is proving a handful for our client Thousands
of tags are pouring in from the site’s users—but now the legal department is saying
that as the manager, he has to approve every single one manually, to avoid a repeat
of a recent nasty litigation
Because the site employs an unconstrained tag system, there are stacks of duplicate
tags in the lists—and with the current system that means stacks of extra administra
tion What the client really wants is a way to easily see tags, select them (and any
duplicates), and click a button to approve or reject them
Our plan of attack is to add jQuery UI’s selectable behavior to our list Making an
element selectable gives the user the ability to lasso any of the element’s children
to select them: if you click on one element, then drag over subsequent elements,
they become highlighted You can than process the selection however you see fit
Perfect for administrating boring lists! The behavior we’re aiming to create is illus
trated in Figure 8.1
Trang 2Licensed to JamesCarlson@aol.com
Figure 8.1 Selectable list items
In addition to lassoing, the selectable behavior also lets you add nonsequential items
to the list using the Ctrl key (as you can do in most desktop applications)—and even
navigate the selections with the keyboard
Keep Your Users in the Loop
Although being able to click and drag selections on a list is very cool and very useful, it’s only cool and useful if your users know about it! Making selections in this manner is a nonstandard form of interaction on the Web, so you’ll need to provide instructions to your users to teach them how to use your new functionality
Let’s have a look at the markup The server spits out a long list of tags, which is a
fine base for our selector grid We’ll also throw in a few buttons to allow users to
approve or reject the tags in their selection, as well as a button to reset the selection:
Trang 3Licensed to JamesCarlson@aol.com
chapter_08/01_jquery_ui_selectable/index.html (excerpt)
⋮
A big long list is a bit intimidating, so we’ll use some basic CSS to make the list
into a grid, and convert each tag into a small box With our grid ready to go, we
have to add the jQuery UI library to the page Now it’s time to tell the tag list to
become selectable:
chapter_08/01_jquery_ui_selectable/script.js (excerpt)
Fire up your browser and check it out Hrrm … has anything actually happened?
Well yes, it has, but it’s invisible! The selectable method works by adding class
attributes to selected items, unless we assign styles to those classes, we’ll be unable
to see anything happening If you inspect the list items with Firebug as you select
them, you’ll see the changes occurring Let’s have a stab at styling selected elements:
chapter_08/01_jquery_ui_selectable/style.css (excerpt)
The ui-selecting classis applied as the user is in the process of selecting elements,
and the ui-selected class is added as soon they stop If you try it now, you’ll see
you can lasso some squares It’s quite a natural interaction—which is exactly what
Trang 4Licensed to JamesCarlson@aol.com
you want from your page components You can also click while holding down the
Ctrl key to select individual items
The next task we want to do is help with the duplicate tags In a tagging system the
number of tags for each term is important—so rather than just deleting duplicates,
we’ll write some code to select any tags that match the user’s selection For instance,
if they click on “A-lister,” all the “A-lister” tags will be highlighted
We need to know which events we can hook into from the jQuery UI component
Consulting the documentation,1 we find that we can capture the start, stop, se
lecting, unselecting, selected, and unselected events We could capture the
selectingevent—and remove duplicates as the user moves the mouse—but it might
be a bit confusing We’ll stick with the stop event, which fires as soon as the user
completes the selection:
chapter_08/01_jquery_ui_selectable/script.js (excerpt)
Now we can begin our quest to find the duplicate tags Our general approach will
be to make a list of all the tags the user has selected, then search for any duplicates
of those tags that appear in the greater tag list:
1 http://docs.jquery.com/UI/Selectable
Trang 5Licensed to JamesCarlson@aol.com
chapter_08/01_jquery_ui_selectable/script.js (excerpt)
To find the duplicates, we’ve called on the service of an assortment of new jQuery
features, so hold on to your hats!
The first of these is the oddest: $('.ui-selected', this) This looks like a regular
jQuery selector, but there’s a second parameter It turns out that the complete
definition for the jQuery selector is actually $(expression, context)—we’ve just
been omitting the second parameter The context defines where jQuery should
look for your selector; by default it looks everywhere on the page—but by specifying
our unordered list as the context, the expression will be limited to elements inside
the list
Next we use a couple of jQuery utility methods: $.map and $.inArray to juggle the
list items The utility methods jQuery provides are mostly for working on JavaScript
arrays—and that’s what we’re doing here First we create an array called names,
which we populate using the $.map method
The $.map method allows you to take each element in the array, process it in some
way, and return the results as a new array You use it when you want to transform
every element in the same way We want to transform our jQuery selection into a
simple list of tag text—so we pass in the selection, and define an anonymous function
to return each element’s text Hey presto: an array of tag text!
Trang 6Licensed to JamesCarlson@aol.com
We next use the context trick as before to retrieve all the list item elements, and
filter them based on whether or not they’re duplicates Our filter function uses
the $.inArray utility method, which searches an array (but only plain JavaScript
arrays—not jQuery selections, unfortunately) for a specified value Given an array
and a search term, like $.inArray(value, array), it will return the value’s index
in the array Helpfully, it will return -1 if the value is not found in the array Re
member that filter expects us to return either true or false—so we just check to
see if $.inArray returns -1, and return trueor falseas appropriate Using filter
in this way allows us to search our array of tag texts for each list item’s text—if it’s
in there, it’s a duplicate, so we return it to the filter to be selected
Accessing the Data
Now that we can make selections, how can we use them? The jQuery UI Selectable
component works with class names, so we will too To acquire the list of selected
values, we simply search for any items that have the ui-selected class on them:
chapter_08/01_jquery_ui_selectable/script.js (excerpt)
Trang 7Licensed to JamesCarlson@aol.com
We’re just adding an approve or reject class when the user clicks on our
but-tons—also being sure to remove the ui-selected class, since we want to style
approved tags differently from selected ones
But what if we wanted to, say, send this information to the server? Perhaps it would
be good to store the list of approved tags in a hidden form field, so that the server
can access it for processing Let’s update the #approve click handler to iterate over
the approved items, and append each item’s index to a hidden field in a simple
pipe-delimited format:
chapter_08/01_jquery_ui_selectable/script.js (excerpt)
We’ll also add a line to our #clear button click handler to clear that input’s value:
chapter_08/01_jquery_ui_selectable/script.js (excerpt)
Thanks to the index method, we now know which items in the list have been ap
proved indexwill tell you an item’s position inside its parent element Our control
is impressive in how easy it is to use The jQuery UI selectable behavior is doing
a lot of work behind the scenes to allow lists to be selectable—but the end result is
a natural-feeling component, and that’s exactly what we want
Sorting Lists
With the tag system under control, it’s time to turn to some of the other lists that
are scattered throughout the admin section Many of these lists are populated by
the server in the order they were entered into the system This is good for seeing
Trang 8Licensed to JamesCarlson@aol.com
what’s new, but bad for finding particular items Our client has asked us to build
some sorting capabilities into all of the lists in the admin section, so he can click a
button and have the lists sorted in ascending or descending alphabetical order
The markup we’ll be dealing with is a simple unordered list made up of links:
chapter_08/02_sorting_lists/index.html (excerpt)
jQuery objects lack any built-in sorting functionality This makes sense, after all; a
selection could include different kinds of elements located in different parts of the
page, so sorting them in a consistent manner would be impossible To sort our
jQuery selections, therefore, we need to fall back on some JavaScript array methods
jQuery selections aren’t actually arrays, but they’re “array-like,” and they allow us
to use the JavaScript sort function on them
We’ll try to build a reusable list-sorting widget We’ll call it SORTER, and we’d call
SORTER.sort(list) to sort a list in ascending order, and SORTER.sort(list,
'desc') to sort in descending order We’ll assume that the selector passed in will
match ordered or unordered lists, but let’s see if we can make that happen:
chapter_08/02_sorting_lists/script.js (excerpt)
Trang 9Licensed to JamesCarlson@aol.com
That code is deceptively short, because it happens to be doing a lot! First up, we
check to see if desc was passed in as the dir parameter, and set the SORTER.dir
variable accordingly All we need to do is grab all of the first-level children list
elements and give them a sort We only want the first-level items; if we grabbed
further levels, they’d be sorted and dragged up to the parent level Because calling
sort reverts our selections to raw JavaScript, we need to rewrap them in the $() to
be able to call the jQuery text method and compare their values We also convert
the values to lowercase—which makes the sorting case-insensitive
The sort Function
The sort function is plain old JavaScript: it sorts an array based on the results
of the function you pass to it sort will go over the contents of the array and pass them to your function in pairs If your function returns 1, sort will swap the items and place the second one first If your function returns -1, JavaScript will put the first item first Finally, if your function returns 0, sort will consider that both items are equal and no sorting will take place
We’re doing a little magic to let us use the same function for sorting in ascending
and descending order: we’ve set our SORTER.dir variable to -1 or 1, depending on
the direction Then in the sort comparison function, we do a further calculation:
if ais less than b, we return -SORTER.dir If the direction comes in as -1, we process
it as -(-1), which is 1—so if we’re trying to sort descending, the return values are
swapped
Once we’ve sorted the items, we can reinsert them into the list in the correct order
Remember, the append function removes the element first—so it removes the item
and appends it in the correct position
To test it out, we’ll add some buttons to our HTML and call SORTER.sortfrom their
click event handlers:
chapter_08/02_sorting_lists/script.js (excerpt)
Trang 10Licensed to JamesCarlson@aol.com
Manipulating Select Box Lists
Although we covered forms in the previous chapter, there’s still time to have a look
at certain form elements in the context of lists Here we’re going to examine select
elements, especially those with multiple="multiple" (that is, select boxes which
appear as selectable lists of items)
Swapping List Elements
The StarTrackr! client has asked us to improve the admin functionality for assigning
celebrities to the A-list The current functionality consists of two select elements:
one contains the A-list celebrities, and the other contains every other celebrity in
the system But the world of popularity is extremely fickle—and an A-lister today
can be a nobody tomorrow So the client wants to be able to easily swap the
celebrities between each list We’ll add a few controls to the interface to let him do
just that, as shown in Figure 8.2
Figure 8.2 List boxes with controls This is the HTML we’re dealing with, consisting of the two select elements, and
a few buttons for performing various operations:
Trang 11Licensed to JamesCarlson@aol.com
chapter_08/03_select_lists/index.html (excerpt)
⋮
⋮
⋮
As stated, the client wants the ability to swap selected items from one list to another
We’ll make a SWAPLIST object that will contain all the functionality we’ll build
This can then be reused anytime we need to play with select elements:
chapter_08/03_select_lists/script.js (excerpt)
We’ve defined a swap function that accepts selector strings targeting two lists: a
source list and a destination list The first task we want to do is to grab any items
that are currently selected We can do this using the findaction with the :selected
form filter This filter will return any form elements that have the attribute selected
set Then we can move the selection over to the destination list with appendTo
Easy! And once we’ve defined this functionality, we can apply it to any two lists
by calling our swap method from appropriate click handlers:
chapter_08/03_select_lists/script.js (excerpt)
Trang 12Licensed to JamesCarlson@aol.com
Now selected items can be swapped back and forth at will! Let’s add some more
functionality to our SWAPLISTobject How about swapping all elements? That’s even
easier:
chapter_08/03_select_lists/script.js (excerpt)
We just take all the child elements (instead of only the selected elements) and append
them to the bottom of the destination—the whole list jumps from source list to
destination list
Inverting a Selection
The next client request is to add a button that inverts the current selection, to make
it easier for his staff when dealing with large selections When this link is clicked,
all currently selected items in the target list become deselected, and vice versa Let’s
make a function inside the SWAPLIST object that does this:
chapter_08/03_select_lists/script.js (excerpt)
All we have to do is retrieve every list item and swap its selected attribute We
use the attr function to set our list items to !$(this).attr('selected') The
JavaScript NOT (!) operator (the exclamation mark) inverts the Boolean value, so
if the value was true it becomes false, and if it was false it becomes true!