[jquery基础教程.(英文原版)].packt.publishing.learning.jquery


Learning jQuery 1.3 Better Interaction Design and Web Development with Simple JavaScript Techniques Jonathan Chaffer Karl Swedberg BIRMINGHAM - MUMBAI Learning jQuery 1.3 Copyright © 2009 Packt Publishing All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews. Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the authors, Packt Publishing, nor its dealers or distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book. Packt Publishing has endeavored to provide trademark information about all the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information. First published: February 2009 Production Reference: 1040209 Published by Packt Publishing Ltd. 32 Lincoln Road Olton Birmingham, B27 6PA, UK. ISBN 978-1-847196-70-5 www.packtpub.com Credits Authors Jonathan Chaffer Karl Swedberg Reviewers Akash Mehta Dave Methvin Mike Alsup Senior Acquisition Editor Douglas Paterson Development Editor Usha Iyer Technical Editor John Antony Editorial Team Leader Akshara Aware Production Editorial Manager Abhijeet Deobhakta Project Team Leader Lata Basantani Project Coordinator Leena Purkait Indexer Rekha Nair Proofreader Jeff Orloff Production Coordinator Aparna Bhagat Cover Work Aparna Bhagata The Gigapedia Team Foreword I feel honored knowing that Karl Swedberg and Jonathan Chaffer undertook the task of writing Learning jQuery. As the first book about jQuery, it set the standard that other jQuery — and, really, other JavaScript books in general — have tried to match. It's consistently been one of the top selling JavaScript books since its release, in no small part due to its quality and attention to detail. I'm especially pleased that it was Karl and Jonathan who wrote the book since I already knew them so well and knew that they would be perfect for the job. Being part of the core jQuery team, I've had the opportunity to come to know Karl quite well over the past couple years, and especially within the context of his book writing effort. Looking at the end result, it's clear that his skills as both a developer and a former English teacher were perfectly designed for this singular task. I've also had the opportunity to meet both of them in person, a rare occurrence in the world of distributed Open Source projects, and they continue to be upstanding members of the jQuery community. The jQuery library is used by so many different people in the jQuery community. The community is full of designers, developers, people who have experience programming, and those who don't. Even within the jQuery team, we have people from all backgrounds providing their feedback on the direction of the project. There is one thing that is common across all of jQuery's users, though: We are a community of developers and designers who want JavaScript development to be made simple. It's almost a cliché, at this point, to say that an open source project is community- oriented, or that a project wants to focus on helping new users get started. But it's not just an empty gesture for jQuery; it's the liquid-oxygen fuel for the project. We actually have more people in the jQuery team dedicated to managing the jQuery community, writing documentation, or writing plugins than actually maintaining the core code base. While the health of the library is incredibly important, the community surrounding that code is the difference between a floundering, mediocre project and one that will match and exceed your every need. How we run the project, and how you use the code, is fundamentally very different from most open source projects — and most JavaScript libraries. The jQuery project and community is incredibly knowledgeable; we understand what makes jQuery a different programming experience and do our best to pass that knowledge on to fellow users. The jQuery community isn't something that you can read about to understand; it's something that you actually have to participate in for it to fully sink in. I hope that you'll have the opportunity to partake in it. Come join us in our forums, mailing lists, and blogs and let us help guide you through the experience of getting to know jQuery better. For me, jQuery is much more than a block of code. It's the sum total of experiences that have transpired over the years in order to make the library happen. The considerable ups and downs, the struggle of development together with the excitement of seeing it grow and succeed. Growing close with its users and fellow team members, understanding them and trying to grow and adapt. When I first saw this book talk about jQuery and discuss it like a unified tool, as opposed to the experiences that it's come to encapsulate for me, I was both taken aback and excited. Seeing how others learn, understand, and mold jQuery to fit them is much of what makes the project so exhilarating. I'm not the only one who enjoys jQuery on a level that is far different from a normal tool-user relationship. I don't know if I can properly encapsulate why this is, but I've seen it time and time again — the singular moment when a user's face lights up with the realization of just how much jQuery will help them. There is a specific moment where it just clicks for a jQuery user, when they realize that this tool that they were using was in fact much, much more than just a simple tool all along — and suddenly their understanding of how to write dynamic web applications completely shifts. It's an incredible thing, and absolutely my favorite part of the jQuery project. I hope you'll have the opportunity to experience this sensation as well. John Resig Creator of jQuery About the Authors Jonathan Chaffer is the Chief Technology Officer of Structure Interactive, an interactive agency located in Grand Rapids, Michigan. There, he oversees web development projects using a wide range of technologies, and continues to collaborate on day-to-day programming tasks as well. In the open-source community, Jonathan has been very active in the Drupal CMS project, which has adopted jQuery as its JavaScript framework of choice. He is the creator of the Content Construction Kit, a popular module for managing structured content on Drupal sites. He is responsible for major overhauls of Drupal's menu system and developer API reference. Jonathan lives in Grand Rapids with his wife, Jennifer. I would like to thank Jenny for her tireless enthusiasm and support, Karl for the motivation to continue writing when the spirit is weak, and the Ars Technica community for constant inspiration toward technical excellence. Karl Swedberg is a web developer at Fusionary Media in Grand Rapids, Michigan, where he spends much of his time implementing design with a focus on "web standards"—semantic HTML, well-mannered CSS, and unobtrusive JavaScript. A member of the jQuery Project Team and an active contributor to the jQuery discussion list, Karl has presented at workshops and conferences and provided corporate training in Europe and North America. Before his current love affair with web development, Karl worked as a copy editor, a high-school English teacher, and a coffee house owner. His fascination with technology began in the early 1990s when he worked at Microsoft in Redmond, Washington, and it has continued unabated ever since. Karl would rather be spending time with his wife, Sara, and his two children, Benjamin and Lucia. I wish to thank my wife, Sara, for her steadfast love and support. Thanks also to my two delightful children, Benjamin and Lucia. Jonathan Chaffer has my deepest respect for his programming expertise and my gratitude for his willingness to write this book with me. Many thanks to John Resig for creating the world's greatest JavaScript library and for fostering an amazing community around it. Thanks also to the folks at Packt Publishing, the technical reviewers of this book, the jQuery Cabal, and the many others who have provided help and inspiration along the way. About the Reviewers Akash Mehta is a web application developer, technical writer and business consultant based in Brisbane, Australia. His past projects include brochure websites, e-learning solutions and information systems. He has written web development articles for several of publishers in print and online, is a regular speaker at local conferences, and contributes to prominent PHP blogs. As a student, Akash maintained PHP web applications and built user interfaces using the jQuery toolkit. While pursuing a degree in both commerce and IT, Akash develops web applications on PHP and Python platforms. After hours, he organizes his local PHP user group. Akash develops applications on a wide range of open source libraries. His toolbox includes a number of application frameworks, including the Zend Framework, CakePHP and Django; Javascript frameworks like jQuery, Prototype and Mootools, platforms such as Adobe Flash/Flex, and the MySQL and SQLite database engines. Currently, Akash provides freelance technical writing and web development through his website, http://bitmeta.org. Dave Methvin has more than 25 years of software development experience in both the Windows and Unix environments. His early career focused on embedded software in the fields of robotics, telecommunications, and medicine. Later, he moved to PC-based software projects using C/C++ and web technologies. Dave also has more than 20 years of experience in computer journalism. He was Executive Editor at PC Tech Journal and Windows Magazine, covering PC and Internet issues; his how-to columns on JavaScript offered some of the first cut-and- paste solutions to common web page problems. He was also a co-author of the book "Networking Windows NT" (John Wiley & Sons, 1997). Currently, Dave is Chief Technology Officer at PC Pitstop, a web site that helps users fix and optimize the performance of their computers. He is also active in the jQuery community. Mike Alsup has been involved with the jQuery project since near its inception and has contributed many popular plugins to the community. He is an active participant in the jQuery Google Group where he frequently provides support to new jQuery users. Mike lives in upstate NY with his wife, Diane, and their triplet teenage sons. He is a Senior Software Developer at Click Commerce, Inc. where he focuses on Java, Swing, and web application development. His jQuery plugins can be found at http://jquery.malsup.com/ Table of Contents Preface 1 Chapter 1: Getting Started 7 What jQuery does 7 Why jQuery works well 8 History of the jQuery project 10 Our first jQuery-powered web page 11 Downloading jQuery 11 Setting up the HTML document 11 Adding jQuery 14 Finding the poem text 15 Injecting the new class 15 Executing the code 15 The finished product 17 Summary 18 Chapter 2: Selectors 19 The Document Object Model 19 The $() factory function 20 CSS selectors 21 Styling list-item levels 23 Attribute selectors 24 Styling links 25 Custom selectors 26 Styling alternate rows 27 Form selectors 29 DOM traversal methods 30 Styling specific cells 31 Chaining 32 Accessing DOM elements 33 Summary 34 Table of Contents [ ii ] Chapter 3: Events 35 Performing tasks on page load 35 Timing of code execution 35 Multiple scripts on one page 36 Shortcuts for code brevity 37 Coexisting with other libraries 38 Simple events 39 A simple style switcher 39 Enabling the other buttons 41 Event handler context 43 Further consolidation 45 Shorthand events 47 Compound events 48 Showing and hiding advanced features 48 Highlighting clickable items 50 The journey of an event 51 Side effects of event bubbling 53 Altering the journey: the event object 53 Event targets 54 Stopping event propagation 55 Default actions 56 Event delegation 56 Removing an event handler 58 Event namespacing 59 Rebinding events 60 Simulating user interaction 62 Keyboard events 63 Summary 66 Chapter 4: Effects 67 Inline CSS modification 67 Basic hide and show 72 Effects and speed 74 Speeding in 74 Fading in and fading out 75 Compound effects 76 Creating custom animations 77 Toggling the fade 78 Animating multiple properties 79 Positioning with CSS 81 Simultaneous versus queued effects 82 Working with a single set of elements 82 Table of Contents [ iii ] Working with multiple sets of elements 85 Callbacks 87 In a nutshell 89 Summary 90 Chapter 5: DOM Manipulation 91 Manipulating attributes 91 Non-class attributes 91 The $() factory function revisited 94 Inserting new elements 96 Moving elements 98 Marking, numbering, and linking the context 101 Appending footnotes 103 Wrapping elements 105 Copying elements 106 Clone with events 107 Cloning for pull quotes 107 A CSS diversion 108 Back to the code 109 Prettifying the pull quotes 111 DOM manipulation methods in a nutshell 113 Summary 114 Chapter 6: AJAX 115 Loading data on demand 115 Appending HTML 117 Working with JavaScript objects 120 Retrieving a JavaScript object 120 Global jQuery functions 121 Executing a script 125 Loading an XML document 127 Choosing a data format 130 Passing data to the server 131 Performing a GET request 132 Performing a POST request 136 Serializing a form 137 Keeping an eye on the request 139 AJAX and events 142 Security limitations 143 Using JSONP for remote data 144 Additional options 146 The low-level AJAX method 146 Modifying default options 147 Table of Contents [ iv ] Loading parts of an HTML page 147 Summary 150 Chapter 7: Table Manipulation 151 Sorting and paging 152 Server-side sorting 152 Preventing page refreshes 153 JavaScript sorting 153 Row grouping tags 155 Basic alphabetical sorting 156 The power of plugins 161 Performance concerns 161 Finessing the sort keys 163 Sorting other types of data 165 Column highlighting 168 Alternating sort directions 168 Server-side pagination 171 Sorting and paging go together 171 JavaScript pagination 173 Displaying the pager 173 Enabling the pager buttons 174 Marking the current page 176 Paging with sorting 177 The finished code 178 Modifying table appearance 180 Row highlighting 181 Row striping 182 Advanced row striping 185 Interactive row highlighting 186 Tooltips 189 Collapsing and expanding sections 194 Filtering 196 Filter options 197 Reversing the filters 199 Interacting with other code 200 The finished code 202 Summary 205 Chapter 8: Forms with Function 207 Improving a basic form 207 Progressively enhanced form styling 208 The legend 210 Required field messages 211 Conditionally displayed fields 215 Form validation 217 Required fields 218 Table of Contents [  ] Required formats 221 A final check 223 Checkbox manipulation 226 The finished code 228 Compact forms 232 Placeholder text for fields 232 AJAX auto-completion 235 On the server 236 In the browser 237 Populating the search field 238 Keyboard navigation 239 Handling the arrow keys 241 Inserting suggestions in the field 242 Removing the suggestion list 243 Auto-completion versus live search 243 The finished code 244 Working with numeric form data 246 Shopping cart table structure 247 Rejecting non-numeric input 250 Numeric calculations 251 Parsing and formatting currency 252 Dealing with decimal places 254 Other calculations 255 Rounding values 256 Finishing touches 257 Deleting items 258 Editing shipping information 263 The finished code 266 Summary 268 Chapter 9: Shufflers and Rotators 269 Headline rotator 269 Setting up the page 270 Retrieving the feed 272 Setting up the rotator 275 The headline rotate function 276 Pause on hover 279 Retrieving a feed from a different domain 281 Adding a loading indicator 282 Gradient fade effect 283 The finished code 285 An image carousel 287 Setting up the page 288 Revising the styles with JavaScript 290 Table of Contents [ vi ] Shuffling images when clicked 291 Adding sliding animation 294 Displaying action icons 295 Image enlargement 299 Hiding the enlarged cover 301 Displaying a close button 302 More fun with badging 304 Animating the cover enlargement 306 Deferring animations until image loads 310 Adding a loading indicator 311 The finished code 313 Summary 316 Chapter 10: Using Plugins 317 Finding plugins and help 317 How to use a plugin 318 The Form plugin 318 Tips and tricks 320 The jQuery UI plugin library 321 Effects 321 Color animations 322 Class animations 322 Advanced easing 322 Additional effects 323 Interaction components 324 Widgets 326 jQuery UI ThemeRoller 329 Other recommended plugins 330 Forms 330 Autocomplete 330 Validation 331 Jeditable 331 Masked input 332 Tables 332 Tablesorter 333 jqGrid 333 Flexigrid 334 Images 334 Jcrop 334 Magnify 335 Lightboxes and Modal Dialogs 336 FancyBox 336 Thickbox 336 BlockUI 337 jqModal 338 Table of Contents [ vii ] Charting 338 Flot 338 Sparklines 339 Events 340 hoverIntent 340 Live query 340 Summary 340 Chapter 11: Developing plugins 341 Adding new global functions 341 Adding multiple functions 342 What's the point? 343 Creating a utility method 343 Adding jQuery Object Methods 345 Object Method context 345 Method chaining 348 DOM traversal methods 349 Adding new shortcut methods 354 Method parameters 357 Simple parameters 359 Parameter maps 360 Default parameter values 361 Callback functions 362 Customizable defaults 363 Adding a selector expression 365 Sharing a plugin with the world 368 Naming conventions 368 Use of the $ alias 369 Method interfaces 369 Documentation style 370 Summary 370 Appendix A: Online Resources 371 jQuery documentation 371 jQuery wiki 371 jQuery API 371 jQuery API browser 371 Visual jQuery 372 Adobe AIR jQueryAPI viewer 372 JavaScript reference 372 Mozilla developer center 372 Dev.opera 372 Table of Contents [ viii ] MSDN JScript Reference 372 Quirksmode 373 JavaScript Toolbox 373 JavaScript code compressors 373 YUI Compressor 373 JSMin 373 Pretty printer 374 (X)HTML reference 374 W3C hypertext markup language home page 374 CSS reference 374 W3C cascading style sheets home page 374 Mezzoblue CSS cribsheet 374 Position is everything 375 Useful blogs 375 The jQuery blog 375 Learning jQuery 375 Ajaxian 375 John Resig 375 JavaScript ant 376 Robert's talk 376 Web standards with imagination 376 Snook 376 Matt Snider JavaScript resource 376 I can't 376 DOM scripting 377 As days pass by 377 A list apart 377 Web development frameworks using jQuery 377 Appendix B: Development Tools 379 Tools for Firefox 379 Firebug 379 Web developer toolbar 380 Venkman 380 Regular expressions tester 380 Tools for Internet Explorer 380 Microsoft Internet Explorer Developer Toolbar 380 Microsoft Visual Web Developer 381 DebugBar 381 Drip 381 Table of Contents [ ix ] Tools for Safari 381 Develop Menu 381 Web Inspector 382 Tools for Opera 382 Dragonfly 382 Other tools 382 Firebug Lite 382 NitobiBug 383 TextMate jQuery bundle 383 Charles 383 Fiddler 383 Aptana 383 Appendix C: JavaScript Closures 385 Inner functions 385 The great escape 387 Variable scoping 388 Interactions between closures 390 Closures in jQuery 391 Arguments to $(document).ready() 391 Event handlers 392 Memory leak hazards 394 Accidental reference loops 395 The Internet Explorer memory leak problem 396 The good news 397 Summary 397 Appendix D: Quick Reference 399 Selector expressions 399 DOM traversal methods 401 Event methods 402 Effect methods 404 DOM manipulation methods 405 AJAX methods 408 Miscellaneous methods 409 Index 411 Preface It began as a labor of love back in 2005 by John Resig, a JavaScript wunderkind who now works for the Mozilla Corporation. Inspired by pioneers in the field such as Dean Edwards and Simon Willison, Resig put together a set of functions to make it easy to programmatically find elements on a web page and assign behaviors to them. By the time he first publicly announced his project in January 2006, he had added DOM modification and basic animations. He gave it the name jQuery to emphasize the central role of finding, or "querying," parts of a web page and acting on them with JavaScript. In the few short years since then, jQuery has grown in its feature set, improved in its performance, and gained widespread adoption by some of the most popular sites on the Internet. While Resig remains the lead developer of the project, jQuery has blossomed, in true open-source fashion, to the point where it now boasts a core team of top-notch JavaScript developers, as well as a vibrant community of thousands of developers. The jQuery JavaScript Library can enhance your websites regardless of your background. It provides a wide range of features, an easy-to-learn syntax, and robust cross-platform compatibility in a single compact file. What's more, hundreds of plug-ins have been developed to extend jQuery's functionality, making it an essential tool for nearly every client-side scripting occasion. Learning jQuery provides a gentle introduction to jQuery concepts, allowing you to add interactions and animations to your pages—even if previous attempts at writing JavaScript have left you baffled. This book guides you past the pitfalls associated with AJAX, events, effects, and advanced JavaScript language features, and provides you with a brief reference to the jQuery library to return to again and again. Preface [  ] What this book covers In Chapter 1 you'll get your feet wet with the jQuery JavaScript library. The chapter begins with a description of jQuery and what it can do for you. It walks you through downloading and setting up the library, as well as writing your first script. In Chapter 2 you'll learn how to use jQuery's selector expressions and DOM traversal methods to find elements on the page, wherever they may be. You'll use jQuery to apply styling to a diverse set of page elements, sometimes in a way that pure CSS cannot. In Chapter 3 you'll use jQuery's event-handling mechanism to fire off behaviors when browser events occur. You'll see how jQuery makes it easy to attach events to elements unobtrusively, even before the page finishes loading. And, you'll be introduced to more advanced topics, such as event bubbling, delegation, and namespacing. In Chapter 4 you'll be introduced to jQuery's animation techniques and see how to hide, show, and move page elements with effects that are both useful and pleasing to the eye. In Chapter 5 you'll learn how to change your page on command. This chapter will teach you how to alter the very structure of an HTML document, as well as its content, on the fly. In Chapter 6 you'll discover the many ways in which jQuery makes it easy to access server-side functionality without resorting to clunky page refreshes. In the next three chapters (7, 8, and 9) you'll work through several real-world examples, pulling together what you've learned in previous chapters and creating robust jQuery solutions to common problems. In Chapter 7, "Table Manipulation," you'll sort, sift, and style information to create beautiful and functional data layouts. In Chapter 8, "Forms with Function," you'll master the finer points of client-side validation, design an adaptive form layout, and implement interactive client-server form features such as autocompletion. In Chapter 9, "Shufflers and Rotators," you'll enhance the beauty and utility of page elements by showing them in more manageable chunks. You'll make information fly in and out of view both on its own and under user control. Chapters 10 and 11 take you beyond the core jQuery methods to explore third-party extensions to the library, and show you various ways you can extend the library yourself. Preface [  ] In Chapter 10, "Using Plug-ins," you'll examine the Form plug-in and the official collection of user interface plug-ins known as jQuery UI. You'll also learn where to find many other popular jQuery plug-ins and see what they can do for you. In Chapter 11, "Developing Plug-ins," you'll learn how to take advantage of jQuery's impressive extension capabilities to develop your own plug-ins from the ground up. You'll create your own utility functions, add jQuery object methods, write custom selector expressions, and more. In Appendix A, "Online Resources," you'll find recommendations for a handful of informative websites on a wide range of topics related to jQuery, JavaScript, and web development in general. In Appendix B, "Development Tools," you'll discover a number of useful third-party programs and utilities for editing and debugging jQuery code within your personal development environment. In Appendix C, "JavaScript Closures," you'll gain a solid understanding of closures—what they are and how you can use them to your advantage. In Appendix D, "Quick Reference," you'll get a glimpse of the entire jQuery library, including every one of its methods and selector expressions. Its easy-to-scan format is perfect for those moments when you know what you want to do, but you're just unsure about the right method name or selector. What you need for this book In order to both write and run the code demonstrated in this book, you need the following: A basic text editor. A modern web browser such as Mozilla Firefox, Apple Safari, or Microsoft Internet Explorer. The jQuery source file, version 1.3.1 or later, which can be downloaded from http://jquery.com/. Additionally, to run the AJAX examples in Chapter 6, you will need a PHP-enabled server. • • • Preface [  ] Who is this book for This book is for web designers who want to create interactive elements for their designs, and for developers who want to create the best user interface for their web applications. Basic JavaScript programming knowledge is required. You will need to know the basics of HTML and CSS, and should be comfortable with the syntax of JavaScript. No knowledge of jQuery is assumed, nor is experience with any other JavaScript libraries required. Conventions In this book, you will find a number of styles of text that distinguish between different kinds of information. Here are some examples of these styles, and an explanation of their meaning. Code words in text are shown as follows: "We can include other contexts through the use of the include directive." A block of code will be set as follows: the title

