JQuery: Novice to Ninja- P22

Chia sẻ: Cong Thanh | Ngày: | Loại File: PDF | Số trang:15

lượt xem

JQuery: Novice to Ninja- P22

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

JQuery: Novice to Ninja- P22:No matter what kind of ninja you are—a cooking ninja, a corporate lawyer ninja, or an actual ninja ninja—virtuosity lies in first mastering the basic tools of the trade. Once conquered, it’s then up to the full-fledged ninja to apply that knowledge in creative and inventive ways.

Chủ đề:

Nội dung Text: JQuery: Novice to Ninja- P22

  1. 292 jQuery: Novice to Ninja 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! Licensed to JamesCarlson@aol.com 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.
  2. Lists, Trees, and Tables 293 Licensed 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:
  3. 294 jQuery: Novice to Ninja chapter_08/01_jquery_ui_selectable/index.html (excerpt) bad singer old plastic surgery broke ⋮ Approve Reject Clear A big long list is a bit intimidating, so we’ll use some basic CSS to make the list Licensed to JamesCarlson@aol.com 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) $("#tags").selectable(); 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) #tags .ui-selecting { background: #FEFF9F; } #tags .ui-selected { background-color:#eEeF8F; } The ui-selecting class is 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
  4. Lists, Trees, and Tables 295 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 selecting event—and remove duplicates as the user moves the mouse—but it might Licensed to JamesCarlson@aol.com 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) $('#tags').selectable({ stop: function() { // The user stopped selecting! } }); 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
  5. 296 jQuery: Novice to Ninja chapter_08/01_jquery_ui_selectable/script.js (excerpt) var names = $.map($('.ui-selected, this'), function(element, i) { return $(element).text(); }); $('li', this) .filter(function() { if ($.inArray($(this).text(), names) != -1) { return true; } else { return false; }; }) Licensed to JamesCarlson@aol.com .addClass('ui-selected'); 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. $.map and $.inArray 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!
  6. Lists, Trees, and Tables 297 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 true or false as 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 Licensed to JamesCarlson@aol.com 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) $('#approve').click(function() { $('#tags') .find('.ui-selected') .addClass('approve') .removeClass('ui-selected reject'); }); $('#reject').click(function() { $('#tags') .find('.ui-selected') .addClass('reject') .removeClass('ui-selected approve'); }); $('#clear').click(function() { $('#tags') .find('li') .removeClass('ui-selected approve reject'); $('#approved').val(''); });
  7. 298 jQuery: Novice to Ninja 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) $('#approve').click(function() { Licensed to JamesCarlson@aol.com var approvedItems = ""; $('#tags') .find('.ui-selected') .addClass('approve') .removeClass('ui-selected reject') .each(function() { approvedItems += $(this).index() + "|"; }); $('#approved').val(approvedItems); }); 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) $('#approved').val(''); Thanks to the index method, we now know which items in the list have been ap­ proved. index will 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
  8. Lists, Trees, and Tables 299 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) Beau Dandy Glendatronix BMX Spandex Corporation Maxwell Zilog Computadors Licensed to JamesCarlson@aol.com 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) var SORTER = {}; SORTER.sort = function(which, dir) { SORTER.dir = (dir == "desc") ? -1 : 1; $(which).each(function() { // Find the list items and sort them var sorted = $(this).find("> li").sort(function(a, b) { return $(a).text().toLowerCase() > $(b).text().toLowerCase() ? SORTER.dir : -SORTER.dir; }); $(this).append(sorted); }); };
  9. 300 jQuery: Novice to Ninja 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 Licensed to JamesCarlson@aol.com 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 a is 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.sort from their click event handlers: chapter_08/02_sorting_lists/script.js (excerpt) $('#ascending').click(function() { SORTER.sort('.sortable'); }); $('#descending').click(function() { SORTER.sort('.sortable', 'desc'); });
  10. Lists, Trees, and Tables 301 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 Licensed to JamesCarlson@aol.com 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:
  11. 302 jQuery: Novice to Ninja chapter_08/03_select_lists/index.html (excerpt) Beau Dandy ⋮ Johnny Stardust ⋮ ⋮ Licensed to JamesCarlson@aol.com 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) var SWAPLIST = {}; SWAPLIST.swap = function(from, to) { $(from) .find(':selected') .appendTo(to); } 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 find action 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) $('#swapLeft').click(function() { SWAPLIST.swap('#candidates', '#a-listers'); });
  12. Lists, Trees, and Tables 303 $('#swapRight').click(function() { SWAPLIST.swap('#a-listers', '#candidates'); }); Now selected items can be swapped back and forth at will! Let’s add some more functionality to our SWAPLIST object. How about swapping all elements? That’s even easier: chapter_08/03_select_lists/script.js (excerpt) SWAPLIST.swapAll = function(from,to) { $(from) .children() Licensed to JamesCarlson@aol.com .appendTo(to); } 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) SWAPLIST.invert = function(list) { $(list) .children() .attr('selected', function(i, selected) { return !selected; }); } 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!
  13. 304 jQuery: Novice to Ninja Calling attr with a Function Parameter This is a great trick: we’ve used the attr action—but it’s not the version we’re used to. Previously we used the attr(key, value) action to set attributes to a static value, but attr also lets us pass in a function to determine the value. The function will be passed two parameters: the index of the element and its current value. The return value of the function becomes the attribute’s new value. For our invert method, we return the opposite of the element’s current selection value—so each element is toggled. We can do this kind of dynamic processing with stacks of commands: text, html, val, addClass, wrap … and many more! Searching through Lists Licensed to JamesCarlson@aol.com After having to listen to the client whine on and on about how hard it is to find the celebrities he’s trying to select, you decide to throw in a little freebie: a quick search feature that lets him type some characters and automatically select any matching elements: chapter_08/03_select_lists/script.js (excerpt) SWAPLIST.search = function(list, search) { $(list) .children() .attr('selected', '') .filter(function() { if (search == '') { return false; } return $(this) .text() .toLowerCase() .indexOf(search) > - 1 }) .attr('selected', 'selected'); } What’s going on here? First, we’re grabbing all list items and then clearing any previous selections (by setting selected to an empty string). Next, we’re using the filter action to find any elements we’re searching for. The filter action accepts a function as a parameter, and runs that function against every jQuery object in the selection. If the function returns true, the element stays
  14. Lists, Trees, and Tables 305 in the selection. But if the function returns false—it’s gone … out of the selection, and unaffected by further processing. To find elements we care about, we check to see if the text they contain has the text we’re looking for in it. To do this we use the text action that gives us a string. We convert it to lower case (so the search will be case-insensitive), and check to see if our source text is located in the element string. The JavaScript indexOf method will find the position of a string inside another string; for example, "hello".index­ Of('ll'); will return 2 (the index starts at 0, as usual). If the substring is not found, indexOf will return -1, which is what we’re checking for here. Whichever elements remain in the jQuery selection after the filter function runs Licensed to JamesCarlson@aol.com must contain the keyword we’re looking for—so once again we use the attr method to select them. To use the search method we created, we could attach it to a click handler—so the user types a word and then clicks a search button. Even better is to attach a keyup handler to the input itself, so it selects as you type: chapter_08/03_select_lists/script.js (excerpt) $('#search').keyup(function() { SWAPLIST.search("#a-listers, #candidates", $(this).val()); }); Trees “This is another mess. We have several subcategories of celebrities … you know, A-listers who are in bands, or who are famous because their parents are rich; B­ listers who came in second on some reality TV contest, stuff like that. Right now they’re all in one big list. I need to see how the categories fit inside each other! Can you build something to help me?” The client’s talking about a tree. Trees are few and far between on the Web for two reasons: they’re hard to do well, and there are few situations where they make sense. However, a nested set of categories is a valid use of a tree structure (a more common one is representing a directory structure), so let’s dive in!
  15. 306 jQuery: Novice to Ninja Expandable Tree Here’s the secret about trees: they’re really just nested lists! The key to dealing with trees in jQuery is to make sure your HTML is consistent—so we can figure out where we are, and open and close the correct branches. The control we’ll build will look like the one in Figure 8.3. We’ve kept the styling deliberately sparse, but as always the potential for improving the appearance is limited only by your CSS skills! Licensed to JamesCarlson@aol.com Figure 8.3. Tree The important part of our markup is the span that contains each category (or direc­ tory) name. We’ll use this span as a base for inserting a small plus/minus graphic—to act as the branch toggle. Here’s a subset of the markup we’ll be working with: chapter_08/04_expandable_tree/index.html (excerpt) A-list Celebrities In a successful band Johnny Stardust Glendatronix ⋮ Famous because they're rich
Đồng bộ tài khoản