This is a paragraph.

This is another paragraph.

This is yet another paragraph.

When we wish to draw your attention to a particular part of a code block, the relevant lines or items will be made bold: $(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); $('a[href$=.pdf]').addClass('pdflink'); $('a[href^=http][href*=henry]') .addClass('henrylink'); }); Preface [  ] Any command-line input and output is written as follows: outerFn(): Outer function Inner function New terms and important words are introduced in a bold-type font. Words that you see on the screen, in menus or dialog boxes for example, appear in our text like this: "Note the PDF icon to the right of the Hamlet link, the envelope icon next to the email link, and the white background and black border around the Henry V link.". Warnings or important notes appear in a box like this. Tips and tricks appear like this. Reader feedback Feedback from our readers is always welcome. Let us know what you think about this book, what you liked or may have disliked. Reader feedback is important for us to develop titles that you really get the most out of. To send us general feedback, simply drop an email to feedback@packtpub.com, making sure to mention the book title in the subject of your message. If there is a book that you need and would like to see us publish, please send us a note in the SUGGEST A TITLE form on www.packtpub.com or email suggest@packtpub.com. If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide on www.packtpub.com/authors. Customer support Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase. Preface [  ] Downloading the example code for the book Visit http://www.packtpub.com/files/code/6705_Code.zip to directly download the example code. The downloadable files contain instructions on how to use them. Errata Although we have taken every care to ensure the accuracy of our contents, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in text or code—we would be grateful if you would report this to us. By doing this you can save other readers from frustration, and help to improve subsequent versions of this book. If you find any errata, report them by visiting http://www.packtpub. com/support, selecting your book, clicking on the let us know link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata added to the list of existing errata. The existing errata can be viewed by selecting your title from http://www.packtpub.com/support. Piracy Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works in any form on the Internet, please provide the location address or website name immediately so we can pursue a remedy. Please contact us at copyright@packtpub.com with a link to the suspected pirated material. We appreciate your help in protecting our authors, and our ability to bring you valuable content. Questions You can contact us at questions@packtpub.com if you are having a problem with some aspect of the book, and we will do our best to address it. Getting Started Today's World Wide Web is a dynamic environment, and its users set a high bar for both style and function of sites. To build interesting, interactive sites, developers are turning to JavaScript libraries such as jQuery to automate common tasks and simplify complicated ones. One reason the jQuery library is a popular choice is its ability to assist in a wide range of tasks. It can seem challenging to know where to begin because jQuery performs so many different functions. Yet, there is a coherence and symmetry to the design of the library; most of its concepts are borrowed from the structure of HTML and Cascading Style Sheets (CSS). The library's design lends itself to a quick start for designers with little programming experience since many web developers have more experience with these technologies than they do with JavaScript. In fact, in this opening chapter we'll write a functioning jQuery program in just three lines of code. On the other hand, experienced programmers will also be aided by this conceptual consistency, as we'll see in the later, more advanced chapters. So let's look at what jQuery can do for us. What jQuery does The jQuery library provides a general-purpose abstraction layer for common web scripting, and is therefore useful in almost every scripting situation. Its extensible nature means that we could never cover all possible uses and functions in a single book, as plugins are constantly being developed to add new abilities. The core features, though, address the following needs: Access elements in a document. Without a JavaScript library, many lines of code must be written to traverse the Document Object Model (DOM) tree, and locate specific portions of an HTML document's structure. A robust and efficient selector mechanism is offered in jQuery for retrieving the exact piece of the document that is to be inspected or manipulated. • Getting Started [  ] Modify the appearance of a web page. CSS offers a powerful method of influencing the way a document is rendered, but it falls short when web browsers do not all support the same standards. With jQuery, developers can bridge this gap, relying on the same standards support across all browsers. In addition, jQuery can change the classes or individual style properties applied to a portion of the document even after the page has been rendered. Alter the content of a document. Not limited to mere cosmetic changes, jQuery can modify the content of a document itself with a few keystrokes. Text can be changed, images can be inserted or swapped, lists can be reordered, or the entire structure of the HTML can be rewritten and extended—all with a single easy-to-use Application Programming Interface (API). Respond to a user's interaction. Even the most elaborate and powerful behaviors are not useful if we can't control when they take place. The jQuery library offers an elegant way to intercept a wide variety of events, such as a user clicking on a link, without the need to clutter the HTML code itself with event handlers. At the same time, its event-handling API removes browser inconsistencies that often plague web developers. Animate changes being made to a document. To effectively implement such interactive behaviors, a designer must also provide visual feedback to the user. The jQuery library facilitates this by providing an array of effects such as fades and wipes, as well as a toolkit for crafting new ones. Retrieve information from a server without refreshing a page. This code pattern has become known as Asynchronous JavaScript And XML (AJAX), and assists web developers in crafting a responsive, feature-rich site. The jQuery library removes the browser-specific complexity from this process, allowing developers to focus on the server-end functionality. Simplify common JavaScript tasks. In addition to all of the document-specific features of jQuery, the library provides enhancements to basic JavaScript constructs such as iteration and array manipulation. Why jQuery works well With the recent resurgence of interest in dynamic HTML comes a proliferation of JavaScript frameworks. Some are specialized, focusing on just one or two of the above tasks. Others attempt to catalog every possible behavior and animation, and serve these all up pre-packaged. To maintain the wide range of features outlined above while remaining compact, jQuery employs several strategies: • • • • • • Chapter 1 [  ] Leverage knowledge of CSS. By basing the mechanism for locating page elements on CSS selectors, jQuery inherits a terse yet legible way of expressing a document's structure. The jQuery library becomes an entry point for designers who want to add behaviors to their pages because a prerequisite for doing professional web development is knowledge of CSS syntax. Support extensions. In order to avoid "feature creep", jQuery relegates special-case uses to plugins. The method for creating new plugins is simple and well-documented, which has spurred the development of a wide variety of inventive and useful modules. Even most of the features in the basic jQuery download are internally realized through the plugin architecture, and can be removed if desired, yielding an even smaller library. Abstract away browser quirks. An unfortunate reality of web development is that each browser has its own set of deviations from published standards. A significant portion of any web application can be relegated to handling features differently on each platform. While the ever-evolving browser landscape makes a perfectly browser-neutral code base impossible for some advanced features, jQuery adds an abstraction layer that normalizes the common tasks, reducing the size of code, and tremendously simplifying it. Always work with sets. When we instruct jQuery, Find all elements with the class collapsible and hide them, there is no need to loop through each returned element. Instead, methods such as .hide() are designed to automatically work on sets of objects instead of individual ones. This technique, called implicit iteration, means that many looping constructs become unnecessary, shortening code considerably. Allow multiple actions in one line. To avoid overuse of temporary variables or wasteful repetition, jQuery employs a programming pattern called chaining for the majority of its methods. This means that the result of most operations on an object is the object itself, ready for the next action to be applied to it. These strategies have kept the jQuery package slim—under 20 KB compressed— while at the same time providing techniques for keeping our custom code that uses the library compact, as well. The elegance of the library comes about partly by design, and partly due to the evolutionary process spurred by the vibrant community that has sprung up around the project. Users of jQuery gather to discuss not only the development of plugins, but also enhancements to the core library. Appendix A details many of the community resources available to jQuery developers. • • • • • Getting Started [ 10 ] Despite all of the efforts required to engineer such a flexible and robust system, the end product is free for all to use. This open-source project is dually licensed under the GNU Public License (appropriate for inclusion in many other open-source projects) and the MIT License (to facilitate use of jQuery within proprietary software). History of the jQuery project This book covers the functionality and syntax of jQuery 1.3.x, the latest version at the time of writing. The premise behind the library—providing an easy way to find elements on a web page and manipulating them—has not changed over the course of its development, but some syntax details and features have. This brief overview of the project history describes the most significant changes from version to version. Public Development Phase: John Resig first made mention of an improvement on Prototype's "Behaviour" library in August of 2005. This new framework was formally released as jQuery on January 14, 2006. jQuery 1.0 (August 2006): This, the first stable release of the library, already had robust support for CSS selectors, event handling, and AJAX interaction. jQuery 1.1 (January 2007): This release streamlined the API considerably. Many rarely-used methods were combined, reducing the number of methods to learn and document. jQuery 1.1.3 (July 2007): This minor release contained massive speed improvements for jQuery's selector engine. From this version on, jQuery's performance would compare favorably to its fellow JavaScript libraries such as Prototype, Mootools, and Dojo. jQuery 1.2 (September 2007): XPath syntax for selecting elements was removed in this release, as it had become redundant with the CSS syntax. Effect customization became much more flexible in this release, and plugin development became easier with the addition of namespaced events. jQuery UI (September 2007): This new plugin suite was announced to replace the popular but aging Interface plugin. A rich collection of prefabricated widgets was included, as well as a set of tools for building sophisticated elements such as drag-and-drop interfaces. jQuery 1.2.6 (May 2008): The functionality of Brandon Aaron's popular Dimensions plugin was brought into the main library. jQuery 1.3 (January 2009): A major overhaul of the selector engine (Sizzle) provided a huge boost to the library’s performance. Event delegation became formally supported. • • • • • • • • Chapter 1 [ 11 ] Release notes for older jQuery versions can be found on the project's web site at http://docs.jquery.com/History_of_jQuery. Our first jQuery-powered web page Now that we have covered the range of features available to us with jQuery, we can examine how to put the library into action. Downloading jQuery The official jQuery website (http://jquery.com/) is always the most up-to-date resource for code and news related to the library. To get started, we need a copy of jQuery, which can be downloaded right from the home page of the site. Several versions of jQuery may be available at any given moment; the most appropriate for us as site developers will be the latest uncompressed version of the library. This can be replaced with a compressed version in production environments. No installation is required. To use jQuery, we just need to place it on our site in a public location. Since JavaScript is an interpreted language, there is no compilation or build phase to worry about. Whenever we need a page to have jQuery available, we will simply refer to the file's location from the HTML document. Setting up the HTML document There are three pieces to most examples of jQuery usage: the HTML document itself, CSS files to style it, and JavaScript files to act on it. For our first example, we'll use a page with a book excerpt that has a number of classes applied to portions of it. Through the Looking-Glass Getting Started [ 12 ]

Through the Looking-Glass

by Lewis Carroll

1. Looking-Glass House

There was a book lying near Alice on the table, and while she sat watching the White King (for she was still a little anxious about him, and had the ink all ready to throw over him, in case he fainted again), she turned over the leaves, to find some part that she could read, "—for it's all in some language I don't know," she said to herself.

It was like this.

YKCOWREBBAJ

sevot yhtils eht dna ,gillirb sawT'
;ebaw eht ni elbmig dna eryg diD
,sevogorob eht erew ysmim llA
.ebargtuo shtar emom eht dnA

She puzzled over this for some time, but at last a bright thought struck her. "Why, it's a Looking-glass book, of course! And if I hold it up to a glass, the words will all go the right way again."

This was the poem that Alice read.

JABBERWOCKY

'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
Chapter 1 [ 13 ] The actual layout of files on the server does not matter. References from one file to another just need to be adjusted to match the organization we choose. In most examples in this book, we will use relative paths to reference files (../images/foo.png) rather than absolute paths (/images/foo.png). This will allow the code to run locally without the need for a web server. Immediately following the normal HTML preamble, the stylesheet is loaded. For this example, we'll use a spartan one. body { font: 62.5% Arial, Verdana, sans-serif; } h1 { font-size: 2.5em; margin-bottom: 0; } h2 { font-size: 1.3em; margin-bottom: .5em; } h3 { font-size: 1.1em; margin-bottom: 0; } .poem { margin: 0 2em; } .highlight { font-style: italic; border: 1px solid #888; padding: 0.5em; margin: 0.5em 0; background-color: #ffc; } After the stylesheet is referenced, the JavaScript files are included. It is important that the script tag for the jQuery library be placed before the tag for our custom scripts; otherwise, the jQuery framework will not be available when our code attempts to reference it. Getting Started [ 14 ] Throughout the rest of this book, only the relevant portions of HTML and CSS files will be printed. The files in their entirety are available from the book's companion website http://book.learningjquery.com or from the publisher's website http://www.packtpub.com/support. Now we have a page that looks like this: We will use jQuery to apply a new style to the poem text. This example is to demonstrate a simple use of jQuery. In real-world situations, this type of styling could be performed purely with CSS. Adding jQuery Our custom code will go in the second, currently empty, JavaScript file, which we included from the HTML using . For this example, we only need three lines of code: $(document).ready(function() { $('.poem-stanza').addClass('highlight'); }); Chapter 1 [ 15 ] Finding the poem text The fundamental operation in jQuery is selecting a part of the document. This is done with the $() construct. Typically, it takes a string as a parameter, which can contain any CSS selector expression. In this case, we wish to find all parts of the document that have the poem-stanza class applied to them, so the selector is very simple. However, we will cover much more sophisticated options through the course of the book. We will step through the different ways of locating parts of a document in Chapter 2. The $() function is actually a factory for the jQuery object, which is the basic building block we will be working with from now on. The jQuery object encapsulates zero or more DOM elements, and allows us to interact with them in many different ways. In this case, we wish to modify the appearance of these parts of the page, and we will accomplish this by changing the classes applied to the poem text. Injecting the new class The .addClass() method, like most jQuery methods, is named self-descriptively; it applies a CSS class to the part of the page that we have selected. Its only parameter is the name of the class to add. This method, and its counterpart, .removeClass(), will allow us to easily observe jQuery in action as we explore the different selector expressions available to us. For now, our example simply adds the highlight class, which our stylesheet has defined as italicized text with a border. Note that no iteration is necessary to add the class to all the poem stanzas. As we discussed, jQuery uses implicit iteration within methods such as .addClass(), so a single function call is all it takes to alter all of the selected parts of the document. Executing the code Taken together, $() and .addClass() are enough for us to accomplish our goal of changing the appearance of the poem text. However, if this line of code is inserted alone in the document header, it will have no effect. JavaScript code is generally run as soon as it is encountered in the browser, and at the time the header is being processed, no HTML is yet present to style. We need to delay the execution of the code until after the DOM is available for our use. Getting Started [ 16 ] The traditional mechanism for controlling when JavaScript code is run is to call the code from within event handlers. Many handlers are available for user-initiated events, such as mouse clicks and key presses. If we did not have jQuery available for our use, we would need to rely on the onload handler, which fires after the page (along with all of its images) has been rendered. To trigger our code from the onload event, we would place the code inside a function: function highlightPoemStanzas() { $('.poem-stanza').addClass('highlight'); } Then we would attach the function to the event by modifying the HTML tag to reference it: This causes our code to run after the page is completely loaded. There are drawbacks to this approach. We altered the HTML itself to effect this behavior change. This tight coupling of structure and function clutters the code, possibly requiring the same function calls to be repeated over many different pages, or in the case of other events such as mouse clicks, over every instance of an element on a page. Adding new behaviors would then require alterations in multiple places, increasing the opportunity for error and complicating parallel workflows for designers and programmers. To avoid this pitfall, jQuery allows us to schedule function calls for firing once the DOM is loaded—without waiting for images—with the $(document).ready() construct. With our function defined as above, we can write: $(document).ready(highlightPoemStanzas); This technique does not require any HTML modifications. Instead, the behavior is attached entirely from within the JavaScript file. We will learn how to respond to other types of events, divorcing their effects from the HTML structure as well, in Chapter 3. This incarnation is still slightly wasteful, though, because the function highlightPoemStanzas() is defined only to be used immediately, and exactly once. This means that we have used an identifier in the global namespace of functions that we have to remember not to use again, and for little gain. JavaScript, like some other programming languages, has a way around this inefficiency called anonymous functions (sometimes also called lambda functions). Using anonymous functions, we can write the code as it was originally presented: $(document).ready(function() { $('.poem-stanza').addClass('highlight'); }); Chapter 1 [ 17 ] By using the function keyword without a function name, we define a function exactly where it is needed, and not before. This removes clutter and brings us down to three lines of JavaScript. This idiom is extremely convenient in jQuery code, as many methods take a function as an argument and such functions are rarely reusable. When this syntax is used to define an anonymous function within the body of another function, a closure can be created. This is an advanced and powerful concept, but should be understood when making extensive use of nested function definitions as it can have unintended consequences and ramifications on memory use. This topic is discussed fully in Appendix C. The finished product Now that our JavaScript is in place, the page looks like this: The poem stanzas are now italicized and enclosed in boxes, as specified by the alice.css stylesheet, due to the insertion of the highlight class by the JavaScript code. Getting Started [ 18 ] Summary We now have an idea of why a developer would choose to use a JavaScript framework rather than writing all code from scratch, even for the most basic tasks. We also have seen some of the ways in which jQuery excels as a framework, and why we might choose it over other options. We also know in general which tasks jQuery makes easier. In this chapter, we have learned how to make jQuery available to JavaScript code on our web page, use the $() factory function to locate a part of the page that has a given class, call .addClass() to apply additional styling to this part of the page, and invoke $(document).ready() to cause this code to execute upon the loading of the page. The simple example we have been using demonstrates how jQuery works, but is not very useful in real-world situations. In the next chapter, we will expand on the code here by exploring jQuery's sophisticated selector language, finding practical uses for this technique. Selectors The jQuery library harnesses the power of Cascading Style Sheets (CSS) selectors to let us quickly and easily access elements or groups of elements in the Document Object Model (DOM). In this chapter, we will explore a few of these selectors, as well as jQuery's own custom selectors. We'll also look at jQuery's DOM traversal methods that provide even greater flexibility for getting what we want. The Document Object Model One of the most powerful aspects of jQuery is its ability to make selecting elements in the DOM easy. The Document Object Model is a family-tree structure of sorts. HTML, like other markup languages, uses this model to describe the relationships of things on a page. When we refer to these relationships, we use the same terminology that we use when referring to family relationships—parents, children, and so on. A simple example can help us understand how the family tree metaphor applies to a document: the title

This is a paragraph.

This is another paragraph.

This is yet another paragraph.

Selectors [ 20 ] Here, is the ancestor of all the other elements; in other words, all the other elements are descendants of . The and elements are not only descendants, but children of , as well. Likewise, in addition to being the ancestor of and , is also their parent. The

elements are children (and descendants) of

, descendants of and , and siblings of each other. For information on how to visualize the family-tree structure of the DOM using third-party software, see Appendix B. An important point to note before we begin is that the resulting set of elements from selectors and methods is always wrapped in a jQuery object. These jQuery objects are very easy to work with when we want to actually do something with the things that we find on a page. We can easily bind events to these objects and add slick effects to them, as well as chain multiple modifications or effects together. Nevertheless, jQuery objects are different from regular DOM elements or node lists, and as such do not necessarily provide the same methods and properties for some tasks. In the final part of this chapter, therefore, we will look at ways to access the DOM elements that are wrapped in a jQuery object. The $() factory function No matter which type of selector we want to use in jQuery, we always start with the dollar sign and parentheses: $(). Just about anything that can be used in a stylesheet can also be wrapped in quotation marks and placed inside the parentheses, allowing us to apply jQuery methods to the matched set of elements. Making jQuery Play Well with Other JavaScript Libraries In jQuery, the dollar sign $ is simply an "alias" for jQuery. Conflicts could arise if more than one of these libraries were being used in a given page because a $() function is very common in JavaScript libraries. We can avoid such conflicts by replacing every instance of $ with jQuery in our custom jQuery code. Additional solutions to this problem are addressed in Chapter 10. Three building blocks of these selectors are tag name, ID, and class. They can be used either on their own or in combination with other selectors. Here is an example of what each of these three selectors looks like on its own: Chapter 2 [ 21 ] Selector CSS jQuery Description Tag name p $('p') Selects all paragraphs in the document ID #some-id $('#some-id') Selects the single element in the document that has an ID of some-id Class .some-class $('.some-class') Selects all elements in the document that have a class of some-class As mentioned in Chapter 1, when we attach methods to the $() factory function, the elements wrapped in the jQuery object are looped through automatically and implicitly. Therefore, we can usually avoid explicit iteration, such as a for loop, that is so often required in DOM scripting. Now that we have covered the basics, we're ready to start exploring some more powerful uses of selectors. CSS selectors The jQuery library supports nearly all of the selectors included in CSS specifications 1 through 3, as outlined on the World Wide Web Consortium's site: http://www.w3.org/Style/CSS/#specs. This support allows developers to enhance their websites without worrying about which browsers (particularly Internet Explorer 6) might not understand advanced selectors, as long as the browsers have JavaScript enabled. Responsible jQuery developers should always apply the concepts of progressive enhancement and graceful degradation to their code, ensuring that a page will render as accurately, even if not as beautifully, with JavaScript disabled as it does with JavaScript turned on. We will continue to explore these concepts throughout the book. To begin learning how jQuery works with CSS selectors, we'll use a structure that appears on many websites, often for navigation—the nested, unordered list.
  • Comedies
    • As You Like It
    • All's Well That Ends Well
    • A Midsummer Night's Dream
    • Twelfth Night
  • Tragedies Selectors [ 22 ]
    • Hamlet
    • Macbeth
    • Romeo and Juliet
  • Histories
Notice that the first
    has an ID of selected-plays, but none of the
  • tags have a class associated with them. Without any styles applied, the list looks like this: The nested list appears as we would expect it to—a set of bulleted items arranged vertically and indented according to their level. Chapter 2 [ 23 ] Styling list-item levels Lets suppose that we want the top-level items, and only the top-level items, to be arranged horizontally. We can start by defining a horizontal class in the stylesheet: .horizontal { float: left; list-style: none; margin: 10px; } The horizontal class floats the element to the left of the one following it, removes the bullet from it if it's a list item, and adds a 10 pixel margin on all sides of it. Rather than attaching the horizontal class directly in our HTML, we'll add it dynamically to the top-level list items only—Comedies, Tragedies, and Histories—to demonstrate jQuery's use of selectors: $(document).ready(function() { $('#selected-plays > li').addClass('horizontal'); }); As discussed in Chapter 1, we begin the jQuery code with the $(document). ready() wrapper, that runs as soon as the DOM has loaded. The second line uses the child combinator (>) to add the horizontal class to all top-level items only. In effect, the selector inside the $() function is saying, find each list item (li) that is a child (>) of the element with an ID of selected-plays (#selected-plays). With the class now applied, our nested list looks like this: Selectors [ 24 ] Styling all of the other items—those that are not in the top level—can be done in a number of ways. Since we have already applied the horizontal class to the top- level items, one way to select all sub-level items is to use a negation pseudo-class to identify all list items that do not have a class of horizontal. Note the addition of the third line of code: $(document).ready(function() { $('#selected-plays > li').addClass('horizontal'); $('#selected-plays li:not(.horizontal)').addClass('sub-level'); }); This time we are selecting every list item (li) that: 1. Is a descendant of the element with an ID of selected-plays (#selected-plays) 2. Does not have a class of horizontal (:not(.horizontal)) When we add the sub-level class to these items, they receive the shaded background defined in the stylesheet. Now the nested list looks like this: Attribute selectors Attribute selectors are a particularly helpful subset of CSS selectors. They allow us to specify an element by one of its HTML properties, such as a link's title attribute or an image's alt attribute. For example, to select all images that have an alt attribute, we write the following: $('img[alt]') In versions prior to 1.2, jQuery used XML Path Language (XPath) syntax for its attribute selectors and included a handful of other XPath selectors. While these basic XPath selectors have since been removed from the core jQuery library, they are still available as a plugin: http://plugins.jquery.com/project/xpath/ Chapter 2 [ 25 ] Styling links Attribute selectors accept a wildcard syntax inspired by regular expressions for identifying the value at the beginning (^) or ending ($) of a string. They can also take an asterisk (*) to indicate the value at an arbitrary position within a string or an exclamation mark to indicate a negated value. Let's say we want to have different styles for different types of links. We first define the styles in our stylesheet: a { color: #00c; } a.mailto { background: url(images/mail.png) no-repeat right top; padding-right: 18px; } a.pdflink { background: url(images/pdf.png) no-repeat right top; padding-right: 18px; } a.henrylink { background-color: #fff; padding: 2px; border: 1px solid #000; } Then, we add the three classes—mailto, pdflink, and henrylink—to the appropriate links using jQuery. To add a class for all email links, we construct a selector that looks for all anchor elements (a) with an href attribute ([href) that begins with mailto: (^=mailto:]), as follows: $(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); }); To add a class for all links to PDF files, we use the dollar sign rather than the caret symbol. This is because we're selecting links with an href attribute that ends with .pdf: $(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); $('a[href$=.pdf]').addClass('pdflink'); }); Selectors [ 26 ] Attribute selectors can be combined as well. We can, for example, add a henrylink class for all links with an href value that both starts with http and contains henry anywhere: $(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); $('a[href$=.pdf]').addClass('pdflink'); $('a[href^=http][href*=henry]') .addClass('henrylink'); }); With the three classes applied to the three types of links, we should see the following: Note the PDF icon to the right of the Hamlet link, the envelope icon next to the email link, and the white background and black border around the Henry V link. Custom selectors To the wide variety of CSS selectors, jQuery adds its own custom selectors. Most of the custom selectors allow us to pick certain elements out of a line-up, so to speak. The syntax is the same as the CSS pseudo-class syntax, where the selector starts with a colon (:). For example, to select the second item from a matched set of div selectors with a class of horizontal, we write this: $('div.horizontal:eq(1)') Note that :eq(1) selects the second item in the set because JavaScript array numbering is zero-based, meaning that it starts with 0. In contrast, CSS is one-based, so a CSS selector such as $('div:nth-child(1)') would select all div selectors that are the first child of their parent (in this case, however, we would probably use $('div:first-child') instead). Chapter 2 [ 27 ] Styling alternate rows Two very useful custom selectors in the jQuery library are :odd and :even. Let's take a look at how we can use one of them for basic table striping, given the following table:
    As You Like It Comedy
    All's Well that Ends Well Comedy 1601
    Hamlet Tragedy 1604
    Macbeth Tragedy 1606
    Romeo and Juliet Tragedy 1595
    Henry IV, Part I History 1596
    Henry V History 1599
    Selectors [ 28 ] Now we can add a style to the stylesheet for all table rows, and use an alt class for the even rows: tr { background-color: #fff; } .alt { background-color: #ccc; } Finally, we write our jQuery code, attaching the class to the even-numbered table rows ( tags): $(document).ready(function() { $('tr:odd').addClass('alt'); }); But wait! Why use the :odd selector for even-numbered rows? Well, just as with the :eq() selector, the :odd and :even selectors use JavaScript's native zero-based numbering. Therefore, the first row counts as 0 (even) and the second row counts as 1 (odd), and so on. With this in mind, we can expect our simple bit of code to produce a table that looks like this: Note that we may see unintended results if there is more than one table on a page. For example, since the last row in this table has a white background, the first row in the next table would have the "alternate" gray background. One way to avoid this type of problem is to use the :nth-child() selector instead. This selector can take either a number, odd, or even as its argument. Notably, however, :nth-child() is the only jQuery selector that is one-based. To achieve the same row striping as we did above, and to make it consistent across multiple tables in a document, the code would look like this: $(document).ready(function() { $('tr:nth-child(even)').addClass('alt'); }); :input Input, textarea, select, and button elements name (excluding the colon). For example, :text selects Input elements with a type attribute equal to the selector password, :file :image, :submit, :reset, : :text, :checkbox, :radio, Selector Match these selectors: selecting just the elements we need. The following table describes a handful of When working with forms, jQuery's custom selectors can make short work of Form selectors server-side code. content is generated dynamically and we don’t have access to either the HTML or along with CSS, is a great alternative for this type of styling in cases where the jQuery—or any client‑side programming, for that matter. Nevertheless, jQuery, Admittedly, there are ways to achieve the row striping and text highlighting without contains(henry)') instead, without the uppercase "H," would select no cells. It's important to note that the :contains() selector is case sensitive. Using $('td: prominently featured: So, now we can see our lovely striped table with the Henry plays }); $('td:contains(Henry)').addClass('highlight'); $('tr:nth-child(even)').addClass('alt'); $(document).ready(function() { to our jQuery code, using the :contains() selector. ( .highlight {font-weight:bold; font-style: italics;} )—is add a line do—after adding a class to the stylesheet to make the text bold and italicized highlight any table cell that referred to one of the Henry plays. All we have to For one fnal custom-selector touch, let's suppose for some reason we want to [ 29 ] Chapter 2 Selectors [ 30 ] Selector Match :button Button elements and input elements with a type attribute equal to button :enabled Form elements that are enabled :disabled Form elements that are disabled :checked Radio buttons or checkboxes that are checked :selected Option elements that are selected As with the other selectors, form selectors can be combined for greater specificity. We can, for example, select all checked radio buttons (but not checkboxes) with $(':radio:checked') or select all password inputs and disabled text inputs with $(':password, :text:disabled'). Even with custom selectors, we use the same basic principles of CSS to build the list of matched elements. DOM traversal methods The jQuery selectors that we have explored so far allow us to select a set of elements as we navigate across and down the DOM tree and filter the results. If this were the only way to select elements, our options would be quite limited (although, frankly, the selector expressions are robust in their own right, especially when compared to the regular DOM scripting options). There are many occasions when selecting a parent or ancestor element is essential; that is where jQuery's DOM traversal methods come into play. With these methods at our disposal, we can go up, down, and all around the DOM tree with ease. Some of the methods have a nearly identical counterpart among the selector expressions. For example, the line we first used to add the alt class, $('tr:odd'). addClass('alt');, could be rewritten with the .filter() method as follows: $('tr').filter(':odd').addClass('alt'); For the most part, however, the two ways of selecting elements complement each other. Also, the .filter() method in particular has enormous power because it can take a function as its argument. The function allows us to create complex tests for whether elements should be kept in the matched set. Let's suppose, for example, we want to add a class to all external links. jQuery has no selector for this sort of case. Without a filter function, we'd be forced to explicitly loop through each element, testing each one separately. With the following filter function, however, we can still rely on jQuery's implicit iteration and keep our code compact: $('a').filter(function() { return this.hostname && this.hostname != location.hostname; }).addClass('external'); Chapter 2 [ 31 ] The second line filters the set of elements by two criteria: 1. They must have an href attribute with a domain name (this.hostname). We use this test to exclude mailto links and others of its ilk. 2. The domain name that they link to (again, this.hostname) must not match (!=) the domain name of the current page (location.hostname). More precisely, the .filter() method iterates through the matched set of elements, testing the return value of the function against each one. If the function returns false, the element is removed from the matched set. If it returns true, the element is kept. Now let's take a look at our striped table again to see what else is possible with traversal methods. Styling specific cells Earlier we added a highlight class to all cells containing the text Henry. To instead style the cell next to each cell containing Henry, we can begin with the selector that we have already written, and simply chain the next() method to it: $(document).ready(function() { $('td:contains(Henry)').next().addClass('highlight'); }); The table should now look like this: The .next() method selects only the very next sibling element. To highlight all of the cells following the one containing Henry, we could use the .nextAll() method instead. $(document).ready(function() { $('td:contains(Henry)').nextAll().addClass('highlight'); }); Selectors [ 32 ] As we might expect, the .next() and .nextAll() methods have counterparts: .prev() and .prevAll(). Additionally, .siblings() selects all other elements at the same DOM level, regardless of whether they come before or after the previously selected element. To include the original cell (the one that contains Henry) along with the cells that follow, we can add the .andSelf() method: $(document).ready(function() { $('td:contains(Henry)').nextAll().andSelf().addClass('highlight'); }); To be sure, there are a multitude of selector and traversal-method combinations by which we can select the same set of elements. Here, for example, is another way to select every cell in each row where at least one of the cells contains Henry: $(document).ready(function() { $('td:contains(Henry)').parent().children().addClass('highlight'); }); Here, rather than traversing across to sibling elements, we travel up one level in the DOM to the with .parent() and then select all of the row's cells with .children(). Chaining The traversal-method combinations that we have just explored illustrate jQuery's chaining capability. It is possible with jQuery to select multiple sets of elements and do multiple things with them, all within a single line of code. This chaining not only helps keep jQuery code concise, but it also can improve a script's performance when the alternative is to re-specify a selector. It is also possible to break a single line of code into multiple lines for greater readability. For example, a single chained sequence of methods could be written as one line … $('td:contains(Henry)').parent().find('td:eq(1)') .addClass('highlight').end().find('td:eq(2)') .addClass('highlight'); Chapter 2 [ 33 ] … or as seven lines … $('td:contains(Henry)') // Find every cell containing "Henry" .parent() // Select its parent .find('td:eq(1)') // Find the 2nd descendant cell .addClass('highlight') // Add the "highlight" class .end() // Return to the parent of the cell containing "Henry" .find('td:eq(2)') // Find the 3rd descendant cell .addClass('highlight'); // Add the "highlight" class Admittedly, the DOM traversal in this example is circuitous to the point of absurdity. We certainly wouldn't recommend using it, as there are clearly simpler, more direct methods at our disposal. The point of the example is simply to demonstrate the tremendous flexibility that chaining affords us. Chaining can be like speaking a whole paragraph's worth of words in a single breath—it gets the job done quickly, but it can be hard for someone else to understand. Breaking it up into multiple lines and adding judicious comments can save more time in the long run. Accessing DOM elements Every selector expression and most jQuery methods return a jQuery object. This is almost always what we want, because of the implicit iteration and chaining capabilities that it affords. Still, there may be points in our code when we need to access a DOM element directly. For example, we may need to make a resulting set of elements available to another JavaScript library. Or we might need to access an element's tag name, which is available as a property of the DOM element. For these admittedly rare situations, jQuery provides the .get() method. To access the first DOM element referred to by a jQuery object, we would use .get(0). If the DOM element is needed within a loop, we would use .get(index). So, if we want to know the tag name of an element with id="my-element", we would write: var myTag = $('#my-element').get(0).tagName; For even greater convenience, jQuery provides a shorthand for .get(). Instead of writing the above line, we can use square brackets immediately following the selector: var myTag = $('#my-element')[0].tagName; It's no accident that this syntax looks like an array of DOM elements; using the square brackets is like peeling away the jQuery wrapper to get at the node list, while including the index (in this case, 0) is like plucking out a DOM element itself. Selectors [ 34 ] Summary With the techniques that we have covered in this chapter, we should now be able to style top-level and sub-level items in a nested list by using basic CSS selectors, apply different styles to different types of links by using attribute selectors, add rudimentary striping to a table by using either the custom jQuery selectors :odd and :even or the advanced CSS selector :nth-child(), and highlight text within certain table cells by chaining jQuery methods. So far, we have been using the $(document).ready() event to add a class to a matched set of elements. In the next chapter, we'll explore ways in which to add a class in response to a variety of user-initiated events. Events JavaScript has several built-in ways of reacting to user interaction and other events. To make a page dynamic and responsive, we need to harness this capability so that we can, at the appropriate times, use the jQuery techniques we have learned so far and the other tricks we'll learn later. While we could do this with vanilla JavaScript, jQuery enhances and extends the basic event handling mechanisms to give them a more elegant syntax while at the same time making them more powerful. Performing tasks on page load We have already seen how to make jQuery react to the loading of a web page. The $(document).ready() event handler can be used to fire off a function's worth of code, but there's a bit more to be said about it. Timing of code execution In Chapter 1, we noted that $(document).ready() was jQuery's way to perform tasks that were typically triggered by JavaScript's built-in onload event. While the two have a similar effect, however, they trigger actions at subtly different times. The window.onload event fires when a document is completely downloaded to the browser. This means that every element on the page is ready to be manipulated by JavaScript, which is a boon for writing featureful code without worrying about load order. On the other hand, a handler registered using $(document).ready() is invoked when the DOM is completely ready for use. This also means that all elements are accessible by our scripts, but does not mean that every associated file has been downloaded. As soon as the HTML has been downloaded and parsed into a DOM tree, the code can run. Events [ 36 ] To ensure that the page has also been styled before the JavaScript code executes, it is a good practice to place tags prior to First, the other library (Prototype in this example) is included. Then, jQuery itself is included, taking over $ for its own use. Next, a call to .noConflict() frees up $, so that control of it reverts to the first included library (Prototype). Now in our custom script we can use both libraries—but whenever we need to use a jQuery method, we need to use jQuery instead of $ as an identifier. The .ready() method has one more trick up its sleeve to help us in this situation. The callback function we pass to it can take a single parameter: the jQuery object itself. This allows us to effectively rename it without fear of conflicts: jQuery(document).ready(function($) { // In here, we can use $ like normal! }); Chapter 3 [ 39 ] Or, using the shorter syntax we learned above: jQuery(function($) { // Code that uses $. }); Simple events There are many other times apart from the loading of the page at which we might want to perform a task. Just as JavaScript allows us to intercept the page load event with or window.onload, it provides similar hooks for user-initiated events such as mouse clicks (onclick), form fields being modified (onchange), and windows changing size (onresize). When assigned directly to elements in the DOM, these hooks have similar drawbacks to the ones we outlined for onload. Therefore, jQuery offers an improved way of handling these events as well. A simple style switcher To illustrate some event handling techniques, suppose we wish to have a single page rendered in several different styles based on user input. We will allow the user to click buttons to toggle between a normal view, a view in which the text is constrained to a narrow column, and a view with large print for the content area. In a real-world example, a good web citizen will employ the principle of progressive enhancement here. The style switcher should either be hidden when JavaScript is unavailable or, better yet, should still function through links to alternative versions of the page. For the purposes of this tutorial, we'll assume that all users have JavaScript turned on. The HTML markup for the style switcher is as follows:

    Style Switcher

    Default
    Narrow Column
    Large Print
    Events [ 40 ] Combined with the rest of the page's HTML markup and some basic CSS, we get a page that looks like the following figure: To begin, we'll make the Large Print button operate. We need a bit of CSS to implement our alternative view of the page: body.large .chapter { font-size: 1.5em; } Our goal, then, is to apply the large class to the tag. This will allow the stylesheet to reformat the page appropriately. Using what we learned in Chapter 2, we already know the statement needed to accomplish this: 'body').addClass('large'); However, we want this to occur when the button is clicked, not when the page loaded as we have seen so far. To do this, we'll introduce the .bind() method. This method allows us to specify any JavaScript event, and to attach a behavior to it. In this case, the event is called click, and the behavior is a function consisting of our one-liner above: $(document).ready(function() { $('#switcher-large').bind('click', function() { $('body').addClass('large'); }); }); Chapter 3 [ 41 ] Now when the button gets clicked, our code runs and the text is enlarged as shown in the following figure: That's all there is to binding an event. The advantages we discussed with the .ready() method apply here, as well. Multiple calls to .bind() coexist nicely, appending additional behaviors to the same event as necessary. This is not necessarily the most elegant or efficient way to accomplish this task. As we proceed through this chapter, we will extend and refine this code into something we can be proud of. Enabling the other buttons We now have a Large Print button that works as advertised, but we need to apply similar handling to the other two buttons (Default and Narrow Column) to make them perform their tasks. This is straightforward; we use .bind() to add a click handler to each of them, removing and adding classes as necessary. The new code reads as follows: $(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass('narrow'); $('body').removeClass('large'); }); $('#switcher-narrow').bind('click', function() { Events [ 42 ] $('body').addClass('narrow'); $('body').removeClass('large'); }); $('#switcher-large').bind('click', function() { $('body').removeClass('narrow'); $('body').addClass('large'); }); }); This is combined with a CSS rule for the narrow class: body.narrow .chapter { width: 400px; } Now, after clicking the Narrow Column button, its corresponding CSS is applied and the text gets laid out differently as shown in the following figure: Clicking Default removes both class names from the tag, returning the page to its initial rendering. Chapter 3 [ 43 ] Event handler context Our switcher is behaving correctly, but we are not giving the user any feedback about which button is currently active. Our approach for handling this will be to apply the selected class to the button when it is clicked, and remove this class from the other buttons. The selected class simply makes the button's text bold: .selected { font-weight: bold; } We could accomplish this class modification as we do above, by referring to each button by ID and applying or removing classes as necessary, but instead we'll explore a more elegant and scalable solution that exploits the context in which event handlers run. When any event handler is triggered, the keyword this refers to the DOM element to which the behavior was attached. Earlier we noted that the $() factory function could take a DOM element as its argument; this is one of the key reasons that facility is available. By writing $(this) within the event handler, we create a jQuery object corresponding to the element, and can act on it just as if we had located it with a CSS selector. With this in mind, we can write: $(this).addClass('selected'); Placing this line in each of the three handlers will add the class when a button is clicked. To remove the class from the other buttons, we can take advantage of jQuery's implicit iteration feature, and write: $('#switcher .button').removeClass('selected'); This line removes the class from every button inside the style switcher. So, placing these in the correct order, we have the code as: $(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass('narrow'); $('body').removeClass('large'); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow'); $('body').removeClass('large'); $('#switcher .button').removeClass('selected'); Events [ 44 ] $(this).addClass('selected'); }); $('#switcher-large').bind('click', function() { $('body').removeClass('narrow'); $('body').addClass('large'); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); }); Now the style switcher gives appropriate feedback as shown in the following figure: Generalizing the statements by using the handler context allows us to be yet more efficient. We can factor the highlighting routine out into a separate handler, as shown in the following code, because it is the same for all three buttons: $(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass('narrow').removeClass('large'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow').removeClass('large'); }); $('#switcher-large').bind('click', function() { Chapter 3 [ 45 ] $('body').removeClass('narrow').addClass('large'); }); $('#switcher .button').bind('click', function() { $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); }); This optimization takes advantage of the three jQuery features we have discussed. First, implicit iteration is once again useful when we bind the same click handler to each button with a single call to .bind(). Second, behavior queuing allows us to bind two functions to the same click event, without the second overwriting the first. Lastly, we're using jQuery's chaining capabilities to collapse the adding and removing of classes into a single line of code each time. Further consolidation The code optimization we've just completed is an example of refactoring—modifying existing code to perform the same task in a more efficient or elegant way. To explore further refactoring opportunities, let's look at the behaviors we have bound to each button. The .removeClass() method's parameter is optional; when omitted, it removes all classes from the element. We can streamline our code a bit by exploiting this as follows: $(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass(); }); $('#switcher-narrow').bind('click', function() { $('body').removeClass().addClass('narrow'); }); $('#switcher-large').bind('click', function() { $('body').removeClass().addClass('large'); }); $('#switcher .button').bind('click', function() { $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); }); Note that the order of operations has changed a bit to accommodate our more general class removal; we need to execute .removeClass() first so that it doesn't undo the .addClass() we perform in the same breath. Events [ 46 ] We can only safely remove all classes because we are in charge of the HTML in this case. When we are writing code for reuse (such as for a plugin), we need to respect any classes that might be present and leave them intact. Now we are executing some of the same code in each of the buttons' handlers. This can be easily factored out into our general button click handler: $(document).ready(function() { $('#switcher .button').bind('click', function() { $('body').removeClass(); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow'); }); $('#switcher-large').bind('click', function() { $('body').addClass('large'); }); }); Note that we need to move the general handler above the specific ones now. The .removeClass() needs to happen before the .addClass(), and we can count on this because jQuery always triggers event handlers in the order in which they were registered. Finally, we can get rid of the specific handlers entirely by once again exploiting event context. Since the context keyword this gives us a DOM element rather than a jQuery object, we can use native DOM properties to determine the ID of the element that was clicked. We can thus bind the same handler to all the buttons, and within the handler perform different actions for each button: $(document).ready(function() { $('#switcher .button').bind('click', function() { $('body').removeClass(); if (this.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (this.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); }); Chapter 3 [ 47 ] Shorthand events Binding a handler for an event (like a simple click event) is such a common task that jQuery provides an even terser way to accomplish it; shorthand event methods work in the same way as their .bind() counterparts with a couple fewer keystrokes. For example, our style switcher could be written using .click() instead of .bind() as follows: $(document).ready(function() { $('#switcher .button').click(function() { $('body').removeClass(); if (this.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (this.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); }); Shorthand event methods such as this exist for all standard DOM events: blur change click dblclick error focus keydown keypress keyup load mousedown mousemove mouseout mouseover mouseup • • • • • • • • • • • • • • • Events [ 48 ] resize scroll select submit unload Each shortcut method binds a handler to the event with the corresponding name. Compound events Most of jQuery's event-handling methods correspond directly to native JavaScript events. A handful, however, are custom handlers added for convenience and cross-browser optimization. One of these, the .ready() method, we have discussed in detail already. The .toggle() and .hover() methods are two more custom event handlers; they are both referred to as compound event handlers because they intercept combinations of user actions, and respond to them using more than one function. Showing and hiding advanced features Suppose that we wanted to be able to hide our style switcher when it is not needed. One convenient way to hide advanced features is to make them collapsible. We will allow one click on the label to hide the buttons, leaving the label alone. Another click on the label will restore the buttons. We need another class to handle the hidden buttons: .hidden { display: none; } We could implement this feature by storing the current state of the buttons in a variable, and checking its value each time the label is clicked to know whether to add or remove the hidden class on the buttons. We could also directly check for the presence of the class on a button, and use this information to decide what to do. Instead, jQuery provides the .toggle() method, which performs this housekeeping task for us. There are in fact two .toggle() methods defined by jQuery. For information on the effect method of this name (which is distinguished by different argument types), see: http://docs.jquery.com/ Effects/toggle • • • • • Chapter 3 [ 49 ] The .toggle() event method takes two or more arguments, each of which is a function. The first click on the element causes the first function to execute, the second click triggers the second function, and so forth. Once each function has been invoked, the cycle begins again from the first function. With .toggle(), we can implement our collapsible style switcher quite easily: $(document).ready(function() { $('#switcher h3').toggle(function() { $('#switcher .button').addClass('hidden'); }, function() { $('#switcher .button').removeClass('hidden'); }); }); After the first click, the buttons are all hidden: And a second click returns them to visibility: Once again we rely on implicit iteration; this time, to hide all the buttons in one fell swoop without requiring an enclosing element. For this specific case, jQuery provides another mechanism for the collapsing we are performing. We can use the .toggleClass() method to automatically check for the presence of the class before applying or removing it: $(document).ready(function() { $('#switcher h3').click(function() { $('#switcher .button').toggleClass('hidden'); }); }); In this case, .toggleClass() is probably the more elegant solution, but .toggle() is a more versatile way to perform two or more different actions in alternation. Events [ 50 ] Highlighting clickable items In illustrating the ability of the click event to operate on normally non-clickable page elements, we have crafted an interface that gives few hints that the buttons—actually just
    elements—are actually live parts of the page, awaiting user interaction. To remedy this, we can give the buttons a rollover state, making it clear that they interact in some way with the mouse: #switcher .hover { cursor: pointer; background-color: #afa; } The CSS specification includes a pseudo-class called :hover, which allows a stylesheet to affect an element's appearance when the user's mouse cursor hovers over it. In Internet Explorer 6, this capability is restricted to link elements, so we can't use it for other items in cross-browser code. Instead, jQuery allows us to use JavaScript to change an element's styling—and indeed, perform any arbitrary action—both when the mouse cursor enters the element and when it leaves the element. The .hover() method takes two function arguments, just as in our .toggle() example above. In this case, the first function will be executed when the mouse cursor enters the selected element, and the second is fired when the cursor leaves. We can modify the classes applied to the buttons at these times to achieve a rollover effect: $(document).ready(function() { $('#switcher .button').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }); }); We once again use implicit iteration and event context for short, simple code. Now when hovering over any button, we see our class applied as shown in the following screenshot: Chapter 3 [ 51 ] The use of .hover() also means we avoid headaches caused by event propagation in JavaScript. To understand this, we need to take a look at how JavaScript decides which element gets to handle a given event. The journey of an event When an event occurs on a page, an entire hierarchy of DOM elements gets a chance to handle the event. Consider a page model like this:
    The quick brown fox jumps over the lazy dog.

    How razorback-jumping frogs can level six piqued gymnasts!

    We then visualize the code as a set of nested elements as shown in the following figure:

    For any event, there are multiple elements that could logically be responsible for reacting. When the link on this page is clicked, for example, the

    , , and all should get the opportunity to respond to the click. After all, the three are all under the user's mouse cursor at the time. The

    element, on the other hand, is not part of this interaction at all. Events [ 52 ] One strategy for allowing multiple elements to respond to a click is called event capturing. With event capturing, the event is first given to the most all-encompassing element, and then to successively more specific ones. In our example, this means that first the

    gets passed the event, then the , and finally the .

    Capturing Technically, in browser implementations of event capturing, specific elements register to listen for events that occur among their descendants. The approximation provided here is close enough for our needs. The opposite strategy is called event bubbling. The event gets sent to the most specific element, and after this element has an opportunity to react, the event bubbles up to more general elements. In our example, the would be handed the event first, and then the and

    in that order.

    Bubbling Unsurprisingly, different browser developers originally decided on different models for event propagation. The DOM standard that eventually developed thus specified that both strategies should be used: first the event is captured from general elements to specific ones, and then the event bubbles back up to the top of the DOM tree. Event handlers can be registered for either part of the process. Not all browsers have been updated to match this new standard, and in those that support capturing it typically must be specifically enabled. To provide cross-browser consistency, therefore, jQuery always registers event handlers for the bubbling phase of the model. We can always assume that the most specific element will get the first opportunity to respond to any event. Chapter 3 [ 53 ] Side effects of event bubbling Event bubbling can cause unexpected behavior, especially when the wrong element responds to a mouseover or mouseout. Consider a mouseout event handler attached to the

    in our example. When the user's mouse cursor exits the
    , the mouseout handler is run as anticipated. Since this is at the top of the hierarchy, no other elements get the event. On the other hand, when the cursor exits the element, a mouseout event is sent to that. This event will then bubble up to the and then to the
    , firing the same event handler. This bubbling sequence is likely not desired; for the buttons in our style switcher example, it could mean the highlight was turned off prematurely. The .hover() method is aware of these bubbling issues, and when we use that method to attach events, we can ignore the problems caused by the wrong element getting a mouseover or mouseout event. This makes .hover() a very attractive alternative to binding the individual mouse events. If action only needs to be taken when the mouse enters or leaves an element, but not both, we can bind jQuery's mouseenter and mouseleave events, which also circumvent bubbling concerns. These events are paired so often, however, that .hover() is generally the right choice. The mouseout scenario just described illustrates the need to constrain the scope of an event. While .hover() handles this specific case, we will encounter other situations in which we need to limit an event spatially (preventing the event from being sent to certain elements) or temporally (preventing the event from being sent at certain times). Altering the journey: the event object We have already seen one situation in which event bubbling can cause problems. To show a case in which .hover() does not help our cause, we'll alter the collapsing behavior we implemented earlier. Suppose we wish to expand the clickable area that triggers the collapsing or expanding of the style switcher. One way to do this is to move the event handler from the label,

    , to its containing
    element: $(document).ready(function() { $('#switcher').click(function() { $('#switcher .button').toggleClass('hidden'); }); }); Events [ 54 ] This alteration makes the entire area of the style switcher clickable to toggle its visibility. The downside is that clicking on a button also collapses the style switcher after the style on the content has been altered. This is due to event bubbling; the event is first handled by the buttons, then passed up to the DOM tree until it reaches the
    , where our new handler is activated and hides the buttons. To solve this problem, we need access to the event object. This is a JavaScript construct that is passed to each element's event handler when it is invoked. It provides information about the event, such as where the mouse cursor was at the time of the event. It also provides some methods that can be used to affect the progress of the event through the DOM. To use the event object in our handlers, we only need to add a parameter to the function: $(document).ready(function() { $('#switcher').click(function(event) { $('#switcher .button').toggleClass('hidden'); }); }); Event targets Now we have the event object available to us as the variable event within our handler. The property event.target can be helpful in controlling where an event takes effect. This property is a part of the DOM API, but is not implemented in all browsers; jQuery extends the event object as necessary to provide the property in every browser. With .target, we can determine which element in the DOM was the first to receive the event—that is, in the case of a click event, the actual item clicked on. Remembering that this gives us the DOM element handling the event, we can write the following code: $(document).ready(function() { $('#switcher').click(function(event) { if (event.target == this) { $('#switcher .button').toggleClass('hidden'); } }); }); Chapter 3 [ 55 ] This code ensures that the item clicked on was
    , not one of its sub‑elements. Now clicking on buttons will not collapse the style switcher, and clicking on the switcher's background will. However, clicking on the label,

    , now does nothing, because it too is a sub‑element. Instead of placing this check here, we can modify the behavior of the buttons to achieve our goals. Stopping event propagation The event object provides the .stopPropagation() method, which can halt the bubbling process completely for the event. Like .target, this method is a plain JavaScript feature, but cannot be safely used across all browsers. As long as we register all of our event handlers using jQuery, though, we can use it with impunity. We'll remove the event.target == this check we just added, and instead add some code in our buttons' click handlers: $(document).ready(function() { $('#switcher .button').click(function(event) { $('body').removeClass(); if (this.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (this.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); event.stopPropagation(); }); }); As before, we need to add a parameter to the function we're using as the click handler, so we have access to the event object. Then we simply call event. stopPropagation() to prevent any other DOM element from responding to the event. Now our click is handled by the buttons, and only the buttons; clicks anywhere else on the style switcher will collapse or expand it. Events [ 56 ] Default actions Were our click event handler registered on a link element () rather than a generic
    , we would face another problem. When a user clicks on a link, the browser loads a new page. This behavior is not an event handler in the same sense as the ones we have been discussing; instead, this is the default action for a click on a link element. Similarly, when the Enter key is pressed while the user is editing a form, the submit event is triggered on the form, but then the form submission actually occurs after this. If these default actions are undesired, calling .stopPropagation() on the event will not help. These actions occur nowhere in the normal flow of event propagation. Instead, the .preventDefault() method will serve to stop the event in its tracks before the default action is triggered. Calling .preventDefault() is often useful after we have done some tests on the environment of the event. For example, during a form submission we might wish to check that required fields are filled in, and prevent the default action only if they are not. We'll see this in action in Chapter 8. Event propagation and default actions are independent mechanisms; either can be stopped while the other still occurs. If we wish to halt both, we can return false from our event handler, which is a shortcut for calling both .stopPropagation() and .preventDefault() on the event. Event delegation Event bubbling isn't always a hindrance; we can often use it to great benefit. One great technique that exploits bubbling is called event delegation. With it, we can use an event handler on a single element to do the work of many. In jQuery 1.3, a new pair of methods, .live() and .die(), have been introduced. These methods perform the same tasks as .bind() and .unbind(), but behind the scenes they use event delegation to gain the benefits we’ll describe in this section. Documentation on these methods can be found at: http://docs.jquery.com/Events/live In our example, there are just three
    elements that have attached click handlers. But what if there were many? This is more common than one might think. Consider, for example, a large table of information in which each row has an interactive item requiring a click handler. Implicit iteration makes assigning all of these click handlers easy, but performance can suffer because of Chapter 3 [ 57 ] the looping being done internally to jQuery, and because of the memory footprint of maintaining all the handlers. Instead, we can assign a single click handler to an ancestor element in the DOM. An uninterrupted click event will eventually reach the ancestor due to event bubbling, and we can do our work there. As an example, let's apply this technique to our style switcher (even though the number of items does not demand the approach). As seen above, we can use the event.target property to check what element was under the mouse cursor when the click occurred. $(document).ready(function() { $('#switcher').click(function(event) { if ($(event.target).is('.button')) { $('body').removeClass(); if (event.target.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (event.target.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(event.target).addClass('selected'); event.stopPropagation(); } }); }); We've used a new method here, called .is(). This method accepts the selector expressions we investigated in the previous chapter, and tests the current jQuery object against the selector. If at least one element in the set is matched by the selector, .is() returns true. In this case, $(event.target).is('.button') asks whether the element clicked has a class of button assigned to it. If so, we proceed with the code from before, with one significant alteration: the keyword this now refers to
    , so every time we are interested in the clicked button we must now refer to it with event.target. We can also test for the presence of a class on an element with a shortcut method, .hasClass(). The .is() method is more flexible, however, and can test any selector expression. Events [ 58 ] We have an unintentional side-effect from this code, however. When a button is clicked now, the switcher collapses, as it did before we added the call to .stopPropagation(). The handler for the switcher visibility toggle is now bound to the same element as the handler for the buttons, so halting the event bubbling does not stop the toggle from being triggered. To sidestep this issue, we can remove the .stopPropagation() call and instead add another .is() test: $(document).ready(function() { $('#switcher').click(function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }); }); $(document).ready(function() { $('#switcher').click(function(event) { if ($(event.target).is('.button')) { $('body').removeClass(); if (event.target.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (event.target.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(event.target).addClass('selected'); } }); }); This example is a bit overcomplicated for its size, but as the number of elements with event handlers increases, event delegation is the right technique to use. Event delegation is also useful in other situations we'll see later, such as when new elements are added by DOM manipulation methods (Chapter 5) or AJAX routines (Chapter 6). Removing an event handler There are times when we will be done with an event handler we previously registered. Perhaps the state of the page has changed such that the action no longer makes sense. It is typically possible to handle this situation with conditional statements inside our event handlers, but it may be more elegant to unbind the handler entirely. Chapter 3 [ 59 ] Suppose that we want our collapsible style switcher to remain expanded whenever the page is not using the normal style. While the Narrow Column or Large Print button is selected, clicking the background of the style switcher should do nothing. We can accomplish this by calling the .unbind() method to remove the collapsing handler when one of the non-default style switcher buttons is clicked. $(document).ready(function() { $('#switcher').click(function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click'); }); }); Now when a button such as Narrow Column is clicked, the click handler on the style switcher
    is removed, and clicking the background of the box no longer collapses it. However, the button doesn't work anymore! It is attached to the click event of the style switcher
    as well because we rewrote the button-handling code to use event delegation. This means that when we call $('#switcher'). unbind('click'), both behaviors are removed. Event namespacing We need to make our .unbind() call more specific, so that it does not remove both of the click handlers we have registered. One way of doing this is to use event namespacing. We can introduce additional information when an event is bound that allows us to identify that particular handler later. To use namespacing, we need to return to the non-shorthand method of binding event handlers, the .bind() method itself. The first parameter we pass to .bind() is the name of the JavaScript event we want to watch for. We can use a special syntax here, though, that allows us to subcategorize the event. $(document).ready(function() { $('#switcher').bind('click.collapse', function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }); Events [ 60 ] $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click.collapse'); }); }); The .collapse suffix is invisible to the event handling system; click events are handled by this function, just as if we wrote .bind('click'). However, the addition of the namespace means that we can unbind just this handler, without affecting the separate click handler we wrote for the buttons. There are other ways of making our .unbind() call more specific, as we will see in a moment. However, event namespacing is a useful tool in our arsenal. It is especially handy in the creation of plugins, as we'll see in Chapter 11. Rebinding events Now clicking the Narrow Column or Large Print button causes the style switcher collapsing functionality to be disabled. However, we want the behavior to return when the Default button is pressed. To do this, we will need to rebind the handler whenever Default is clicked. First, we should give our handler function a name so that we can use it more than once without repeating ourselves: $(document).ready(function() { var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').bind('click.collapse', toggleStyleSwitcher); }); Note that we are here using a new syntax for defining a function. Rather than defining the function by leading with the function keyword, we assign an anonymous function to a local variable. This is a stylistic choice to make our event handlers and other function definitions resemble each other more closely; the two syntaxes are functionally equivalent. Also, recall that .bind() takes a function reference as its second argument. It is important to remember, when using a named function here, to omit parentheses after the function name; parentheses would cause the function to be called, rather than referenced. Chapter 3 [ 61 ] Now that the function has a name, we can bind it again later without repeating the function definition: $(document).ready(function() { var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').bind('click.collapse', toggleStyleSwitcher); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click.collapse'); }); $('#switcher-default').click(function() { $('#switcher') .bind('click.collapse', toggleStyleSwitcher); }); }); Now the toggle behavior is bound when the document is loaded, unbound when Narrow Column or Large Print is clicked, and rebound when Normal is clicked after that. We have sidestepped a potential pitfall here. Remember that when a handler is bound to an event in jQuery, previous handlers remain in effect. This would seem to mean that if Normal was clicked multiple times in succession, many copies of the toggleStyleSwitcher handler would be bound, causing strange behavior when the
    was clicked. Indeed, if we had used anonymous functions throughout our example, this would be the case. But since we gave the function a name and used the same function throughout the code, the behavior is only bound once. The .bind() method will not attach an event handler to an element if it has already been attached. As another benefit to naming this function, we no longer need to use namespacing. The .unbind() method can take a function as a second argument; in this case, it unbinds only that specific handler. $(document).ready(function() { var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').click(toggleStyleSwitcher); Events [ 62 ] $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click', toggleStyleSwitcher); }); $('#switcher-default').click(function() { $('#switcher').click(toggleStyleSwitcher); }); }); A shortcut is also available for the situation in which we want to unbind an event handler immediately after the first time it is triggered. This shortcut, called .one(), is used like this: $(document).ready(function() { $('#switcher').one('click', toggleStyleSwitcher); }); This would cause the toggle action to occur only once. Simulating user interaction At times it is convenient to execute code that we have bound to an event, even if the normal circumstances of the event are not occurring. For example, suppose we wanted our style switcher to begin in its collapsed state. We could accomplish this by hiding buttons from within the stylesheet, or by calling the .hide() method from a $(document).ready() handler. Another way would be to simulate a click on the style switcher so that the toggling mechanism we've already established is triggered. The .trigger() method allows us to do just this: $(document).ready(function() { $('#switcher').trigger('click'); }); Now right when the page loads the switcher is collapsed, just as if it had been clicked. If we were hiding content that we wanted people without JavaScript enabled to see, this would be a reasonable way to implement graceful degradation. Chapter 3 [ 63 ] The .trigger() method provides the same set of shortcuts that .bind() does. When these shortcuts are used with no arguments, the behavior is to trigger the action rather than bind it: $(document).ready(function() { $('#switcher').click(); }); Keyboard events As another example, we can add keyboard shortcuts to our style switcher. When the user types the first letter of one of the display styles, we will have the page behave as if the corresponding button were clicked. To implement this feature, we will need to explore keyboard events, that behave a bit differently from mouse events. There are two types of keyboard events: those that react to the keyboard directly (keyup and keydown) and those that react to text input (keypress). A single character entry event could correspond to several keys, for example when the Shift key in combination with the X key creates the capital letter X. While the specifics of implementation differ from one browser to the next (unsurprisingly), a safe rule of thumb is as follows: if you want to know what key the user pushed, you should observe the keyup or keydown event; if you want to know what character ended up on the screen as a result, you should observe the keypress event. For this feature, we just want to know when the user presses the D, N, or L key, so we will use keyup. Next, we need to determine which element should watch for the event. This is a little less obvious than with mouse events, where we have an obvious mouse cursor to tell us about the event's target. Instead, the target of a keyboard event is the element that currently has the keyboard focus. The element with focus can be changed in several ways, including mouse clicks and presses of the Tab key. Not every element can get the focus, either; only items that have default keyboard-driven behaviors such as form fields, links, and elements with a .tabIndex property are candidates. In this case, we don't really care what element has the focus; we want our switcher to work whenever the user presses one of the keys. Event bubbling will once again come in handy, as we can bind our keyup event to the document element and have assurance that eventually, any key event will bubble up to us. Events [ 64 ] Finally, we will need to know which key was pressed when our keyup handler gets triggered. We can inspect the event object for this. The .keyCode property of the event contains an identifier for the key that was pressed, and for alphabetic keys, this identifier is the ASCII value of the uppercase letter. So we can switch on this value and trigger the appropriate button click: $(document).ready(function() { $(document).keyup(function(event) { switch (String.fromCharCode(event.keyCode)) { case 'D': $('#switcher-default').click(); break; case 'N': $('#switcher-narrow').click(); break; case 'L': $('#switcher-large').click(); break; } }); }); Presses of these three keys now simulate mouse clicks on the buttons—provided that the key event is not interrupted by features such as Firefox's "search for text when I start typing." As an alternative to using .trigger() to simulate this click, let's explore how to factor out code into a function so that more than one handler can call it—in this case, both click and keyup. While not necessary in this case, this technique can be useful in eliminating code redundancy. $(document).ready(function() { // Enable hover effect on the style switcher buttons. $('#switcher .button').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }); // Allow the style switcher to expand and collapse. var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').click(toggleStyleSwitcher); Chapter 3 [ 65 ] // Simulate a click so we start in a collaped state. $('#switcher').click(); // The setBodyClass() function changes the page style. // The style switcher state is also updated. var setBodyClass = function(className) { $('body').removeClass(); $('body').addClass(className); $('#switcher .button').removeClass('selected'); $('#switcher-' + className).addClass('selected'); if (className == 'default') { $('#switcher').click(toggleStyleSwitcher); } else { $('#switcher').unbind('click', toggleStyleSwitcher); $('#switcher .button').removeClass('hidden'); } }; // Invoke setBodyClass() when a button is clicked. $('#switcher').click(function(event) { if ($(event.target).is('.button')) { if (event.target.id == 'switcher-default') { setBodyClass('default'); } if (event.target.id == 'switcher-narrow') { setBodyClass('narrow'); } else if (event.target.id == 'switcher-large') { setBodyClass('large'); } } }); // Invoke setBodyClass() when a key is pressed. $(document).keyup(function(event) { switch (String.fromCharCode(event.keyCode)) { case 'D': setBodyClass('default'); break; case 'N': setBodyClass('narrow'); break; case 'L': setBodyClass('large'); break; } }); }); Events [ 66 ] Summary The abilities we've discussed in this chapter allow us to: Let multiple JavaScript libraries coexist on a single page using .noConflict(). Use mouse event handlers to react to a user's click on a page element with .bind() or .click(). Observe event context to perform different actions depending on the page element clicked, even when the handler is bound to several elements. Alternately expand and collapse a page element by using .toggle(). Highlight page elements under the mouse cursor by using .hover(). Influence event propagation to determine which elements get to respond to an event by using .stopPropagation() and .preventDefault(). Implement event delegation to reduce the number of bound event handlers necessary on a page. Call .unbind() to remove an event handler we're finished with. Segregate related event handlers with event namespacing so they can be acted on as a group. Cause bound event handlers to execute with .trigger(). Use keyboard event handlers to react to a user's key press with .keyup(). Used together, we can use these capabilities to build quite interactive pages. In the next chapter, we'll learn how to provide visual feedback to the user during these interactions. • • • • • • • • • • • Effects If actions speak louder than words, then in the JavaScript world, effects make actions speak louder still. With jQuery, we can easily add impact to our actions through a set of simple visual effects, and even craft our own, more sophisticated animations. jQuery effects certainly add flair, as is evident when we see elements gradually slide into view instead of appearing all at once. However, they can also provide important usability enhancements that help orient the user when there is some change on a page (especially common in AJAX applications). In this chapter, we will explore a number of these effects and combine them in interesting ways. Inline CSS modification Before we jump into the nifty jQuery effects, a quick look at CSS is in order. In previous chapters we have been modifying a document's appearance by defining styles for classes in a separate stylesheet and then adding or removing those classes with jQuery. Typically, this is the preferred process for injecting CSS into HTML because it respects the stylesheet's role in dealing with the presentation of a page. However, there may be times when we need to apply styles that haven't been, or can't easily be, defined in a stylesheet. Fortunately, jQuery offers the .css() method for such occasions. This method acts as both a getter and a setter. To get the value of a style property, we simply pass the name of the property as a string, like .css('backgroundColor'). Multi-word properties can be interpreted by jQuery when hyphenated, as they are in CSS notation (background-color), or camel-cased, as they are in DOM notation (backgroundColor). For setting style properties, the .css() method comes in two flavors—one that takes a single style property and its value and one that takes a map of property-value pairs: .css('property','value') .css({property1: 'value1', 'property-2': 'value2'}) Effects [ 68 ] Experienced JavaScript developers will recognize these jQuery maps as JavaScript object literals. Numeric values do not take quotation marks while string values do. However, when using the map notation, quotation marks are not required for property names if they are written in camel-cased DOM notation. We use the .css() method the same way we've been using .addClass() —by chaining it to a selector and binding it to an event. To demonstrate this, we'll return to the style switcher example of Chapter 3, but with different HTML:
    Text Size

    Fourscore and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal.

    By linking to a stylesheet with a few basic style rules, the page can initially look like the following screenshot: In this version of the style switcher, we're using
    When the DOM is ready, the second paragraph is hidden: $(document).ready(function() { $('p:eq(1)').hide(); }); And the speech looks like the following screenshot: Then, when the user clicks on read more at the end of the first paragraph, that link is hidden and the second paragraph is shown: $(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').show(); $(this).hide(); return false; }); }); Effects [ 74 ] Note the use of return false to keep the link from activating its default action. Now the speech looks like this: The .hide() and .show() methods are quick and useful, but they aren't very flashy. To add some flair, we can give them a speed. Effects and speed When we include a speed (or, more precisely, a duration) with .show() or .hide(), it becomes animated—occurring over a specified period of time. The .hide('speed') method, for example, decreases an element's height, width, and opacity simultaneously until all three reach zero, at which point the CSS rule display:none is applied. The .show('speed') method will increase the element's height from top to bottom, width from left to right, and opacity from 0 to 1 until its contents are completely visible. Speeding in With any jQuery effect, we can use one of three preset speeds: 'slow', 'normal', and 'fast'. Using .show('slow') makes the show effect complete in .6 seconds, .show('normal') in .4 seconds, and .show('fast') in .2 seconds. For even greater precision we can specify a number of milliseconds, for example .show(850). Unlike the speed names, the numbers are not wrapped in quotation marks. Let's include a speed in our example when showing the second paragraph of Lincoln's Gettysburg Address: $(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').show('slow'); $(this).hide(); return false; }); }); Chapter 4 [ 75 ] When we capture the paragraph's appearance at roughly halfway through the effect, we see something like the following: Fading in and fading out While the animated .show() and .hide() methods are certainly flashy, they may at times be too much of a good thing. Fortunately, jQuery offers a couple other pre-built animations for a more subtle effect. For example, to have the whole paragraph appear just by gradually increasing the opacity, we can use .fadeIn('slow') instead: $(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').fadeIn('slow'); $(this).hide(); return false; }); }); This time when we capture the paragraph's appearance halfway, it's seen as: The difference here is that the .fadeIn() effect starts by setting the dimensions of the paragraph so that the contents can simply fade into it. To gradually decrease the opacity we can use .fadeOut(). Effects [ 76 ] Compound effects Sometimes we have a need to toggle the visibility of elements, rather than displaying them once as we did in the previous example. Toggling can be achieved by first checking the visibility of the matched elements and then attaching the appropriate method. Using the fade effects again, we can modify the example script to look like this: $(document).ready(function() { var $firstPara = $('p:eq(1)'); $firstPara.hide(); $('a.more').click(function() { if ($firstPara.is(':hidden')) { $firstPara.fadeIn('slow'); $(this).text('read less'); } else { $firstPara.fadeOut('slow'); $(this).text('read more'); } return false; }); }); As we did earlier in the chapter, we're caching our selector here to avoid repeated DOM traversal. Notice, too, that we're no longer hiding the clicked link; instead, we're changing the its text. Using an if else statement is a perfectly reasonable way to toggle elements' visibility. But with jQuery's compound effects we can leave the conditionals out of it (although, in this example, we still need one for the link text). jQuery provides a .toggle() method, which acts like .show() and .hide(), and like them, can be used with a speed argument or without. The other compound method is .slideToggle(), which shows or hides elements by gradually increasing or decreasing their height. Here is what the script looks like when we use the .slideToggle() method: $(document).ready(function() { var $firstPara = $('p:eq(1)'); $firstPara.hide(); $('a.more').click(function() { $firstPara.slideToggle('slow'); var $link = $(this); if ( $link.text() == "read more" ) { $link.text('read less'); } else { Chapter 4 [ 77 ] $link.text('read more'); } return false; }); }); This time $(this) would have been repeated, so we're storing it in the $link variable for performance and readability. Also, the conditional statement checks for the text of the link rather than the visibility of the second paragraph, since we're only using it to change the text. Creating custom animations In addition to the pre-built effect methods, jQuery provides a powerful .animate() method that allows us to create our own custom animations with fine-grained control. The .animate() method comes in two forms. The first takes up to four arguments: 1. A map of style properties and values—similar to the .css() map discussed earlier in this chapter 2. An optional speed—which can be one of the preset strings or a number of milliseconds 3. An optional easing type—an advanced option discussed in Chapter 10 4. An optional callback function—which will be discussed later in this chapter All together, the four arguments look like this: .animate({property1: 'value1', property2: 'value2'}, speed, easing, function() { alert('The animation is finished.'); } ); The second form takes two arguments, a map of properties and a map of options. .animate({properties}, {options}) In effect, the second argument wraps up the second through fourth arguments of the first form into another map, and adds two more options to the mix. When we adjust the line breaks for readability, the second form looks like this: .animate({ property1: 'value1', property2: 'value2' Effects [ 78 ] }, { duration: 'value', easing: 'value', complete: function() { alert('The animation is finished.'); }, queue: boolean, step: callback }); For now we'll use the first form of the .animate() method, but we'll return to the second form later in the chapter when we discuss queuing effects. Toggling the fade When we discussed compound effects, did you notice that not all methods have a corresponding method for toggling? That's right: while the sliding methods include .slideToggle(), there is no corresponding .fadeToggle() to go along with .fadeIn() and .fadeOut()! The good news is that we can use the .animate() method to easily make our own toggling fade animation. Here, we'll replace the .slideToggle() line of the previous example with our custom animation: $(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').animate({opacity: 'toggle'}, 'slow'); var $link = $(this); if ( $link.text() == "read more" ) { $link.text('read less'); } else { $link.text('read more'); } return false; }); }); As the example illustrates, the .animate() method provides convenient shorthand values for CSS properties — 'show', 'hide', and 'toggle' — to ease the way when the shorthand methods aren't quite right for the particular task. Chapter 4 [ 79 ] Animating multiple properties With the .animate() method, we can modify any combination of properties simultaneously. For example, to create a simultaneous sliding and fading effect when toggling the second paragraph, we simply add the height property-value pair to .animate()'s properties map: $(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').animate({ opacity: 'toggle', height: 'toggle' }, 'slow'); var $link = $(this); if ( $link.text() == "read more" ) { $link.text('read less'); } else { $link.text('read more'); } return false; }); }); Additionally, we have not only the style properties used for the shorthand effect methods at our disposal, but also other properties such as: left, top, fontSize, margin, padding, and borderWidth. Recall the script to change the text size of the speech paragraphs. We can animate the increase or decrease in size by simply substituting the .animate() method for the .css() method: $(document).ready(function() { var $speech = $('div.speech'); var defaultSize = $speech.css('fontSize'); $('#switcher button').click(function() { var num = parseFloat( $speech.css('fontSize'), 10 ); switch (this.id) { case 'switcher-large': num *= 1.4; break; case 'switcher-small': num /= 1.4; break; default: num = parseFloat(defaultSize, 10); Effects [ 80 ] } $speech.animate({fontSize: num + 'px'}, 'slow'); }); }); The extra properties allow us to create much more complex effects, too. We can, for example, move an item from the left side of the page to the right while increasing its height by 20 pixels and changing its border width to 5 pixels. So, let's do that with the
    box. Here is what it looks like before we animate it: With a flexible-width layout, we need to compute the distance that the box needs to travel before it lines up at the right side of the page. Assuming that the paragraph's width is 100%, we can subtract the Text Size box's width from the paragraph's width. While jQuery's .width() method would usually come in handy for such calculations, it doesn't factor in the width of the right and left padding or the right and left border. As of jQuery version 1.2.6, though we also have the .outerWidth() method at our disposal. This is what we'll use here, to avoid having to add padding and border widths as well. For the sake of this example, we'll trigger the animation by clicking the Text Size label, just above the buttons. Here is what the code should look like: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher.animate({left: paraWidth - switcherWidth, height: '+=20px', borderWidth: '5px'}, 'slow'); }); }); Chapter 4 [ 81 ] Note that the height property has += before the pixel value. This expression, introduced in jQuery 1.2, indicates a relative value. So, instead of animating the height to 20 pixels, the height is animated to 20 pixels greater than the current height. Although this code successfully increases the height of the
    and widens its border, at the moment the left position cannot be changed. We still need to enable changing its position in the CSS. Positioning with CSS When working with .animate(), it's important to keep in mind the limitations that CSS imposes on the elements that we wish to change. For example, adjusting the left property will have no effect on the matching elements unless those elements have their CSS position set to relative or absolute. The default CSS position for all block-level elements is static, which accurately describes how those elements will remain if we try to move them without first changing their position value. For more information on absolute and relative positioning, see Joe Gillespie's article, Absolutely Relative at: http://www.wpdfd.com/ issues/78/absolutely_relative/ A peek at our stylesheet shows that we have now set
    to be relatively positioned: #switcher { position: relative; } With the CSS taken into account, the result of clicking on Text Size, when the animation has completed, will look like this: Effects [ 82 ] Simultaneous versus queued effects The .animate() method, as we've just discovered, is very useful for creating simultaneous effects in a particular set of elements. There may be times, however, when we want to queue our effects, having them occur one after the other. Working with a single set of elements When applying multiple effects to the same set of elements, queuing is easily achieved by chaining those effects. To demonstrate this queuing, we'll again move the Text Size box to the right, increase its height and increase its border width. This time, however, we perform the three effects sequentially, simply by placing each in its own .animate() method and chaining the three together: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .animate({left: paraWidth - switcherWidth}, 'slow') .animate({height: '+=20px'}, 'slow') .animate({borderWidth: '5px'}, 'slow'); }); }); Recall that chaining permits us to keep all three .animate() methods on the same line, but here we have indented them and put each on its own line for greater readability. We can queue any of the jQuery effect methods, not just .animate(), by chaining them. We can, for example, queue effects on
    in the following order: 1. Fade its opacity to .5 with .fadeTo(). 2. Move it to the right with .animate(). 3. Fade it back in to full opacity with .fadeTo(). 4. Hide it with .slideUp(). 5. Show it once more with .slideDown(). Chapter 4 [ 83 ] All we need to do is chain the effects in the same order in our code: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({ 'left': paraWidth - switcherWidth }, 'slow') .fadeTo('slow',1.0) .slideUp('slow') .slideDown('slow'); }); }); But what if we want to move the
    to the right at the same time as it fades to half opacity? If the two animations were occurring at the same speed, we could simply combine them into a single .animate() method. But in this example, the fade is using the 'fast' speed while the move to the right is using the 'slow' speed. Here is where the second form of the .animate() method comes in handy: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({ 'left': paraWidth - switcherWidth }, {duration: 'slow', queue: false}) .fadeTo('slow',1.0) .slideUp('slow') .slideDown('slow'); }); }); The second argument, an options map, provides the queue option, which when set to false makes the animation start simultaneously with the previous one. Effects [ 84 ] One final observation about queuing effects on a single set of elements is that queuing does not automatically apply to other, non-effect methods such as .css(). So let's suppose we wanted to change the background color of
    to red after the .slideUp() but before the slideDown(). We could try doing it like this: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({ 'left': paraWidth - switcherWidth }, 'slow') .fadeTo('slow',1.0) .slideUp('slow') .css('backgroundColor','#f00') .slideDown('slow'); }); }); However, even though the background-changing code is placed at the correct position in the chain, it occurs immediately upon the click. One way we can add non-effect methods to the queue is to use the appropriately named .queue() method. Here is what it would look like in our example: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({ 'left': paraWidth - switcherWidth }, 'slow') .fadeTo('slow',1.0) .slideUp('slow') .queue(function() { $switcher .css('backgroundColor', '#f00') .dequeue(); Chapter 4 [ 85 ] }) .slideDown('slow'); }); }); When given a callback function, as it is here, the .queue() method adds the function to the queue of effects for the matched elements. Within the function, we set the background color to red and then add the corollary .dequeue() method. Including this .dequeue() method allows the animation queue to pick up where it left off and complete the chain with the following .slideDown('slow') line. If we hadn't used .dequeue(), the animation would have stopped. More information and examples for .queue() and .dequeue() are available at http://docs.jquery.com/Effects. We'll discover another way to queue non-effect methods as we examine effects with multiple sets of elements. Working with multiple sets of elements Unlike with a single set of elements, when we apply effects to different sets, they occur at virtually the same time. To see these simultaneous effects in action, we'll slide one paragraph down while sliding another paragraph up. First, we'll add the remaining portion of the Gettysburg Address to the HTML, dividing it into two separate paragraphs:
    Text Size

    Fourscore and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal.

    Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battlefield of that war. We have come to dedicate a portion of that field as a final resting-place for those who here gave Effects [ 86 ] their lives that the nation might live. It is altogether fitting and proper that we should do this. But, in a larger sense, we cannot dedicate, we cannot consecrate, we cannot hallow, this ground.

    read more

    The brave men, living and dead, who struggled here have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember, what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced.

    It is rather for us to be here dedicated to the great task remaining before us—that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion—that we here highly resolve that these dead shall not have died in vain—that this nation, under God, shall have a new birth of freedom and that government of the people, by the people, for the people, shall not perish from the earth.

    Next, to help us see what's happening during the effect, we'll give the third paragraph a 1-pixel border and the fourth paragraph a gray background. We'll also hide the fourth paragraph when the DOM is ready: $(document).ready(function() { $('p:eq(2)').css('border', '1px solid #333'); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); }); Finally, we'll add the .click() method to the third paragraph so that when it is clicked, the third paragraph will slide up (and out of view), while the fourth paragraph slides down (and into view): $(document).ready(function() { $('p:eq(2)') .css('border', '1px solid #333') .click(function() { Chapter 4 [ 87 ] $(this).slideUp('slow') .next().slideDown('slow'); }); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); }); A screenshot of these two effects in mid-slide confirms that they do, indeed, occur virtually simultaneously: The third paragraph, which started visible, is halfway through sliding up at the same time as the fourth paragraph, which started hidden, is halfway through sliding down. Callbacks In order to allow queuing effects on different elements, jQuery provides a callback function for each effect method. As we have seen with event handlers and with the .queue() method, callbacks are simply functions passed as method arguments. In the case of effects, they appear as the last argument of the method. If we use a callback to queue the two slide effects, we can have the fourth paragraph slide down before the third paragraph slides up. Let's first look at how to set up the .slideDown() method with the callback: $(document).ready(function() { $('p:eq(2)') .css('border', '1px solid #333') .click(function() { $(this).next().slideDown('slow',function() { // code here executes after 3rd paragraph's // slide down has ended }); }); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); }); Effects [ 88 ] We do need to be careful here, however, about what is actually going to slide up. The context has changed for $(this) because the callback is inside the .slideDown() method. Here, $(this) is no longer the third paragraph, as it was at the point of the .click() method; rather, since the .slideDown() method is attached to $(this). next(), everything within that method now sees $(this) as the next sibling, or the fourth paragraph. Therefore, if we put $(this).slideUp('slow') inside the callback, we would end up hiding the same paragraph that we had just made visible. A simple way to keep the reference of $(this) stable is to store it in a variable right away within the .click() method, like var $thirdPara = $(this). Now $thirdPara will refer to the third paragraph, both outside and inside the callback. Here is what the code looks like using our new variable: $(document).ready(function() { var $thirdPara = $('p:eq(2)'); $thirdPara .css('border', '1px solid #333') .click(function() { $(this).next().slideDown('slow',function() { $thirdPara.slideUp('slow'); }); }); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); }); Using $thirdPara inside the .slideDown() callback relies on the properties of closures. We'll be discussing this important, yet difficult-to-master, topic in Appendix C. This time, a snapshot halfway through the effects will reveal that both the third and the fourth paragraphs are visible; the fourth has finished sliding down and the third is about to begin sliding up: Chapter 4 [ 89 ] Now that we've discussed callbacks, we can return to the code from earlier in this chapter in which we queued a background-color change near the end of a series of effects. Instead of using the .queue() method, as we did earlier, we can simply use a callback function: $(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('slow',0.5) .animate({ 'left': paraWidth - switcherWidth }, 'slow') .fadeTo('slow',1.0) .slideUp('slow', function() { $switcher .css('backgroundColor', '#f00'); }) .slideDown('slow'); }); }); Here again, the background color of
    changes to red after it slides up, and before it slides back down. In a nutshell With all the variations to consider when applying effects, it can become difficult to remember whether the effects will occur simultaneously or sequentially. A brief outline might help: 1. Effects on a single set of elements are: simultaneous when applied as multiple properties in a single .animate() method queued when applied in a chain of methods, unless the queue option is set to false 2. Effects on multiple sets of elements are: simultaneous by default queued when applied within the callback of another effect or within the callback of the .queue() method ° ° ° ° Effects [ 90 ] Summary By using effect methods that we have explored in this chapter, we should now be able to incrementally increase and decrease text size by using either the .css() or the .animate() method. We should also be able to apply various effects to gradually hide and show page elements in different ways and also to animate elements, simultaneously or sequentially, in a number of ways. In the first four chapters of the book, all of our examples have involved manipulating elements that have been hard-coded into the page's HTML. In Chapter 5 we will explore ways in which we can use jQuery to create new elements and insert them into the DOM wherever we choose. DOM Manipulation Like a magician who appears to produce a bouquet of flowers out of thin air, jQuery can create elements, attributes, and text in a web page—as if by magic. But wait, there's more! With jQuery, we can also make any of these things vanish. And, we can take that bouquet of flowers and transform it into a
    dove
    . Manipulating attributes Throughout the first four chapters of this book, we have been using the .addClass() and .removeClass() methods to demonstrate how we can change the appearance of elements on a page. Effectively, what these two methods are doing is manipulating the class attribute (or, in DOM scripting parlance, the className property). The .addClass() method creates or adds to the attribute, while .removeClass() deletes or shortens it. Add to these the .toggleClass() method, which alternates between adding and removing a class, and we have an efficient and robust way of handling classes. Nevertheless, the class attribute is only one of several attributes that we may need to access or change: for example, id and rel and href. For manipulating these attributes, jQuery provides the .attr() and .removeAttr() methods. We could even use .attr() and .removeAttr() to modify the class attribute, but the specialized .addClass() and .removeClass() methods are better in this case because they correctly handle cases where multiple classes are applied to a single element, such as
    . Non-class attributes Some attributes are not so easily manipulated without the help of jQuery. In addition, jQuery lets us modify more than one attribute at a time, similar to the way we worked with multiple CSS properties using the .css() method in Chapter 4. DOM Manipulation [ 92 ] For example, we can easily set the id, rel, and title attributes for links, all at once. Let's start with some sample HTML:

    Flatland: A Romance of Many Dimensions

    by Edwin A. Abbott

    Part 1, Section 3

    Concerning the Inhabitants of Flatland

    an excerpt

    Our Professional Men and Gentlemen are Squares (to which class I myself belong) and Five-Sided Figures or Pentagons .

    Next above these come the Nobility, of whom there are several degrees, beginning at Six-Sided Figures, or Hexagons, and from thence rising in the number of their sides till they receive the honourable title of Polygonal, or many-Sided. Finally when the number of the sides becomes so numerous, and the sides themselves so small, that the figure cannot be distinguished from a circle, he is included in the Circular or Priestly order; and this is the highest class of all.

    It is a Law of Nature with us that a male child shall have one more side than his father, so that each generation shall rise (as a rule) one step in the scale of development and nobility. Thus the son of a Square is a Pentagon; the son of a Pentagon, a Hexagon; and so on.

    Chapter 5 [ 93 ] Now we can iterate through each of the links inside
    and apply attributes to them one by one. If we only needed to set a common attribute value for all of the links, we could do so with a single line of code within our $(document).ready() handler: $(document).ready(function() { $('div.chapter a').attr({'rel': 'external'}); }); This technique works because we want the new rel attribute to have the same value for each link. Often, though, the attributes we add or change must have different values for each element. One example of this is that for any given document, each id must be unique if we want our JavaScript code to behave predictably. To set a unique id for each link, we abandon the single-line solution in favor of jQuery's .each() method. $(document).ready(function() { $('div.chapter a').each(function(index) { $(this).attr({ 'rel': 'external', 'id': 'wikilink-' + index }); }); }); The .each() method, which acts as an explicit iterator, is actually a more convenient form of the for loop. It can be employed when the code we want to use on each item in the selector's set of matched elements is too complex for the implicit iteration syntax. In our situation, the .each() method's anonymous function is passed an index that we can append to each id. This index argument acts as a counter, starting at 0 for the first link and incrementing by 1 with each successive link. Thus, setting the id to 'wikilink-' + index gives the first link an id of wikilink-0, the second an id of wikilink-1, and so on. In fact, we could have stuck with implicit iteration here, because the .attr() method can take a function as its second argument, similar to the way the .filter() method can do so with its single argument as we saw in Chapter 2 (see http://docs.jquery.com/Attributes/ attr#keyfn for details). However, using .each() seems more convenient for our needs. DOM Manipulation [ 94 ] We'll use the title attribute to invite people to learn more about the linked term at Wikipedia. In the HTML example, all of the links point to Wikipedia. However, it's probably a good idea to make the selector expression a little more specific, selecting only links that contain wikipedia in the href, just in case we decide to add a non-Wikipedia link to the HTML at a later time: $(document).ready(function() { $('div.chapter a[href*=wikipedia]').each(function(index) { var $thisLink = $(this); $thisLink.attr({ 'rel': 'external', 'id': 'wikilink-' + index, 'title': 'learn more about ' + $thisLink.text() + ' at Wikipedia' }); }); }); One thing worth noting here is that we're now storing $(this) in a variable called $thisLink, simply because we end up using it more than once. With all three attributes set, the HTML of the first link, for example, now looks like this: Pentagons The $() factory function revisited From the start of this book, we've been using the $() function to access elements in a document. In a sense, this function lies at the very heart of the jQuery library, as it is used every time we attach an effect, event, or property to a matched set of elements. What's more, the $() function has yet another trick within its parentheses—a feature so powerful that it can change not only the visual appearance but also the actual contents of a page. Simply by inserting a snippet of HTML code inside the parentheses, we can create an entirely new DOM structure from thin air. Accessibility reminder We should keep in mind, once again, the inherent danger in making certain functionality, visual appeal, or textual information available only to those with web browsers capable of (and enabled for) using JavaScript. Important information should be accessible to all, not just people who happen to be using the right software. Chapter 5 [ 95 ] A feature commonly seen on FAQ pages is the back to top link that appears after each question-and-answer pair. It could be argued that these links serve no semantic purpose and therefore can be included via JavaScript legitimately as an enhancement for a subset of the visitors to a page. For our example, we'll add a back to top link after each paragraph, as well as the anchor to which the back to top links will take us. To begin, we simply create the new elements: $(document).ready(function() { $('back to top'); $(''); }); Here is what the page looks like at this point: But where are the back to top links and the anchor? Shouldn't they appear on the page? The answer is no. While the two lines do create the elements, they don't yet add the elements to the page. To do that, we can use one of the many jQuery insertion methods. DOM Manipulation [ 96 ] Inserting new elements jQuery has two methods for inserting elements before other elements: .insertBefore() and .before(). These two methods have the same function; their difference lies only in how they are chained to other methods. Another two methods, .insertAfter() and .after(), bear the same relationship with each other, but as their names suggest, they insert elements after other elements. For the back to top links we'll use the .insertAfter() method: $(document).ready(function() { $('back to top') .insertAfter('div.chapter p'); $(''); }); The .after() method would accomplish the same thing as .insertAfter(), but with the selector expression preceding the method rather than following it. Using .after(), the first line inside $(document).ready() would look like this: $('div.chapter p').after('back to top'); With .insertAfter(), we can continue acting on the created element by chaining additional methods. With .after(), additional methods would act on the elements matched by the $('div.chapter p') selector instead. So, now that we've actually inserted the links into the page (and into the DOM) after each paragraph that appears within
    , the back to top links will appear: Chapter 5 [ 97 ] Unfortunately, the links won't work yet. We still need to insert the anchor with id="top". For this, we can use one of the methods that insert elements inside of other elements. $(document).ready(function() { $('back to top') .insertAfter('div.chapter p'); $('') .prependTo('body'); }); This additional code inserts the anchor right at the beginning of the ; in other words, at the top of the page. Now, with the .insertAfter() method for the links and the .prependTo() method for the anchor, we have a fully functioning set of back to top links for the page. With back to top links, it doesn't make much sense to have them appear when the top of the page is still visible. A quick improvement to the script would start the links only after, say, the fourth paragraph. This is easy to accomplish with a little change to the selector expression: .insertAfter('div.chapter p:gt(2)'). Why the 2 here? Remember that JavaScript indexing starts at 0; therefore, the first paragraph is indexed as 0, the second is 1, the third is 2, and the fourth paragraph is 3. Our selector expression begins inserting the links after each paragraph when the index reaches 3, because that is the first one greater than 2. The effect of this selector-expression change is now evident: DOM Manipulation [ 98 ] Moving elements With the back to top links, we created new elements and inserted them on the page. It's also possible to take elements from one place on the page and insert them into another place. A practical application of this type of insertion is the dynamic placement and formatting of footnotes. One footnote already appears in the original Flatland text that we are using for this example, but we'll also designate a couple of other portions of the text as footnotes for the purpose of this demonstration:

    Rarely—in proportion to the vast numbers of Isosceles births—is a genuine and certifiable Equal-Sided Triangle produced from Isosceles parents. "What need of a certificate?" a Spaceland Chapter 5 [ 99 ] class="footnote">And how perfect a proof of the natural fitness and, I may almost say, the divine origin of the aristocratic constitution of the States of Flatland! By a judicious use of this Law of Nature, the Polygons and Circles are almost always able to stifle sedition in its very cradle, taking advantage of the irrepressible and boundless hopefulness of the human mind.…

    Each of these three paragraphs has a single footnote wrapped inside . By marking up the HTML in this way, we can preserve the context of the footnote. With a CSS rule applied in the stylesheet to italicize the footnotes, the three paragraphs look like this: Now we can grab the footnotes and insert them in between
    and