Ajax The Definitive Guide

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 982

DownloadAjax The Definitive Guide
Open PDF In BrowserView PDF
www.it-ebooks.info

www.it-ebooks.info

Ajax
The Definitive Guide

www.it-ebooks.info

Other resources from O’Reilly
Related titles

oreilly.com

Ajax Design Patterns
Ajax Hacks
Ajax on Java
Ajax on Rails

Head Rush Ajax
Learning ASP.NET 2.0 with
AJAX
Programming ASP.NET AJAX

oreilly.com is more than a complete catalog of O’Reilly books.
You’ll also find links to news, events, articles, weblogs, sample
chapters, and code examples.
oreillynet.com is the essential portal for developers interested in
open and emerging technologies, including new platforms, programming languages, and operating systems.

Conferences

O’Reilly brings diverse innovators together to nurture the ideas
that spark revolutionary industries. We specialize in documenting the latest tools and systems, translating the innovator’s
knowledge into useful skills for those in the trenches. Visit
conferences.oreilly.com for our upcoming events.
Safari Bookshelf (safari.oreilly.com) is the premier online reference library for programmers and IT professionals. Conduct
searches across more than 1,000 books. Subscribers can zero in
on answers to time-critical questions in a matter of seconds.
Read the books on your Bookshelf from cover to cover or simply flip to the page you need. Try it today for free.

www.it-ebooks.info

Ajax
The Definitive Guide

Anthony T. Holdener III

Beijing • Cambridge • Farnham • Köln • Sebastopol • Taipei • Tokyo

www.it-ebooks.info

Ajax: The Definitive Guide
by Anthony T. Holdener III
Copyright © 2008 Anthony T. Holdener III. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions
are also available for most titles (safari.oreilly.com). For more information, contact our
corporate/institutional sales department: (800) 998-9938 or corporate@oreilly.com.

Editor: Simon St.Laurent
Production Editor: Rachel Monaghan
Copyeditor: Audrey Doyle
Proofreader: Rachel Monaghan

Indexer: Ellen Troutman Zaig
Cover Designer: Karen Montgomery
Interior Designer: David Futato
Illustrator: Jessamyn Read

Printing History:
January 2008:

First Edition.

Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of
O’Reilly Media, Inc. Ajax: The Definitive Guide, the image of a woolly monkey, and related trade dress
are trademarks of O’Reilly Media, Inc.
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as
trademarks. Where those designations appear in this book, and O’Reilly Media, Inc. was aware of a
trademark claim, the designations have been printed in caps or initial caps.
While every precaution has been taken in the preparation of this book, the publisher and author assume
no responsibility for errors or omissions, or for damages resulting from the use of the information
contained herein.

ISBN: 978-0-596-52838-6
[M]

[9/09]

www.it-ebooks.info

To Sarah, the love of my life and my unending
inspiration.
And to Kate and Tony, whom I hope to always
inspire.

www.it-ebooks.info

www.it-ebooks.info

Table of Contents

Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii

Part I.

Ajax Fundamentals

1. Reinventing the Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Web Page Components
Modern Web Standards
Browsers
Standards Compliance
Welcome to Web 2.0

3
9
17
19
20

2. From Web Sites to Web Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
The Transition
Basic Web and Ajax Design Patterns
Application Environments
The Developer
What Ajax Is Not

22
28
31
33
34

3. Servers, Databases, and the Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
The Web Server
Server-Side Scripting
Databases
Getting Data Into and Out of Relational Databases
Interfacing the Interface
Frameworks and Languages
What Good Are Frameworks?

36
39
44
48
54
57
63

vii

www.it-ebooks.info

4. Foundations: Scripting XML and JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
XML
JSON
Choosing a Data Exchange Format
A Quick Introduction to Client Frameworks
Simplifying Development

68
86
92
94
97

5. Manipulating the DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Understanding the DOM
We’ve Already Met
Manipulating DOM Elements, Attributes, and Objects
Change That Style
Events in the DOM
DOM Stuff for Tables
Is innerHTML Evil?

103
105
106
117
129
135
138

6. Designing Ajax Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Usability
Functionality
Visualization
Accessibility
The Ajax Interface

141
153
158
167
171

Part II. Ajax Foundations
7. Laying Out Site Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Menus
Tabs
Navigation Aids
Problems with Ajax Navigation
General Layout

175
212
221
243
246

8. Fun with Tables and Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Layout Without Tables
Accessible Tables
Sorting Tables
Tables with Style
Table Pagination

viii

|

247
252
264
280
283

Table of Contents

www.it-ebooks.info

Lists 2.0
Lists for All Seasons

291
292

9. Page Layout with Frames That Aren’t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Using Frames
XHTML and Frames
The Magic of Ajax and a DIV
Page Layout

316
321
323
329

10. Navigation Boxes and Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
The Alert Box
Integrating the Window
Navigation Windows
Tool Tips
The Necessary Pop Up

335
335
347
355
360

11. Customizing the Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Browser Customizations
Stylesheet Switching
Switching Different Customizations
Easy Font-Size Switching
Creating Color Themes
Throwing Ajax into the Mix
Changing Site Language with Ajax
Repositioning Objects and Keeping Those Positions
Storing It All in the Database

363
368
381
386
392
397
400
403
407

12. Errors: To Be (in Style) or Not to Be . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
Error Handling on the Web
Should I React to That Error?
Handling an Error with Care
Integrating the User Error

408
413
417
420

13. This Ain’t Your Father’s Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
Animation on the Web
What Is Wrong with GIF?
Building Animation with the PNG Format
Ajax Animations

434
437
439
453

Table of Contents

www.it-ebooks.info

|

ix

14. A Funny Thing Happened on the Way to the Form . . . . . . . . . . . . . . . . . . . . . 482
XHTML Forms
Using JavaScript
Fancier Forms
The Basics of Ajax and Forms
Accepting Ajax-Delivered Data
Server Responses

482
490
498
519
524
531

15. Data Validation: Client, Server, or Both . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
Data Validation Is Important
Validation with JavaScript
CSS Notification of Errors
Validation on the Server
Ajax Client/Server Validation

534
536
552
555
558

Part III. Ajax in Applications
16. Search: The New Frontier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
Types of Site Searches
Dynamic Searching with Ajax
Googling a Site

565
577
581

17. Introducing Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
What Is a Web Service?
Web Service Architectures
Ajax and Web Services
Web Feeds
Web Service APIs

594
594
606
613
618

18. Web Services: The APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
Publicly Available Web Services
Ajax and the API
The Next Step with Services

619
657
658

19. Mashups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
Mashups in Web 2.0 Applications
What Are Mashups?

x

|

Table of Contents

www.it-ebooks.info

659
659

Mashups As Applications
Data Sources
Application Portlets
Building a Mashup
Mashups and Business

661
665
668
668
671

20. For Your Business Communication Needs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Businesses and Ajax
Real-Time Communication
File Sharing
Whiteboards
Combining Applications

672
674
691
703
720

21. Internet Games Without Plug-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
Gaming on the Web
Internet Requirements
Animating a Character
Basic Collisions
User Input
The Basics of Event Handling
Putting It All Together

721
732
735
753
764
767
776

Part IV. Wrapping Up
22. Modular Coding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
What Is Modular Coding?
The Client Side
The Server Side

789
791
804

23. Optimizing Ajax Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
Site Optimization Factors
HTTP
Packets
Client-Side Optimizations
Server-Side Optimizations
Ajax Optimization

807
809
815
818
830
838

Table of Contents

www.it-ebooks.info

|

xi

Part V.

References

A. The XML and XSLT You Need to Know . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
B. JavaScript Framework, Toolkit, and Library References . . . . . . . . . . . . . . . . 863
C. Web Service API Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892
D. Ajax Risk References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925

xii

|

Table of Contents

www.it-ebooks.info

Preface

1

Ajax melds together existing technologies to help developers give web users a more
advanced browsing experience. By utilizing XHTML, CSS, JavaScript, and XML, all
tried-and-true technologies, along with the XMLHttpRequest object, you can turn
browsers into application platforms that closely mirror desktop applications. This
capability is allowing existing web sites to convert to Web 2.0 sites, while increasing
the number of new web applications that can be found on the Internet today.
Not that long ago, some web technologies, especially JavaScript, were losing their
user base as developers turned their attention to other technologies, such as Flash,
that could provide more of the functionality that was needed. The coining of Ajax in
2005 gave JavaScript the shot in the arm that some developers felt was sorely needed,
and since then, some truly wonderful things have been done with JavaScript that
were never thought possible before.
New innovations, together with the functionality of Ajax, have given the Web a new
look and appeal. Ajax: The Definitive Guide explores what you can do with Ajax to
enhance web sites and give them a Web 2.0 feel, and how additional JavaScript
enhancements can turn a web browser and web site into a true application. Even
before that, you will get a background on what goes into today’s web sites and applications. Knowing what comprises Ajax and how to use it helps you apply it more
effectively and integrate it with the latest web technologies (advanced browser searching, web services, mashups, etc.). This book also demonstrates how you can build
applications in the browser, as an alternative to the traditional desktop application.
Ajax is giving developers a new way to create content on the Web while throwing off
the constraints of the past. Web 2.0 technologies are being integrated with Ajax to
give the Web a new punch that could only be achieved before with browser plug-ins.
Ajax is helping to redefine how we all should look at the Web, and I hope this book
puts you on the path to defining your own Web 2.0 applications.

xiii

www.it-ebooks.info

Who Should Read This Book
This book is intended for two very different types of people: web developers, and
project managers or other higher-level people who do not necessarily need to know
the nitty-gritty details but would benefit from a general overview of how this Ajax
stuff works. The different parts of the book will reflect these different audiences.

Web Developers
For web developers, this book assumes the following:
• You have had some experience with HTML/XHTML.
• You have experience using CSS, and you understand the principles behind separating presentation from content.
• You understand JavaScript syntax and have written scripts with it.
• You are comfortable with server-side scripting in at least one language, whether
it be ASP.NET, PHP, Python, or something similar.
• You have some experience with relational databases and how to retrieve data
from them.
This book does not expect you to be an expert in all of these skills, but it does expect
that you can figure things out on your own or that you can get help from another
resource (another book on the technology, perhaps) so that you can follow along
with the examples presented.
Server-side code examples throughout the book will use PHP, as it seems to be the
most readily understandable to the widest range of developers.

Managers
Project managers reading this book may not need such a rigid set of prerequisites.
This book expects that you have seen web technology before and that you understand the concept of client-side and server-side development. It also expects that you
can recognize HTML, CSS, and JavaScript, though there is no need to have ever done
anything with them. Finally, this book expects that Internet terms and phrases are
not foreign to you so that you can follow along with the examples. Managers will
probably want to spend more time on the first three chapters to get a broad idea of
how Ajax fits into the Web and into application development.

How This Book Is Organized
This book consists of five parts, each focusing on a different aspect of Ajax. It is certainly not necessary to read it from beginning to end, though later parts of the book
do build on ideas from previous parts. The five parts of the book comprise 23 chapters

xiv |

Preface

www.it-ebooks.info

and four appendixes. Part I is intended for project managers looking to get a leg up
on Ajax, or for anyone who is looking for its fundamentals. The rest of the book
focuses on using Ajax from a programming point of view.
Part I, Ajax Fundamentals, explains the basic technologies that form the core of Ajax
and building Ajax applications:
Chapter 1, Reinventing the Web
Demonstrates how the first web sites were completely data-driven sites without
the benefit of tools to improve page presentation, whereas today’s Web is completely different. From the tools that are used to develop sites to the fact that the
Web is now very much driven by a combination of media and data, today is
nothing like yesterday.
Chapter 2, From Web Sites to Web Applications
Explains the nature of web site construction in the past versus the applications
they have become, and the fact that they require the same process and design
approach utilized by developers for regular desktop applications.
Chapter 3, Servers, Databases, and the Web
Shows the technologies available on the server side of web applications, briefly
discussing each and how you can use them as a backend to an Ajax application.
An introduction to databases rounds out the topic.
Chapter 4, Foundations: Scripting XML and JSON
Gives the foundation for all Ajax requests using the XMLHttpRequest object, and
explores XML and JSON responses and their advantages and disadvantages.
Frameworks that make Ajax simpler are also addressed.
Chapter 5, Manipulating the DOM
Explores manipulation and utilization of the DOM for JavaScript, examining differences between Internet Explorer’s handling of the DOM versus that of other
browsers. This chapter also gives an overview of everything necessary for a developer to work with the DOM.
Chapter 6, Designing Ajax Interfaces
Examines the different parts of a web interface and how to lay out an Ajax application so that it is usable, functional, visually pleasing, and accessible.
Part II, Ajax Foundations, describes how these technologies are applied in an Ajax
web application:
Chapter 7, Laying Out Site Navigation
Shows the different components that make up a web application and how you
can enhance them using Ajax. This chapter also explores how some Ajax techniques can break browser functionality.
Chapter 8, Fun with Tables and Lists
Examines how to properly create a table, enhance it, and add functionality with
Ajax. It also discusses the different uses for Ajax-enhanced lists.

Preface |

www.it-ebooks.info

xv

Chapter 9, Page Layout with Frames That Aren’t
Explores frames and iframes and their use before XHTML was introduced, and
explains how to emulate their behavior using XML with Ajax, JavaScript, and CSS.
Chapter 10, Navigation Boxes and Windows
Examines how to create navigation controls that do not rely on the default
browser’s window to display messages to the user, by using Ajax to transport
information back and forth between client and server.
Chapter 11, Customizing the Client
Shows how to customize the user’s experience with an application that uses Ajax
to send new data to the client when the user requests it, giving the application a
Web 2.0 feel.
Chapter 12, Errors: To Be (in Style) or Not to Be
Shows how to handle errors thrown by the application, how to use Ajax to send
messages back to the server when it is called for, and how to determine when to
display errors to the user.
Chapter 13, This Ain’t Your Father’s Animation
Examines the traditional method for animating images on the Internet, the disadvantages of using the GIF format, and the advantages of the PNG format.
Then this chapter shows how you can use PNGs for animation on the Web and
how to use Ajax to asynchronously download images in the background.
Chapter 14, A Funny Thing Happened on the Way to the Form
Explains the significance of forms on the Web, regardless of the backend
markup used, and shows the additions for making forms accessible. Then this
chapter examines how you can build custom form types to follow the style of the
overall page, and how Ajax is used in Web 2.0 forms.
Chapter 15, Data Validation: Client, Server, or Both
Shows how Ajax can aid in the validation of data in an XHTML form without
requiring a lot of extra time on behalf of the client, and where validation should
take place in a web application.
Part III, Ajax in Applications, shows you how to integrate Ajax into applications to
create faster and more responsive web components:
Chapter 16, Search: The New Frontier
Explores available methods for searching pages on a site, their advantages and
disadvantages, and how you can leverage Ajax to bring more intelligent and
helpful functionality to searching.
Chapter 17, Introducing Web Services
Examines web services and their role on the Internet, exploring the different protocols that are used—from SOAP to REST and everything in between—and shows
how you can take advantage of these services with Ajax behind the scenes.

xvi |

Preface

www.it-ebooks.info

Chapter 18, Web Services: The APIs
Gives a brief introduction to some of the web services that are available on the
Internet, and how to use the APIs that make up the frontend to these services.
This chapter also shows how JavaScript and Ajax can take advantage of these
services in creating dynamic content.
Chapter 19, Mashups
Explains how mashups are created from different web services and how Ajax can
bring together services in a way that makes them even more seamless than the
original mashups.
Chapter 20, For Your Business Communication Needs
Shows how you can use the different techniques you learned in the first parts of
this book to develop components for business applications, and how you can
use these components to build a business mashup that has desktop application
functionality.
Chapter 21, Internet Games Without Plug-ins
Shows how to build on the techniques you learned earlier in this book to
develop an Internet game that relies on JavaScript and Ajax without the need for
browser plug-ins. This chapter also examines the different gaming genres and
explains which ones make the best Internet games for Ajax.
Part IV, Wrapping Up, summarizes how to best structure Ajax applications, and how
to write them with optimization in mind:
Chapter 22, Modular Coding
Explains modular coding through all aspects of the application, from the
XHTML markup, CSS styling, and JavaScript functionality on the client side, to
server modules and SQL stored procedures on the server side, and what this programming technique brings to an application.
Chapter 23, Optimizing Ajax Applications
Explores techniques that you can use on both the client side and the server side
of an Ajax application to make it run as quickly and efficiently as possible in
light of the web technologies used.
Part V, References, contains the appendixes that refer you to important parts of Ajax
development:
Appendix A, The XML and XSLT You Need to Know
Discusses XML and XSLT, how to use them, and how to leverage them within a
web framework.
Appendix B, JavaScript Framework, Toolkit, and Library References
Discusses the major JavaScript frameworks, libraries, and toolkits—including
Prototype, script.aculo.us, Dojo, Ajax.NET, the Yahoo! User Interface, and
others—showing how each implements an Ajax wrapper or manipulates XML.

Preface |

www.it-ebooks.info

xvii

Appendix C, Web Service API Catalog
Discusses some of the major web services currently available on the Internet, along
with the protocol(s) used to implement the APIs, and whether they are free.
Appendix D, Ajax Risk References
Discusses the major risks associated with implementing Ajax, such as security,
default browser functionality, and accessibility, so that developers know what to
expect regarding the Ajax and Web 2.0 technologies.

Conventions Used in This Book
The following typographical conventions are used in this book:
Italic
Indicates new terms, URLs, filenames, and file extensions.
Constant width

Indicates computer coding in a broad sense. This includes commands, options,
variables, attributes, keys, requests, functions, methods, types, classes, modules,
properties, parameters, values, objects, events, event handlers, XML and
XHTML tags, macros, and keywords.
Constant width bold

Indicates commands or other text that the user should type literally.
Constant width italic

Indicates text that should be replaced with user-supplied values or values determined by context.
This icon signifies a tip, suggestion, or general note. You’ll also see
notes regarding the WCAG guidelines. Even if you aren’t interested in
accessibility specifically, these are useful best practices.
This icon indicates a warning or caution.

Using Code Examples
This book is here to help you get your job done. In general, you may use the code in
this book in your programs and documentation. You do not need to contact us for
permission unless you’re reproducing a significant portion of the code. For example,
writing a program that uses several chunks of code from this book does not require
permission. Selling or distributing a CD-ROM of examples from O’Reilly books does
require permission. Answering a question by citing this book and quoting example

xviii |

Preface

www.it-ebooks.info

code does not require permission. Incorporating a significant amount of example
code from this book into your product’s documentation does require permission.
We appreciate, but do not require, attribution. An attribution usually includes the title,
author, publisher, and ISBN. For example: “Ajax: The Definitive Guide, by Anthony T.
Holdener III. Copyright 2008 Anthony T. Holdener III, 978-0-596-52838-6.”
If you feel your use of code examples falls outside fair use or the permission given
here, feel free to contact us at permissions@oreilly.com.

How to Contact Us
Please address comments and questions concerning this book to the publisher:
O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707-829-0104 (fax)
We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at:
http://www.oreilly.com/catalog/9780596528386
You can also download the examples from the author’s web site:
http://ajax.holdener.com/
To comment or ask technical questions about this book, send email to:
bookquestions@oreilly.com
For more information about our books, conferences, Resource Centers, and the
O’Reilly Network, see our web site at:
http://www.oreilly.com/

Safari® Books Online
When you see a Safari® Books Online icon on the cover of your
favorite technology book, that means the book is available online
through the O’Reilly Network Safari Bookshelf.
Safari offers a solution that’s better than e-books. It’s a virtual library that lets you
easily search thousands of top tech books, cut and paste code samples, download
chapters, and find quick answers when you need the most accurate, current information. Try it for free at http://safari.oreilly.com.

Preface

www.it-ebooks.info

| xix

Acknowledgments
I could never have imagined when I started writing this, my first book, just how
much work and time would go into it, or how I would rely on so many others to
complete this undertaking.
First and foremost, I want to thank my wife, Sarah, for her love, support, and understanding. Sarah, without you, I never would have succeeded in this endeavor. I love
you with all my heart. Thank you for allowing me the late nights and countless weekends to work toward this dream. You have sacrificed so much of your life taking care of
things while I could not, and for that, I do not have the words to express my gratitude.
I want to thank Kate and Tony for their understanding that Daddy was not there for
the better part of a year. I hope that as you get older, you will use this as an example
of knowing that your dreams are attainable with hard work. I love you both, and I
hope to make up the time that I have missed. Kate and Tony, Daddy is not working
on his ’puter.
I want to thank my family, and that goes to everyone who chipped in and helped
with the kids and I do not even know what else, as I worked away on my laptop. All
of you gave up countless hours of your own time so that I could write. It humbles me
to know I have so much love and support around me.
I want to thank Gateway EDI, Inc. for their willingness to work with me as I
crunched to get this book finished. It was reassuring to know that I had that support
from them, and it made it less stressful down the home stretch.
I want to thank all of my reviewers; no matter how small your contribution, I am
grateful for the comments, suggestions, and corrections that I received. Thanks go to
John Aughey, Prerit Bhakta, Zachary Kessin, Steve Olson, Bruce W. Perry, Stacy
Trease, and Chris Wells—all of the work has been much appreciated.
I want to thank Simon St.Laurent, my editor, for calming me down when I would
start to panic, working with me to give me the time that I needed, and giving me the
chance to write this book in the first place. This whole process, being my first and,
hopefully, not last, was made almost painless with your help and guidance. I cannot
begin to thank you enough.
I also want to thank everyone else who helped get this book ready for production.
Thanks to Audrey Doyle for all of the catches, corrections, and changes that have
made this so much more readable. Thanks to Rachel Monaghan for all of the work
you put into the production of this book, as well as the proofreading. Thank you,
Karen, for giving me such a great animal! And thanks to Ellen, David, Jessamyn, and
everyone else who made this book what it is.
I have spent more than a year working to see this book become a reality. Everything
else in my life took somewhat of a backseat as this happened. I want everyone to
know that with the publication of this book comes the completion of one of my life
goals—I could not have done it without all of the support that I received.
xx |

Preface

www.it-ebooks.info

PART I
I.

Ajax Fundamentals

Chapters 1 through 6 provide the basic ideas that form the fundamental core of Ajax
and building Ajax applications. This part of the book discusses the technologies and
foundations that you will need to know before moving on to Ajax within applications and as components.
Chapter 1, Reinventing the Web
Chapter 2, From Web Sites to Web Applications
Chapter 3, Servers, Databases, and the Web
Chapter 4, Foundations: Scripting XML and JSON
Chapter 5, Manipulating the DOM
Chapter 6, Designing Ajax Interfaces

www.it-ebooks.info

www.it-ebooks.info

Chapter 1

CHAPTER 1

Reinventing the Web

1

Back in 1996, the Web was incredibly exciting, but not a whole lot was actually happening on web pages. Programming a web page in 1996 often meant working with a
static page, and maybe a bit of scripting helped manage a form on that page. That
scripting usually came in the form of a Perl or C Common Gateway Interface (CGI)
script, and it handled basic things such as authorization, page counters, search queries, and advertising. The most dynamic features on the pages were the updating of a
counter or time of day, or the changing of an advertising banner when a page
reloaded. Applets were briefly the rage for supplying a little chrome to your site, or
maybe some animated GIF images to break the monotony of text on the page.
Thinking back now, the Web at that time was really a boring place to surf.
But look at what we had to use back then. HTML 2.0 was the standard, with HTML
3.2 right around the corner. You pretty much had to develop for Internet Explorer 3.0
or Netscape Navigator 2.1. You were lucky if someone was browsing with a resolution of 800 × 600, as 640 × 480 was still the norm. It was a challenging time to make
anything that felt truly cool or creative.
Since then, tools, standards, hardware technology, and browsers have changed so
much that it is difficult to draw a comparison between what the Web was then and
what it is today. Ajax’s emergence signals the reinvention of the Web, and we should
take a look at just how much has changed.
If you want to jump into implementation, skip ahead to Chapter 4.
You can always come back to reflect on how we got here.

Web Page Components
When a carpenter goes to work every day, he takes all of his work tools: hammer, saw,
screwdrivers, tape measure, and more. Those tools, though, are not what makes a
house. What makes a house are the materials that go into it: concrete for a foundation;

3

www.it-ebooks.info

wood and nails for framing; brick, stone, vinyl, or wood for the exterior—you get the
idea. When we talk about web tools, we are interested in the materials that make up
the web pages, web sites, and web applications, not necessarily the tools that are
used to build them. Those discussions are best left for other books that can focus
more tightly on individual tools. Here, we want to take a closer look at these web
tools (the components or materials, if you will), and see how these components have
changed over the history of the Web—especially since the introduction of Ajax.

Classic Web Components
The tools of the classic web page are really more like the wood-framed solid or wattle walls of the Neolithic period. They were crude and simple, serving their purpose
but leaving much to be desired. They were a renaissance, though. Man no longer
lived the lifestyle of a nomad following a herd, and instead built permanent settlements to support hunting and farming. In much the same way, the birth of the Web
and these classic web pages was a renaissance, giving people communication tools
they never had before.
The tools of the classic Web were few and simple:
• HyperText Markup Language (HTML)
• HyperText Transfer Protocol (HTTP)
Eventually, other things went into the building of a web page, such as CGI scripting
and possibly even a database.
The World Wide Web Consortium (W3C) introduced the Cascading
Style Sheets Level 1 (CSS1) Recommendation in December 1996, but
it was not widely adopted for some time after. Most of the available
web browsers were slow to adopt the technology. It wasn’t until
browser makers began to support CSS that it even made sense to start
using the technology.

HTML provided everything in a web page in the classic environment. There was no
separation of presentation from structure; JavaScript was in its infancy at best, and
could not be used to create “dynamic HTML” through Document Object Model
(DOM) manipulation, because there was no DOM. If the client and the server were
to communicate, they did so using very basic HTTP GET and, sometimes, POST
calls.

Ajax
Many more parts go into web sites and web applications today. Ajax is like the materials that go into making a high-rise building. High rises are made of steel instead
of wood, and their exteriors are modern and flashy with metals and special glass.

4

|

Chapter 1: Reinventing the Web

www.it-ebooks.info

The basic structure is still there, though (unless the building was designed by Frank
Lloyd Wright); walls run parallel and perpendicular to one another at 90-degree
angles, and all of the structure’s basic elements, including plumbing, electricity, and
lighting, are the same—they are just enhanced.
In this way, the structure of an Ajax application is built on an underlying structure of
XHTML, which was merely an extension of HTML, and so forth. Here are what I
consider to be the tools used to build Ajax web applications:
• Extensible HyperText Markup Language (XHTML)
• Document Object Model (DOM)
• JavaScript
• Cascading Style Sheets (CSS)
• Extensible Markup Language (XML)
Now, obviously, other things can go into building an Ajax application, such as
Extensible Stylesheet Language Transformation (XSLT), syndication feeds with RSS
and Atom (of course), some sort of server-side scripting (which is often overlooked
when discussing Ajax in general), and possibly a database.
XHTML is the structure of any Ajax application, and yes, HTML is too, but we
aren’t going to discuss older technology here. XHTML holds everything that is going
to be displayed on the client browser, and everything else works off of it. The DOM
is used to navigate all of the XHTML on the page. JavaScript has the most important
role in an Ajax application. It is used to manipulate the DOM for the page, but more
important, JavaScript creates all of the communication between client and server that
makes Ajax what it is. CSS is used to affect the look of the page, and is manipulated
dynamically through the DOM. Finally, XML is the protocol that is used to transfer
data back and forth between clients and servers.

Case Study
You may not think that changing and adding tools would have that much of an
impact on how a site functions, but it certainly does. For a case study, I want to turn
your attention to a site that actually existed in the classic web environment, and
exists now as a changed Ajax web application. Then there will be no doubt as to just
how far the Web has come.
The following is a closer look at MapQuest, Inc. (http://www.mapquest.com/), how it
functioned and existed in 2000, and how it functions today.

The application then
Most people are familiar with MapQuest, seen in Figure 1-1, and how it pretty much
single-handedly put Internet mapping on the map (no pun intended). For those who
are not familiar with it, I’ll give the briefest of introductions. MapQuest was

Web Page Components |

www.it-ebooks.info

5

launched on February 5, 1996, delivering maps and directions based on user-defined
search queries. It has been the primary source for directions and maps on the Web
for millions of people ever since (well, until Google, at least).

Figure 1-1. MapQuest’s home page in 2000, according to The Wayback Machine (http://www.
archive.org/)

As MapQuest evolved, it began to offer more services than just maps and driving
directions. By 2000, it offered traffic reports, travel guides, and Yellow and White
Pages as well. How did it deliver all of these services? The same way all other Internet sites did at the time: click on a link or search button, and you were taken to a
new page that had to be completely redrawn. The same held true for all of the map
navigation. A change in the zoom factor or a move in any direction yielded a round
trip to the server that, upon return, caused the whole page to refresh. You will learn
more about this client/server architecture in the section “Basic Web and Ajax Design
Patterns” in Chapter 2.
What you really need to note about MapQuest—and all web sites in general at the
time—is that for every user request for data, the client would need to make a round
trip to the server to get information. The client would query the server, and when the
server returned with an answer, it was in the form of a completely new page that

6

|

Chapter 1: Reinventing the Web

www.it-ebooks.info

needed to be loaded into the browser. Now, this can be an extremely frustrating process, especially when navigating a map or slightly changing query parameters for a
driving directions search. And no knock at MapQuest is intended here. After all, this
was how everything was done on the Internet back then; it was the only way to do
things.
The Web was still in its click-wait-click-wait stage, and nothing about a web page
was in any way dynamic. Every user interaction required a complete page reload,
accompanied by the momentary “flash” as the page began the reloading process. It
could take a long time for these pages to reload in the browser—everything on the
page had to be loaded again. This includes all of the background loading of CSS and
JavaScript, as well as images and objects. Figure 1-2 illustrates the flow of interaction on the Web as it was in 2000.
Page request 1

Page response 1
Page request 2

Page response 2
Page request n

Page response n

Figure 1-2. The flow of a typical interaction on the Web in 2000

The application now
In 2005, when Google announced its version of Internet mapping, Google Maps,
everything changed both for the mapping industry and for the web development
industry in general. The funny thing was that Google was not using any fancy new
technology to create its application. Instead, it was drawing on tools that had been
around for some time: (X)HTML, JavaScript, and XML. Soon after, all of the major
Internet mapping sites had to upgrade, and had to implement all the cool features
that Google Maps had, or they would not be able to compete in the long term.
MapQuest, shown in Figure 1-3, did just that.

Web Page Components |

www.it-ebooks.info

7

Jesse James Garrett coined the term Ajax in February 2005 in his
essay, “Ajax: A New Approach to Web Applications” (http://www.
adaptivepath.com/publications/essays/archives/000385.php). Although he
used Ajax, others began using the acronym AJAX (which stands for
Asynchronous JavaScript and XML). I prefer the former simply because
the X for XML is not absolutely necessary for Ajax to work; JavaScript
Object Notation (JSON) or plain text could be used instead.

Figure 1-3. MapQuest’s home page, after Ajaxification

Now, when you’re browsing a map, the only thing on the page that refreshes when
new data is requested is the map itself. It is dynamic. This is also the case when you
get driving directions and wish to add another stop to your route. The whole page
does not refresh, only the map does, and the new directions are added to the list. The
result is a more interactive user experience.
Ajax web applications remove the click-wait-click-wait scenario that has plagued the
Web for so long. Now, when you request information, you may still perform other
tasks on the page while your request (not the whole page) loads. All of this is done
by using the Ajax tools discussed earlier, in the “Ajax” section of this chapter, and

8

|

Chapter 1: Reinventing the Web

www.it-ebooks.info

the standards that apply to them. After reading the section “Standards Compliance,”
later in this chapter, you will have a better idea of why coding to standards is important, and what it means when a site does not validate correctly (MapQuest, incidentally, does not). Figure 1-4 shows how Ajax has changed the flow of interaction on a
web page.
Page request 1
XHR request 1
XHR request 2
XHR request n
XHR response n
XHR response 2
XHR response 1
Page response 1

Figure 1-4. The flow of an Ajax interaction within a web page

The addition of Ajax as a tool to use in web applications allows a developer to make
user interaction more similar to that of a desktop application. Flickering as a page is
loaded after user interaction goes away. The user will perceive everything about the
web application as being self-contained. With this technology a savvy developer can
make an application function in virtually the same way, whether on the Web or on
the desktop.

Modern Web Standards
Web standards: these two words evoke different feelings in different people. Some
will scoff and roll their eyes, some will get angry and declare the need for them, and
some will get on a soapbox and preach to anyone who will listen. Whatever your
view is, it is time to reach a common ground on which everyone can agree. The simple fact is that web standards enable the content of an application to be made available to a much wider range of people and technologies at lower costs and faster
development speeds.
Using the standards that have been published on the Web (and making
sure they validate) satisfies the following Web Accessibility InitiativeWeb Content Accessibility Guidelines (WAI-WCAG) 1.0 guideline:
• Priority 2 checkpoint 3.2: Create documents that validate to published formal grammars.

Modern Web Standards |

www.it-ebooks.info

9

In the earlier years of the Web, the browser makers were to blame for difficulties in
adopting web standards. Anyone that remembers the days of the 4.0 browsers, more
commonly referred to as the “Browser Wars,” will attest to the fact that nothing you
did in one environment would work the same in another. No one can really blame
Netscape and Microsoft for what they did at the time. Competition was stiff, so why
would either of them want to agree on common formats for markup, for example?
This is no longer the case. Now developers are to blame for not adopting standards.
Some developers are stuck with the mentality of the 1990s, when browser quirks mode,
coding hacks, and other tricks were the only things that allowed code to work in all
environments. Also at fault is “helpful” What You See Is What You Get (WYSIWYG)
software that still generates code geared for 4.0 browsers without any real thought to
document structure, web standards, separating structure from presentation, and so
forth.
Now several standards bodies provide the formal standards and technical specifications we all love and hold dear to our hearts. For our discussion on standards, we
will be concerning ourselves with the W3C (http://www.w3.org/), Ecma International (formerly known as ECMA; http://www.ecma-international.org/), and the Internet Engineering Task Force (IETF; http://www.ietf.org/). These organizations have
provided some of the standards we web developers use day in and day out, such as
XHTML, CSS, JavaScript, the DOM, XML, XSLT, RSS, and Atom.
Not only does Ajax use each standard, but also these standards are either the fundamental building blocks of Ajax or may be used in exciting ways with Ajax web
applications.

XHTML
On January 26, 2000, the W3C published “XHTML 1.0: The Extensible HyperText
MarkUp Language,” a reformulation of HTML 4.01 as XML. Unfortunately, even
today XHTML 1.0 is still not incorporated in a vast majority of web sites. It may be
that people are taking the “if it ain’t broke, don’t fix it” mentality when it comes to
changing their markup from HTML 4.01 to XHTML 1.0, it may be that people just
do not see the benefits of XML, or it may be, as is especially true in corporate environments, that there is simply no budget to change sites that already exist and
function adequately. Even after a second version of the standard was released on
August 1, 2002, incorporating the errata changes made to that point, it still was
not widely adopted.
On May 31, 2001, even before the second version of XHTML 1.0 was released, the
W3C introduced the “XHTML 1.1—Module-based XHTML Recommendation.”
This version of XHTML introduced the idea of a modular design, with the intention

10

|

Chapter 1: Reinventing the Web

www.it-ebooks.info

that you could add other modules or components to create a new document type
without breaking standards compliance (though it would break XHTML compliance); see Example 1-1. All deprecated features of HTML (presentation elements,
framesets, etc.) were also completely removed in XHTML 1.1. This, more than anything, slowed the adoption of XHTML 1.1 in the majority of web sites, as few people were willing to make the needed changes—redesigning site layout without frames
and adding additional attributes to elements, not to mention removing presentation
and placing that into CSS. Contributing to XHTML 1.1’s lack of deployment is the
fact that it is not backward-compatible with XHTML 1.0 and HTML.
Example 1-1. The simplest XHTML 1.1 document



Example 1-1. The simplest XHTML 1.1 document



Hello World!
Although the vast majority of web sites out there are not following the XHTML 1.1 Recommendation, it has tremendous potential for certain key areas. The development of new applications on the Web, and the use of those applications on different platforms such as mobile and wireless devices, is leading to a greater rate of adoption than when XHTML 1.1 was first published. For this reason, I believe it is important to recognize the power and potential of XHTML 1.1. Therefore, we will follow this standard in nearly every example in this book (see Chapters 20 and 21 for different standards usage). With that said, we must be mindful that the future of web application development is being proposed right now. Already the W3C has a working draft for an XHTML 2.0 specification. In XHTML 2.0, HTML forms are replaced with XForms, HTML frames are replaced with XFrames, and DOM Events are replaced with XML Events. It builds on past recommendations, but when the XHTML 2.0 Recommendation is published, it will define the beginning of a new era in web development. You should note that XHTML 2.0 is not designed to be backward-compatible. Development taking advantage of this recommendation will most likely be geared toward more specialized audiences that have the ability to view such applications, and not the general public. It will be some time before this recommendation gets its feet off the ground, but I felt that it was worth mentioning. You can find more information on the XHTML family of recommendations at http://www.w3.org/MarkUp/. Modern Web Standards | www.it-ebooks.info 11 The New Kid on the Block? The Web Hypertext Application Technology Working Group (WHATWG) announced its arrival June 4, 2004. Its mission, according to its web site, is “to develop specifications based on HTML and related technologies to ease the deployment of interoperable Web Applications, with the intention of submitting the results to a standards organization.” The group was formed with the idea of creating a single development environment on which web applications are built. To that end, it is publishing technical specifications intended for implementation in what it calls “mass-market web browsers” such as Safari, Mozilla, and Opera. Its current work, now proceeding jointly with the W3C, is HTML 5. HTML 5 abandons the strictness of XML that XHTML had adopted, and focuses on adding new features to HTML itself. Added elements include nav, article, aside, section, header, footer, mark, time, meter, progress, figure, dialog, datagrid, details, menu, command, and more. HTML 5 is currently only a draft specification, and is not available in browsers. JavaScript Netscape Communications Corporation’s implementation of ECMAScript, now a registered trademark of Sun Microsystems, Inc., is JavaScript. It was first introduced in December 1995. In response, Microsoft developed its own version of the ECMA standard, calling it JScript. This confused a lot of developers, and at the time it was thought to contribute to the incompatibilities among web browsers. These incompatibilities, however, are more likely due to differences in DOM implementation rather than JavaScript or its subset, ECMAScript. The European Computer Manufacturer’s Association (ECMA) International controls the recommendations for ECMAScript. JavaScript 1.5 corresponds to the ECMA-262 Edition 3 standard that you can find at http://www.ecma-international.org/publications/ standards/Ecma-262.htm. As of 2009, the latest implemented version of JavaScript is 1. 9, which builds upon all of its predecessors (1.5 through 1.8.1) - all of which correspond to ECMA-262 Edition 3 starting at 1.5. This latest addition includes ECMAScript 5 compliance, and is projected to first be seen in Mozilla Firefox 4. JavaScript technically does not comply with ECMA International standards. Mozilla has JavaScript, Internet Explorer has JScript, and Opera and Safari have other ECMAScript implementations, though it should be noted that Mozilla is closer to standards than Internet Explorer is. Most of these browsers have now implemented to at least JavaScript 1.7, with the exception being Internet Explorer and surprisingly, Opera, who have still only implemented to JavaScript 1.5. For this reason all code examples, unless otherwise noted, are based on this version. 12 | Chapter 1: Reinventing the Web www.it-ebooks.info The DOM The Document Object Model, a Level 2 specification built onto the existing DOM Level 1 specification, introduced modules to the specification. The Core, View, Events, Style, and Traversal and Range modules were introduced on November 13, 2000. The HTML module was introduced on January 9, 2003. The DOM Level 3 specification built onto its predecessor as well. The modules changed around somewhat, but what this version added to DOM Level 2 was greater functionality to work with XML. This was an important specification, as it adds to the functionality of Ajax applications as well. The Validation module was published on December 15, 2003. The modules Core and Load and Save were published on April 7, 2004. Not all of the modules for DOM Level 3 have become recommendations yet, and because of that they bear watching. The Abstract Schemas module has been a note since July 25, 2002; Events has been a working group note since November 7, 2003 (though it was updated April 13, 2006); XPath has been a working group note since February 24, 2004; and Requirements and Views and Formatting have been working group notes since February 26, 2004. These modules will further shape the ways in which developers can interact with the DOM, subsequently shaping how Ajax applications perform as well. The W3C’s DOM Technical Reports page is located at http://www.w3.org/DOM/ DOMTR. Cascading Style Sheets (CSS) The W3C proposed the “Cascading Style Sheets Level 2 (CSS2) Recommendation” on May 12, 1998. Most modern browsers support most of the CSS2 specifications, though there are some issues with full browser support, as you will see in the “Browsers” section, later in this chapter. The CSS2 specification was built onto the “Cascading Style Sheets Level 1 (CSS1) Recommendation,” which all modern browsers should fully support. Because of poor adoption by browsers of the CSS2 Recommendation, the W3C revised CSS2 with CSS2.1 on August 2, 2002. This version was more of a working snapshot of the current CSS support in web browsers than an actual recommendation. CSS2.1 became a Candidate Recommendation on February 24, 2004, but it went back to a Working Draft on June 13, 2005 to fix some bugs and to match the current browser implementations at the time. Browsers are working toward full implementation of the CSS2.1 standard (some more than others), even though it is still a working draft, mainly so that when the newer Cascading Style Sheets Level 3 (CSS3) finally becomes a recommendation they Modern Web Standards | www.it-ebooks.info 13 will not be as far behind the times. CSS3 has been under development since 2000, and is important in that it also has taken into account the idea of modularity with its design. Beyond that, it defines the styles needed for better control of paged media, positioning, and generated content, plus support for Scalable Vector Graphics (SVG) and Ruby. These recommendations will take Ajax web development to a whole new level, but as of this writing CSS3 is very sparsely implemented. So, this book will primarily be using the CSS2.1 Recommendation for all examples, unless otherwise noted. You can find more information on the W3C’s progress on CSS at http://www.w3.org/ Style/CSS/. XML XML is the general language for describing different kinds of data, and it is one of the main data transportation agents used on the Web. The W3C’s XML 1.0 Recommendation is now in its fifth edition: the first was published on February 10, 1998 while the latest edition was published on November 26, 2008. At the same time as edition three was being released (February 4, 2004), the W3C also published the XML 1.1 Recommendation, which gave consistency in character representations and relaxed names, allowable characters, and end-of-line representations. The second edition of XML 1.1 was published on September 29, 2006. Though both XML 1.0 and XML 1.1 are considered current versions, this book will not need anything more than XML 1.0. People like XML for use on the Web for a number of reasons. It is self-documenting, meaning that the structure itself defines and describes the data within it. Because it is plain text, there are no restrictions on its use, an important point for the free and open Web. And both humans and machines can read it without altering the original structure and data. You can find more on XML at http://www.w3.org/XML/. Even though Ajax is no longer an acronym and the X in AJAX is now just an x, XML is still an important structure to mention when discussing Ajax applications. It may not be the transportation mode of choice for many applications, but it may still be the foundation for the data that is being used in those applications by way of syndication feeds. Syndication The type of syndication that we will discuss here is, of course, that in which sections of a web site are made available for other sites to use, most often using XML as the transport agent. News, weather, and blog web sites have always been the most common sources for syndication, but there is no limitation as to where a feed can come from. The idea of syndication is not new. It first appeared on the Web around 1995 when R. V. Guha created a system called Meta Content Framework (MCF) while working 14 | Chapter 1: Reinventing the Web www.it-ebooks.info for Apple. Two years later, Microsoft released its own format, called Channel Definition Format (CDF). It wasn’t until the introduction of the RDF-SPF 0.9 Recommendation in 1999, later renamed to RSS 0.9, that syndication feeds began to take off. For much more on syndication and feeds see Developing Feeds with RSS and Atom, by Ben Hammersley (O’Reilly). RSS RSS is not a single standard, but a family of standards, all using XML for their base structure. Note that I use the term standard loosely here, as RSS is not actually a standard. (RDF, the basis of RSS 1.0, is a W3C standard.) This family of standards for syndication feeds has a sordid history, with the different versions having been created through code forks and disagreements among developers. For the sake of simplicity, the only version of RSS that we will use in this book is RSS 2.0, a simple example of which you can see in Example 1-2. Example 1-2. A modified RSS 2.0 feed from O’Reilly’s News & Articles Feeds O'Reilly News/Articles http://www.oreilly.com/ O'Reilly's News/Articles Copyright O'Reilly Media, Inc. en-US http://blogs.law.harvard.edu/tech/rss Buy Two Books, Get the Third Free! http://www.oreilly.com/store http://www.oreilly.com/store webmaster@oreillynet.com (O'Reilly Media, Inc.) New! O'Reilly Photography Learning Center http://digitalmedia.oreilly.com/learningcenter/ http://digitalmedia.oreilly.com/learningcenter/ webmaster@oreillynet.com (O'Reilly Media, Inc.) Modern Web Standards | www.it-ebooks.info 15 Make sure you know which RSS standard you are using: • RDF Site Summary (RSS 0.9 and 1.0) • Rich Site Summary (RSS 0.91 and 1.0) • Really Simple Syndication (RSS 2.0) Each syndication format is different from the next, especially RSS 1.0. (This version is more modular than the others, but also more complex.) Most RSS processors can handle all of them, but mixing pieces from different formats may confuse even the most flexible processors. Atom Because of all the different versions of RSS and resulting issues and confusion, another group began working on a new syndication specification, called Atom. In July 2005, the IETF accepted Atom 1.0 as a proposed standard. In December of that year, it published the Atom Syndication Format protocol known as RFC 4287 (http:// tools.ietf.org/html/4287). An example of this protocol appears in Example 1-3. There are several major differences between Atom 1.0 and RSS 2.0. Atom 1.0 is within an XML namespace, has a registered MIME type, includes an XML schema, and undergoes a standardization process. By contrast, RSS 2.0 is not within a namespace, is often sent as application/rss+xml but has no registered MIME type, does not have an XML schema, and is not standardized, nor can it be modified, as per its copyright. Example 1-3. A modified Atom feed from O’Reilly’s News & Articles Feeds O'Reilly News/Articles O'Reilly's News/Articles Copyright O'Reilly Media, Inc. http://www.oreilly.com/ Buy Two Books, Get the Third Free! http://www.oreilly.com/store O'Reilly Media, Inc. New! O'Reilly Photography Learning Center http://digitalmedia.oreilly.com/learningcenter/ O'Reilly Media, Inc. XSLT XSLT is an XML-based language used to transform, or format, XML documents. On November 16, 1999, XSLT version 1.0 became a W3C Recommendation. As of January 23, 2007, XSLT version 2.0 is a Recommendation that works in conjunction with XPath 2.0. (Most browsers currently support only XSLT 1.0 and XPath 1.0.) XSLT uses XPath to identify subsets of the XML document tree and to perform calculations on queries. We will discuss XPath and XSLT in more detail in Chapter 5. For more information on the XSL family of W3C Recommendations, visit http:// www.w3.org/Style/XSL/. XSLT takes an XML document and creates a new document with all of the transformations, leaving the original XML document intact. In Ajax contexts, the transformation usually produces XHTML with CSS linked to it so that the user can view the data in his browser. Browsers Like standards, browsers can be a touchy subject for some people. Everyone has a particular browser that she is comfortable with, whether because of features, simplicity of use, or familiarity. Developers need to know, however, the differences among the browsers—for example, what standards they support. Also, it should be noted that it’s not the browser, but rather the engine driving it that really matters. To generalize our discussion of browsers, therefore, it’s easiest to focus on the following engines: • Gecko • Trident • KHTML/WebKit • Presto Table 1-1 shows just how well each major browser layout engine supports the standards we have discussed in this chapter, as well as some that we will cover later in the book. Browsers www.it-ebooks.info | 17 Table 1-1. Standards supported by browser engines Gecko Trident KHTML/WebKit Presto HTML Yes Yes Yes Yes XHTML/XML Yes Partial Yes Yes CSS1 Yes Yes Yes Yes CSS2 (CSS2.1) Yes Partial Yes Yes CSS3 Partial Partial Partial Partial DOM Level 1 Yes Partial Yes Yes DOM Level 2 Yes No Yes Yes DOM Level 3 Partial No Partial Partial RSS Yes Yes Yes Yes Atom Yes Yes Yes Yes JavaScript 1.8.1 1.5 1.7 1.5 PNG alpha-transparency Yes Yes Yes Yes XSLT Yes Yes Yes Yes SVG Partial No Partial Partial XPath Yes Yes No Yes Ajax Yes Yes Yes Yes Progressive JPEG Yes No Yes Yes Gecko Gecko is the layout engine built by the Mozilla project and used in all Mozillabranded browsers and software. Some of these products are Mozilla Firefox, Netscape, and K-Meleon. One of the nice features of Gecko is that it is cross-platform by design, so it runs on several different operating systems, including Windows, Linux, and Mac OS X. Trident Trident is the layout engine that Internet Explorer (Windows versions only) has used since version 4.0, and it is sometimes referred to as MSHTML. AOL Explorer and Netscape use it as well (Netscape can use either Gecko or Trident). KHTML/WebKit KHTML is the layout engine developed by the KDE project. The most notable browsers that use KHTML are KDE Konqueror and Apple’s Safari, though Safari uses a variant called WebKit, which Google’s Chrome and OmniWeb also use.. 18 | Chapter 1: Reinventing the Web www.it-ebooks.info Gecko’s Future The roadmap to Gecko 1.9 shows that it will add support for some of the proposals made by the WHATWG, which would enable developers to build web applications more easily. In addition, the graphics engine is in major maintenance mode—or, I should say, it is being trashed and replaced by Cairo (http://cairographics.org/introduction). This will give Gecko more modern 2D graphics capabilities: • • • • Filling Stroking and clipping Affine transforms Total alpha transparency support Also, via Glitz, 3D graphics card acceleration will be used to speed up 2D image rendering. Unlike the Gecko of the past, there will be a single rendering pipeline for XHTML, CSS, Canvas, and SVG. This will allow SVG effects to be applied to XHTML content. Gecko 1.9 is also implementing JavaScript 2 (Edition 4 of ECMA-262), though it may not have full support for the proposal written by Waldemar Horwat, as Edition 4 is similar but not the same. Presto Presto is the layout engine developed by Opera Software for the Opera web browser. The engine is also used in the Mac OS X versions of Macromedia Dreamweaver MX and later. Presto is probably the most standards-compliant browser out there today. Others Other layout engines support browsers on the Web, but these browsers make up less than two percent of all browsers in use today, and maybe even less than that. These layout engines support a wide range of standards, but none of these browsers implements any standard that another one of the aforementioned layout engines does not already implement. Standards Compliance So far, I have pointed out the current standards and when they were introduced, as well as which browsers support them, but I still need to answer a burning question: “Why program to standards, anyway?” Let’s discuss that now. What is one of the worst things developers have to account for when programming a site for the Internet? That answer is easy, right? Backward compatibility. Standards Compliance | www.it-ebooks.info 19 Developers are always struggling to make their sites work with all browsers that could potentially view their work. But why bend over backward for the 0.01 percent of people clinging to their beloved 4.0 browsers? Is it really that important to make sure that 100 percent of the people can view your site? Some purists will probably answer “yes,” but in this new age of technology, developers should be concerned with a more important objective: forward compatibility. Forward compatibility is, in all actuality, harder to achieve than backward compatibility. Why? Just think about it for a minute. With backward compatibility, you as a developer already know what format all your data needs to be in to work with older browsers. This is not the case with forward compatibility, because you are being asked to program to an unknown. Here is where standards compliance really comes into play. By adhering to the standards that have been put forth and by keeping faith that the standards bodies will keep backward compatibility in mind when producing newer recommendations, the unknown of forward compatibility is not so unknown. Even if future recommendations do not have built-in backward compatibility, by following the latest standards that have been put forth, you will still, in all likelihood, be set up to make a smoother transition if need be. After all, instead of worrying whether my site works for a browser that is nine years old and obsolete, I would rather worry that my site will work, with only very minor changes, nine years from now. Wouldn’t you? Keep in mind, too, that by complying with the latest standards, you are ensuring that site accessibility can still be achieved. For examples of maintaining accessibility, see “Accessibility” in Chapter 6. After all, shouldn’t we be more concerned with making our sites accessible to handicapped viewers than to viewers whose only handicap is that they have not upgraded their browsers? And why not have standards-compliant sites now? I mean, come on. Most of the recommendations that I laid out earlier are not exactly new. XHTML 1.1 is from 2001. DOM Level 3 is from 2003 and 2004. The recommendations for CSS2 started in 1998. The latest XML is from 2004, and XSLT has not had a new recommendation since 1999. It is time to give the users of the older browsers reasons to upgrade to something new, because let’s face it, if they haven’t upgraded by now (we are talking about almost a decade here!), they are never going to unless they are pushed to do so. It is time to give old browser users that push, and to give users of the current browsers the sites they deserve to have. Welcome to Web 2.0 So, what exactly do users deserve? They deserve interaction, accessibility, and functionality; but most of all, they deserve for the Web to be a platform, and Ajax is the means to that end. With Ajax, you can make the interface in the browser be just like a desktop application, and it can react faster and offer functionality that web users 20 | Chapter 1: Reinventing the Web www.it-ebooks.info have not traditionally had in the past (such as inline editing, hints as you type, etc.). Sites can be built that allow unprecedented levels of collaboration. But what, you may ask, is in it for the developers and clients paying for this platform? The answer: lower costs, better accessibility, more visibility, and better perception. A great plus to building a standards-based Ajax web application is that it is so much easier to maintain. By separating presentation from content, you are allowing your application to be more easily modified and updated. It also reduces the size of files, consuming less bandwidth. This equals less money spent on making those changes and lower hosting costs for your application. Another plus is that your web application becomes more accessible to your viewers. A well-built web application functions in a manner in which users have come to expect from desktop applications, and can more easily adapt to your site. Also, the accessibility for handicapped viewers is more readily available (we will discuss the coding for such sites in later chapters). Search engines can more easily interpret the relevance of text on your site when the application is coded correctly. This leads to better visibility on these search engines, and more viewing of your application, as you are better represented by user queries. Finally, users will have a better perception of your application when it provides easyto-use navigation, reacts quickly, and functions correctly in their browsers. And who can perceive a site badly when it loads quickly, yielding better user experiences? Ajax web development gives you everything you need. And what makes Ajax special is that it is not a new technology—it is the combination of many technologies that have been around for a while and that are production-tested. User interaction, fast response time, desktop-like features: web applications are no longer something that you can only dream of for the future. Web applications are in the here and now. Welcome to Web 2.0 with Ajax. Welcome to Web 2.0 www.it-ebooks.info | 21 Chapter 2 2 CHAPTER From Web Sites to Web Applications 2 Ajax web applications are here, and they are the future of the Web. The big question at this point is, how do we get there? How do we get from simple web sites to web applications? This seems easy on the surface, right? Unfortunately, it’s not easy. Developing an application, whether it is on the desktop or on the Web, takes more forethought than the old model of web design did. Think for a minute about the old model. Sure, you could lay out your site and know what pages you wanted linked to other pages, or maybe you could draw a simple flow diagram, but that was usually as far as it went. Need to add another page? No problem: you’d create it and stick the link for it wherever it needed to be. There is nothing wrong with this process, especially for small sites. Web sites in general are not inherently complicated, and they don’t need a more complex development model (though content management can be helpful). Application development, for the Web or otherwise, demands a more structured approach, however. If you want to jump into implementation, skip ahead to Chapter 4. You can always come back to reflect on best practices for development. The Transition The art of computer science slowly begins to creep back into the Web as the application life cycle begins. Any software developer can describe the life cycle of a software application. If a programmer does not learn it as part of her curriculum in school, you can bet she finds out what it is very quickly on the job. Why is this so important? Because it is a process that is tried and true (though not necessarily followed consistently). Figure 2-1 shows a typical life cycle model. Following are the phases of software application development. 22 www.it-ebooks.info Requirements analysis Upgrade Maintenance Software Development Life Cycle Design Implementation Release Testing Figure 2-1. The typical software development life cycle Requirements analysis Gathering the customer’s requirements and figuring out the appropriate way to proceed with each item. This phase usually produces a formal requirements document aimed at freezing all of the requirements so that the design phase may begin. Design Designing the software based on the requirements document. Programmers lay out classes and their members and methods, and might create UML diagrams for documentation. This phase produces a formal design document that the developers will use as a reference when they implement the design. Implementation The actual coding of the software. What is produced here is a working version of the software, maybe along with a user manual or some other software documentation. Testing Putting the software through a validation and verification process against the requirements document produced in the requirements analysis phase. Release Packaging the software in a manner suitable for distribution to the public. Maintenance Fixing any new bugs that may be discovered once the software has been released, and producing patches. Upgrade Identifying a need for the software to be enhanced or upgraded in some manner. At this point, the life cycle process starts over. The Transition | www.it-ebooks.info 23 This life cycle works, but it may be a little formal for most Ajax web development. Figure 2-2 shows a simpler Ajax web application life cycle. Planning Test and Release Web Application Development Life Cycle Implementation Design Figure 2-2. The Ajax web application development life cycle Why simplify the process for the Web? A lot of applications on the Web are the product of a more rapid development process, and simplifying the model makes it easier to keep that quick pace. An Ajax application can also follow the traditional development life cycle, but the simpler cycle fits better with the rapid iteration development style often used for web work. Rather than simply list the phases of the Ajax web application development life cycle, as I did with the software development life cycle, I will provide a more thorough examination. After all, each phase of the process works differently in web development than in typical software development. Although it presents a stripped-down approach, the following description still applies to a relatively formal process for developing Ajax applications. Depending on your project’s needs (and especially depending on the number of people involved), you may need more or less formality. Planning The first step in Ajax web application development is to sit down and plan what needs to be done. I call this the planning phase, but just as in the software development life cycle, you should devote some time in this phase to gathering requirements as well. These requirements come from the client and the developers. Both groups have input here, because developers sometimes choose the programming languages, servers, and databases that will be used as long as the client does not object. Likewise, the client gives input regarding his wants, which should be discussed openly with the developers in terms of feasibility, difficulty, and so forth. 24 | Chapter 2: From Web Sites to Web Applications www.it-ebooks.info One of the first things to identify is the target audience, which I will discuss in more detail in the section “Application Environments,” later in this chapter. Then you should determine the hardware and software that are to be used. The hardware and software requirements play a major role in deciding how an Ajax application is designed. You should analyze the types of data that are to be collected or displayed so that appropriate database structures can be designed and implemented. At times, a web application is to be a part of a larger site, so you should think about how the application will fit in with the existing system. After all of this is settled, you should write a formal requirements document. This helps both sides to remember what they agreed upon, and more important, it aids in the design phase. Design The design phase of Ajax web application development is probably the most important phase in the life cycle. It involves more than simply organizing how the requirements set in the first phase fit together. Yes, it involves the flow diagram, but this is also when agreement must be made on many other design issues. Foremost is what the application is going to look like. The target audience will have much clout when it comes to the application’s “look and feel,” but it will also determine accessibility needs. How majestically does the application degrade in older browsers? Must it meet guidelines set forth in Section 508 of the Rehabilitation Act of 1973? What priorities of the Web Content Accessibility Guidelines (WGAC) should it meet? These are some questions you should answer based on the application’s target audience. Then there is the specification of classes, methods, structures, and so forth that will be used as references during the implementation phase of development. The programming languages chosen during the planning phase play a major role here. How will the languages implement the features needed? Will a framework be used to implement the structure of the site? Of course, a major decision that you should make here is whether to use open source software. You may not think it is a big deal, but this choice will shape how the application is written and implemented. The types of third-party software introduced to the application also involve decisions regarding licensing, support, and management peace of mind. With open source software, quite a few different licensing scenarios may be in effect, and it is very important that your application follows these licensing agreements. Also, open source software generally does not have the formal support structure that other types of software provide, which may cause problems when critical issues need to be addressed right away. And last but most important is the question of whether your decisions regarding third-party components sit well with management. Managers hold the purse strings, and they must be satisfied that your solution is the right one for them going forward. The Transition | www.it-ebooks.info 25 Once everything has been designed, you should write formal design documents that include the site diagrams, UML diagrams, and possibly a prototype of the application. Developers will then have a better handle on how to implement the requirements, and with the use of a prototype the client can determine whether he likes the application’s design. Implementation At this point, the developers put their heads down and begin to code like mad. Well, that is the client’s hope, at any rate. Implementing an Ajax web application can involve many different people, all of whom should have a basic idea of how navigation and design are to function. Graphic designers, database administrators, and web developers all have a hand in application development during implementation. In the implementation phase of the life cycle, the developers should produce more than just the web application. They should also produce testing plans and technical documents; software is available to help them. Microsoft .NET has built-in documentation when C# documentation comments are used. Other inline documentation exists for PHP, Java™, and JavaScript as well. Test and Release When the developers have produced the testing plans and declare parts of the application as ready, testing can begin. Testing is not the same for Ajax web applications as it is for typical desktop applications. All web-based applications need intense scrutiny, as they are to function on a multitude of different environments. Some common things to test are: • Cross-browser compatibility • Validation • Broken links • Load • Resolution • Stress Environments can differ dramatically. However, a web application should be able to yield acceptable response times for both a user with a broadband connection and a user with a dial-up connection. It should work in all browsers targeted in the planning phase on all resolutions. 26 | Chapter 2: From Web Sites to Web Applications www.it-ebooks.info Documentation Made Easy Microsoft built into its C# compiler the ability to produce documentation when the code is compiled utilizing XML. It is as simple as adding a comment to a piece of code using a triple slash instead of just a double slash, and using some predefined elements: /// /// /// /// /// /// /// This function builds a simple XML file for the client to parse. Returns an XML string. See the C# Documentation page of the Visual C# Developer Center at http://msdn. microsoft.com/vcsharp/programming/documentation/ for more information on how this form of documentation works. PHP, Java, and JavaScript use a different method for inline documentation, but follow the same basic principle of using comments to document code. They all use the block comment and add a second asterisk to the opening of the block. Then, inside the comment block, elements delineated by the at symbol (@) provide the documentation structure: /** * This function builds a simple XML file for the client to parse. * * @author Anthony T. Holdener III * @since Version 0.5.3-23 * @return string Returns an XML string. * @see ui::get_xml( ) */ Then the source code must be parsed to provide the documentation. The different documentation parsers available for these languages provide the same basic functionality. PHP uses phpDocumentor (http://www.phpdoc.org/), Java uses Javadoc (http://java. sun.com/j2se/javadoc/writingdoccomments/index.html), and JavaScript uses JSDoc (http://jsdoc.sourceforge.net/). Of course, other parsers are available if you do a Google search for them, but the ones listed here are the easiest to use. At this point, you also need to address patching and retesting for the general bugs that are typical in desktop and web applications alike. This includes things such as server-scripting errors and broken links on pages. If agreed upon in the planning phase, all pages should be validated against parsers such as the World Wide Web Consortium (W3C) Markup Validation Service at http://validator.w3.org/, and the W3C CSS Validation Service at http://jigsaw.w3.org/css-validator/. The Transition | www.it-ebooks.info 27 The W3C has open source validators for a number of quality assurance needs other than just HTML/XHTML and CSS. A link checker is available, as well as validators for RDF documents, feeds, Platform for Privacy Preferences (P3P) adherence, and XML schema. You can find links to all of these at http://www.w3.org/QA/Tools/. Other validators are freely available on the Web besides those of the W3C. The Web Design Group (WDG) maintains a good list of validators on its web site, at http://www.htmlhelp.org/links/validators.htm, as well as its own validators and others found in the tools section of the site (http://www.htmlhelp.org/tools/). When all parts of the application have been tested, the Ajax web application is released to the target audience. All that is left at this point is to patch bugs when they invariably crop up, and wait for the next upgrade to the site. Basic Web and Ajax Design Patterns Design patterns! Now, before anyone gets too excited, this isn’t going to be another book that talks about the general subject of design patterns. Erich Gamma et al. did a fine job of that in the book Design Patterns (Addison-Wesley). There is a time and place for further discussions, and this isn’t it. Instead, I want to take a look at the development of the overall design pattern that defines an Ajax web application. The simplest definition of a design pattern is “the solution to a problem in generic terms.” I want to keep the discussion of an Ajax design pattern to that. Check out Ajax Design Patterns, by Michael Mahemoff (O’Reilly), for a vastly more detailed look at design patterns as they relate to specific Ajax problems and their solutions. To begin our discussion of design patterns, we’ll study the classic model of an Internet site and see how it evolved into the design pattern used today in Ajax web applications. Client/Server When web sites were first being built and all of the content was static, the Web as a whole was built on client/server architecture. This architecture is basically predicated on the thought that many clients (web browsers in this case) connect to servers that host web pages, as shown in Figure 2-3. With this environment, the client is active, sending requests and waiting for replies from a server that is passive, waiting for requests and sending them when asked for. This basic pattern was all that the Web needed until the introduction of forms and server-side scripting. This allowed servers to begin to deliver and process more dynamic data. Eventually, the use of simple databases entered the scene, and the design pattern of the Web changed. 28 | Chapter 2: From Web Sites to Web Applications www.it-ebooks.info Client Server Figure 2-3. The client/server model for the Web Basic Three-Tier With the introduction of databases and database servers to the architecture, the Web became a three-tier design pattern, as shown in Figure 2-4. Now, the client made a request to the server, which processed that request and could, in turn, request data from the database server. Client Server Data Figure 2-4. The basic three-tier model for the Web This design pattern evolved over time as server scripting became more robust, and more could be done with browsers. The software architecture design pattern slowly crept into the picture, and instead of viewing the architecture in terms of clients and servers, it viewed the architecture in a more abstract manner. In this pattern, three separate modules interact with one another: a user interface, a business or process logic, and a data access module. You could easily transform this type of pattern into a multitier architecture by adding modules to the design. The importance of this type of design pattern is that it allows you to modify one layer while having only a minimal effect on the other layers. Basic Web and Ajax Design Patterns | www.it-ebooks.info 29 Model-View-Controller From here, more complex design patterns evolved from the three-tier pattern that related more to the web application itself. One of them is the Model-View-Controller (MVC) architecture, shown in Figure 2-5. This design pattern separates the user interface, control logic, and data model into three separate components. With MVC, the end user interacts with the user interface through the browser. The controller is in charge of input events from the user interface, and when it receives these events, it calls the model and updates a view according to the user’s action. The view creates a new user interface according to the data from the model, but the model never talks directly with the view. Then the user interface waits for new input from the user, and the pattern starts over again. Calls Business logic Controller Request Return forward Forward Client Response View Update Uses Model Figure 2-5. The Model-View-Controller design pattern For web applications, the view module is in charge of building the XHTML whenever there is a user request. The controller is all of the navigation code that runs the application, and it can be both client- and server-side scripting. The model is the data access module for the design pattern, handling most data access requests and all business logic. I said most because if there is a user request for an XML response, for example, the view module alone may respond through an XML transformation or something similar. Many of the server-side scripting languages now have frameworks that are based on the MVC design pattern, as you will see in Chapter 4. Rich Internet Applications Rich Internet Applications (RIAs) are Ajax web applications. They function like traditional desktop applications by changing the browser from a thin to a fat client, through the use of JavaScript. A truly robust RIA usually incorporates the MVC design pattern into its model for stability and reliability, as shown in Figure 2-6. 30 | Chapter 2: From Web Sites to Web Applications www.it-ebooks.info RIA Calls Request Business logic Controller Return forward Forward Client Response View Update Uses Model Figure 2-6. An RIA implementation on top of MVC RIA applications are getting a bigger and bigger push, as more people realize that their traditional desktop applications can be ported to the Web. This way of thinking has many merits: • Ajax web applications require no installation, updating, or distribution, as everything is served up by a web server. • Ajax web applications are less prone to virus attacks (generally). • Ajax web applications can be accessed anywhere, and if they are built properly, you can run them on any operating system. These merits are saving companies millions of dollars. But even if you aren’t a corporate mogul looking to save tons of cash, Ajax web applications are just plain cool to develop! Application Environments You can implement Ajax web applications in many environments. Each of them has special design considerations. Understanding the environment for which the application is to be built is as important as understanding for whom the application is to be built. Intranet A lot of Ajax web applications the public will never see, because they are meant for company intranets. Intranets, unfortunately, come in different shapes and sizes, so to speak, and there is never a one-size-fits-all approach to them. The first consideration for an intranet application is the browser environment in which you will be working. More often than not, a large corporation has standards dictating that all applications must be built for a certain browser. This makes your job easier. Application Environments | www.it-ebooks.info 31 Smaller environments, which do not have such standards, are harder to develop for. Your goal should be to attempt to develop toward one browser, to reduce incompatibility issues, code size, and code complexity. Speaking of company standards, the operating system that the client browser sits on is also an important consideration. Most large companies will have one operating system for all desktop applications to work in, and this is usually some flavor of Windows. A smaller company is more likely or willing to try an alternative to Windows that costs less money. This is an important consideration. If you intend for your Ajax web application to utilize a plug-in, you need to know whether the plug-in is supported in the operating system environment you are programming against. Let’s not forget about existing applications. Companies may want your Ajax web application to integrate with an existing web site, or maybe even a desktop application. It is much less confusing for the end user if things appear to work seamlessly together. Commercial A commercial Ajax web application is most likely the hardest environment in which to program. Why is this? Because in these situations, you must make absolutely sure that your application can function everywhere. It must work on every modern commercial browser, and it must work on every operating system. After all, this is why Ajax web applications are getting so much notice in the first place. Unlike desktop applications, it takes less code, programming, and money to roll out a commercial Ajax application. This is the environment where you should hold nothing back. It will need to be flashier than your typical application, and it needs to have functionality people don’t expect from the browser. At the same time, you need to reach as many potential customers as possible. If people can’t use your application because of accessibility or compatibility reasons, they won’t choose your business! Educational The educational environment poses its own challenges not seen in other environments. If the environment is slanted more for mathematics or computer science, chances are slim that it’s using a Windows operating system. You must get a handle on the browsers that the target audience is using. Here, the target audience will care less about how flashy the application looks and more about the functionality it has. Furthermore, math-based pages generally need to be in an XHTML document so that MathML features can be used to their fullest. Other educational environments may still have their own requirements, but you can likely treat them like any other intranet application. 32 | Chapter 2: From Web Sites to Web Applications www.it-ebooks.info Government Government environments have strict guidelines when it comes to putting an application on one of their servers. They will tell you what languages are acceptable and what standards need to be met. If your application is for any U.S. government agency, you are required by law under Section 508 of the Rehabilitation Act to make it accessible to all browsers. Other countries, such as Canada, Australia, and countries in the European Union, have similar laws. Make sure you understand all the guidelines that have been set before you begin to implement your Ajax web application. Governments may be strict, but they also want to be on the forefront of technology. Ajax is finding a happy home in this environment. Specific Content When you build an application that has a target audience geared toward a specific technology, it is quite acceptable to expect the audience to have the tools necessary to use the technology. If you are building a site that’s all about the latest Flash programming techniques, you should expect your visitors to have Flash installed, right? In these situations, you can really focus on the application’s functionality without worrying whether everyone will be able to use it. The Developer At this point, you’ve had a little primer on Ajax web development life cycles, the basic design pattern of Ajax applications, and considerations for different environments in which your application could be used. What else do you need to know to move from building web sites to building web applications? An important bit of information to hold on to is that a web application is just that: an application. It is more complex to build, it takes more time, and it requires more skilled developers to build it right. It isn’t as simple as opening a text browser, writing some markup, and saving it with an .html extension. A web application developer has to know XHTML, CSS, JavaScript, XML, and the Document Object Model (DOM) at a minimum. Most developers also know Extensible Stylesheet Language Transformation (XSLT), and syndication techniques. And remember, this is just the client side of things. In Chapter 3, we will explore the other side of Ajax, which is the server side. A developer should also understand how the HTTP server works, one or more server-side scripting languages, and databases. Web applications require developers to know a wider variety of things than a desktop application developer would ever need to know. If people were mystified by how web sites worked before, what will they think about Ajax web applications now? The Developer www.it-ebooks.info | 33 What Ajax Is Not Ajax is not the be-all and end-all solution to every new application being built for the Web. It is not even something that should be considered as an upgrade to every existing product. Ajax is a great model for building more modern, faster Web 2.0 applications, but only when they are built correctly. Throwing Ajax at every application can create complications, such as accessibility issues, cross-browser compliance nightmares, and requiring more intricate and complex programming to perform simple tasks. I want to make sure this is clear, before everyone gets all gung-ho and throws Ajax everywhere. Ajax is not for everything. Let me repeat that. Ajax is not for everything. Take a look at Appendix D for risks that Ajax can create. I love the Ajax model of design; I think it brings web applications closer to the capabilities of desktop applications. I also know, somewhat from experience, that Ajax is not the best solution for every project, and that it can sometimes overcomplicate what could have been a simple solution. As you read the rest of this book and you see Ajax solutions that I present to common web design issues, ask yourself whether Ajax is right for you and what you are trying to accomplish. It could fit perfectly, but it could also be the wrong solution for you after all. 34 | Chapter 2: From Web Sites to Web Applications www.it-ebooks.info Chapter 3 CHAPTER 3 Servers, Databases, and the Web 3 Most of this book will be about the client side, as people think of Ajax as something that works specifically in the browser. Ajax definitely needs server support to work, though. So far, we’ve looked at the standards and technology that form the backbone of an Ajax web application, and how these applications moved away from the traditional web site model. Now, it’s time to turn our attention to the server side of things. Servers still hand out all of the requested data to the client, so we cannot always focus on the client side. It is important to understand the different web servers, server-side scripting languages, and databases that are available to developers. How will you know which of these to choose? Well, the old saying “there is a place for everything, and everything has its place” has real merit here. I cannot tell you which web server is better, or what language you should use, or which database is the best. Those are choices each developer must make. To make that process a little easier, I will provide information on all of these choices and how they relate to Ajax web applications, with the hope that you will be able to back up with hard facts whatever choice you make. The fact is (and this is a good thing, really) that unlike on the client side, where you have to use XHTML or HTML, CSS, JavaScript, the Document Object Model (DOM), and so forth with no choice in the matter, on the server side you have many good choices to explore and vastly more opportunities to work with the tools you like and to avoid the ones that seem inconvenient. If you want to jump into client-side implementation, skip ahead to Chapter 4. You can always return to the server side of the conversation. 35 www.it-ebooks.info The Web Server Only two servers are widely used on the Web today: the Apache HTTP Server from the Apache Software Foundation (http://httpd.apache.org/) and Internet Information Services (IIS) from Microsoft (http://www.iis.net/). At the most rudimentary level, both of these HTTP servers function in the same basic way, as shown in Figure 3-1. A client browser requests information from the server in the form of an HTTP request. The server responds to the request by sending an appropriate response, usually in the form of an XHTML document, an image, a raw text file, or some other type of document. If the request is bad, the server’s response is in the form of an error. Client Process request Web server Request Send response Create response Text Image Sound Dynamic Figure 3-1. The typical model for an HTTP server According to the July 2009 Netcraft Web Server Survey (http://news.netcraft.com/ archives/2009/07/28/july_2009_web_server_survey.html), Apache had a 51.12 percent market share, whereas Microsoft had 23.99 percent (this is a combination of all servers using Personal Web Server, both PWS and PWS-95, and IIS, both IIS and IIS-W). This doesn’t automatically mean that Apache is better than IIS. For one thing, Apache has been around longer than IIS, giving Apache an edge since it’s already been integrated into a lot of systems. For another, Apache is open source software, and it is free. IIS only comes prebundled with the server versions of Windows and cannot be downloaded separately. Finally, Apache runs on pretty much every operating system out there—Windows, Mac OS X, and all flavors of Linux and Unix. IIS runs only on Windows. But what is really important when it comes to comparing different software applications is looking at their features. Table 3-1 examines the features available with Apache and IIS. 36 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Table 3-1. Web server features a Basic authentication https Virtual hosting CGI FastCGI Servlet SSI Apache HTTP Server Yes Yes Yes Yes Yes Yesa Yes Internet Information Services (IIS) Yes Yes Yes Yes No No Yes Apache HTTP Server can integrate seamlessly with Apache Tomcat to provide servlet support. Now, although security features such as authentication and https are important for a web server, because the topic is Ajax web applications our focus should be on what the web server can do for dynamic interaction. This is where CGI, FastCGI, servlets, and SSI come into play. All provide ways for the HTTP server to provide dynamic content back to the client. CGI The Common Gateway Interface (CGI) has been around forever. Well, since 1993 anyway. This was how dynamic content was served in the beginning, by accessing a program (usually written in Perl) that generated the requested content. The one problem with this technology is that it can overwhelm a web server if too many requests hit the server. This is because every CGI request generates a new copy of the program to be executed. How do we get around this dilemma? There are two ways, really. The first is to bone up on a compiled language such as C or Pascal. Compiled languages terminate faster, thus reducing the chances of server overload. The second way is through FastCGI. FastCGI FastCGI is a variation of CGI designed to reduce the load on the web server created by CGI’s multiple-process model. Instead of generating a process for each CGI request, FastCGI creates a persistent process that can handle many requests at one time. It does this by having the process use a multithreading technique that allows it to poll different connections virtually at the same time. Unfortunately, as with CGI, FastCGI sees its best performance when the program is written in a high-level language such as C or C++. Yes, you can use it with any scripting language, and you can use it with frameworks such as Ruby on Rails and Django. I simply do not see the Web moving in this direction, though. Because Ruby on Rails, Django, and others (as you will see later in the chapter) can also use embedded interpreters, and because there are other methods of delivering dynamic content The Web Server www.it-ebooks.info | 37 from the server, this is not as likely to pick up much steam. Remember that both of the major web servers do or will support FastCGI, so there is no reason to choose one over the other because of this technical factor. The embedded-interpreter alternative to FastCGI is through Apache’s compiled modules. These include modules such as mod_perl, mod_php, mod_python, and mod_ruby, though others are also available. The downside to using these modules is that there is no separation between the web server and the web application. Servlets If CGI or FastCGI is not your cup of tea, another dynamic content approach is servlets, Java’s answer to the dynamic content problem. A servlet is a Java object that listens on the server for requests and sends the necessary response back to the client. You can create these servlets automatically when you’re developing using JavaServer Pages (JSP). Servlets require a web engine, commonly called a web container, to provide an environment for the Java code to run in conjunction with the web server. Examples of some available web containers are: • Java System Application Server (http://www.sun.com/software/products/appsrvr/ index.xml) • Apache Tomcat (http://tomcat.apache.org/) • IBM’s WebSphere (http://www-306.ibm.com/software/websphere/) • Oracle Application Server (http://www.oracle.com/appserver/index.html) • WebObjects (http://www.apple.com/webobjects/) Chapter 5 of Java Enterprise in a Nutshell, Second Edition (O’Reilly), by Jim Farley et al., gives a good history of servlets and more information on how to implement them. Servlets respond fairly quickly to requests to the server for dynamic content, and they make a good environment for developing Ajax web applications. SSI The final option available to the developer for providing dynamic content is the Server Side Include (SSI). SSI was used mainly in the beginning to add the content that was needed on every, or almost every, page while being able to maintain the content section in one place. For a web server to recognize that there was SSI content, a different file extension (.shtml) was used, which invoked the web server’s parser. For example: 38 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info A SSI Example

An SSI example shown firsthand.

SSI, as shown here, was the precursor to the type of server-side includes web developers are accustomed to today. It brought to the Web the ability to embed programming languages directly within the HTML. Following this first SSI were more advanced server-side languages that eventually developed into object-oriented server-side scripting. These scripting languages are what’s being used today, and you have heard of all of them, I’m sure. Among them are Active Server Pages (ASP), PHP, JSP, Python, and Ruby. There are others, of course, but these deserve a closer look, as they are leading the server-side charge with Ajax. Server-Side Scripting Server-side scripting in the early days of web development was done with C, Pascal, or Perl for a CGI script. In the cases of C and Pascal, this was not even really scripting in the traditional sense, as these CGI “scripts” were compiled programs. They did what developers needed them to do: crank out dynamic content quickly. In fact, many CGI programs are still written in C, and they work faster and better than any true scripting language. MapServer (http://mapserver.gis.umn.edu/) is a good example of one of these. Scripting languages hold one distinct advantage over their compiled brethren: they have better portability. Think about a compiled language on a Windows system, or a Linux system, for that matter. If I wrote a program for Windows 2000, I relied on the DLLs for that operating system when I compiled my program. If I want to port that program to Windows Vista, I may have to do a lot of work to make sure all of the DLLs are compatible on the new system. Worse still, I may need to modify my code for it to compile correctly on the new system. This is true for the *NIXs as well. They all have libraries that are not compatible with one another, making portability a chore. With scripting languages, on the other hand, once the interpreter for the language in question has been ported to the operating system I want to port to, the script will move to the new system without needing any modifications. That is the beauty of scripting languages, and it’s why they are used so heavily in the Web 2.0 environment. Server-Side Scripting www.it-ebooks.info | 39 Before we go any further, I want to point out that of the languages I will be detailing next, I do not believe any particular one is better than another. They all have their pros and cons. I am not saying I do not have a favorite; I do. I am just not going to say, “You have to pick X because it is the best.” ASP/ASP.NET Microsoft introduced ASP in December 1996 with the distribution of IIS 3.0, and it was Microsoft’s solution for dynamic content for the Web. ASP uses the idea of built-in objects to allow for easier web site construction, for common needs such as Response, Request, and Session, among others. The most common scripting language used for ASP is Microsoft’s VBScript, though other languages could be used as well (JScript comes to mind). Since ASP is an SSI interpreted technology, it uses delimiters to separate scripting code from straight markup, as shown here: <% ' Hello world in ASP. Response.write "Hello world." %> As far as using ASP for Ajax, it can function fine as the server-side language that produces the dynamic content for the client. The biggest downsides to ASP are that it is slow due to its interpreted nature, and that Microsoft has abandoned it for a newer version. In January 2002, Microsoft unveiled its latest version (version 4) of ASP, calling it ASP.NET. ASP.NET is a completely different type of scripting language than ASP (now called “classic” ASP). It is compiled into DLLs that reside on the server, offering major speed increases over its predecessors. Like classic ASP, ASP.NET can be written in many different languages, including C#, VB.NET, and JScript.NET. Because it is compiled, these languages use what Microsoft calls a Common Language Runtime (CLR) to interpret the different languages into a common bytecode that then gets compiled into a DLL. Microsoft took a page out of its Windows development environment when designing ASP.NET, giving it a GUI environment for developing web pages. Unfortunately, the first two versions of ASP.NET (1.0 and 1.1) did not produce standards-compliant HTML and JavaScript using their built-in controls. ASP.NET version 2.0 addressed these issues when it came out in November 2005. The controls now produce standardscompliant XHTML, and there is also better support for CSS. 40 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Although the newest version of the .NET Framework (which is downloadable in service packs for Windows XP and Vista) is 3.5 SP1, do not confuse the numbers. The 3.5 Framework still uses the 2.0 version of the CLR—essentially the same ASP.NET, Windows Forms, and ADO.NET that come with the 2.0 Framework. It is presumed that the next version of the Framework, .NET 4.0, will come with a new version of the CLR. At that point, thought the 4.0 Framework will run side by side with earlier ones, you will have to learn a new model. Developing an Ajax application with ASP.NET was a little tricky in its first versions, basically because of the inherent fun of attaching JavaScript calls to events on elements, among other things. Now, however, Microsoft has Ajax.NET (formerly called Atlas), a package that has ready-to-use client- and server-side scripts. Other options for Ajax support with ASP.NET range from open source to commercial products. You can get a better list of available third-party libraries and extensions in Michael Mahemoff’s book, Ajax Design Patterns (O’Reilly), or by searching his Wiki at http://ajaxpatterns.org/. PHP PHP is the recursive acronym for PHP: Hypertext Preprocessor. Rasmus Lerdorf developed it in 1994, and at that time it was called Personal Home Page Tools, but Zeev Suraski and Andi Gutmans rewrote it in 1997. That version of PHP (PHP/FI) led to another rewrite of the PHP core, called the Zend engine. PHP 5 is the current version of PHP and it uses the Zend II engine. Much like other interpreted languages, PHP uses delimiters to separate scripting code from straight markup, as shown here: PHP, like most other server-side scripting languages being used, is object-oriented, starting with the release of PHP 5. Yes, there was class support in PHP 4, but it did not have any other object-oriented features. PHP also has a huge library of standard functions, which makes it faster to develop with. Plus, if you search on the Web, you’ll find thousands of PHP scripts that cover just about every programming problem imaginable. PHP is touted as a language that is easy to learn and makes developing dynamic content quick and painless. It supports most major databases and runs with all major web servers, on most major operating systems. PHP is, in a word, portable. Ajax web development is simple with PHP as the backend of an application—both as the language itself and, as you will see in our discussion of the Zend Framework later in this chapter, within a framework. Server-Side Scripting www.it-ebooks.info | 41 Lighting the LAMP LAMP (Linux, Apache, MySQL, PHP [Perl/Python]) is an acronym that started in Germany and has been buzzing around the Internet since the late 1990s. Once O’Reilly and MySQL AB popularized the term, it spread. It stands for the quintessential open source web development platform that has been around for a long time and sometimes does not get the recognition it deserves. But it is obviously out there. Refer to the Netcraft survey referenced in the section “The Web Server,” earlier in this chapter. There is no denying that Apache is the most-used web server on the Internet. Take a closer look at the survey and see the number of Apache servers using mod_php, mod_perl, or mod_python. Combine that with the trends you can see for PHP versus ASP/ASP.NET, Python, Ruby, and JSP by using Google’s latest toy, Google Trends (http://www.google.com/trends). The number of downloads for MySQL should clearly indicate its usage on the Web. As for Linux, it continues to gain ground, no matter how much you want to argue to the contrary. LAMP has become the platform of choice for development of high-performance web applications, especially if you just follow the open source model of the platform. Have the L stand for Linux, FreeBSD, Solaris, or any other open source operating system; the M stand for MySQL or PostgreSQL; and the P stand for PHP, Python, Perl, Java, or Ruby. There is, of course, really no altering Apache with the A. LAMP seems to be the Web 2.0 platform of choice too. Look at the list of innovative, inventive sites on the Web that use LAMP: Wikipedia, WordPress, MySQL AB, Amazon, Google, Yahoo!, and MySpace. These are all high-volume sites that use a model that obviously works. LAMP has also been incorporated into other corporate systems, including those of Disney and Boeing, to name a few. LAMP provides a stable, scalable, and cheap web platform for use with any Ajax web application. As the Web 2.0 movement grows with more Ajax web applications replacing the more classic sites, LAMP will be right there as well. Check out O’Reilly’s LAMP site, ONLamp.com, at http://www.onlamp.com/ for more on LAMP. Python Guido van Rossum created Python in 1990, not as a scripting language but as a general-purpose programming language. Python 2.1 came out in 2002 and is significant not just because it combined Python 1.6.1 and Python 2.0 into a single release, but because Python 2.1 was the first version of Python to fall under a new license owned by the Python Software Foundation. At the time of this writing, Python 2.5.1 is the stable production version of the software. 42 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Python fills the role of a scripting language often, from the Web to databases and even to games. Though it may fill this role, Python is more of a compiled language, like Java, where the source code is compiled into a bytecode format that an interpreter can then read. This makes Python very portable, as the bytecode is operating systemindependent. What makes it such a good scripting language is its clean and simple language structure, seen here: # Hello world in Python print "Hello world." Because of its interpreted nature, certain Python applications can be slower than true compiled languages. This does not deter it from excelling as the backend of an Ajax web application, however. Ruby The first version of Ruby, created by Yukihiro “Matz” Matsumoto, was released to the public in 1995. It was created as a language that reduces the grunt work that programmers often must do in application development. Ruby’s syntax is somewhat similar in nature to Python’s, or perhaps Perl’s, as shown in the following code snippet. As an interpreted language, Ruby is slower in execution speed than the compiled languages and some of the interpreted languages. # Hello world in Ruby puts "Hello world." What makes Ruby unique is the way it treats its data. Every single piece of data in Ruby is treated as an object; even what other languages would consider primitive types (integers, Booleans, etc.). Functions in Ruby are methods of some object. Even methods outside the scope of an object are considered methods of the object main. Ruby in itself is not an ideal scripting language for use with Ajax, but when it is the base of a framework such as Ruby on Rails (more on this later in this chapter), it can be a developer’s dream. With Rails, developers require less code to get tasks done, and it has almost built-in support for Ajax calls. This makes it a great fit for building Ajax web applications. Java The Java programming language was released in 1996 at Sun Microsystems. Like ASP.NET and Python, Java is not a true compiled language. Instead, it is a language that is compiled into bytecode and then interpreted. Java looks heavily like C and C++, and it takes a lot of their models and structures. The big difference between these languages is that Java does not have the idea of pointers. Java has seen many versions and changes since its initial release. The current version of Java is Java SE 6, which was released in fall 2006. Server-Side Scripting www.it-ebooks.info | 43 Because of Java’s use of bytecode, developers have created Java Virtual Machines (JVMs) that run on basically every major operating system. Instead of the Java language itself, what interests a web developer is JSP and servlets. Here we see an example of JSP: <%@ page language='java' %> <%="Hello world." %> This is an example of a Java servlet: // Hello world in a Java servlet import java.io.*; import javax.servlet.*; import java.servlet.http.*; public class HelloWorld extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter output = response.getWriter( ); output.println("Hello world."); output.close( ); } } Both are designed to create dynamic responses to a client request. JSP functions just like classic ASP did—scripting commands are embedded within the XHTML markup for the page. Servlets, as you read earlier in the chapter, are the interface that the client makes requests to, and these interfaces are written in Java. Both of these options for using Java execute quickly and provide a good server base for an Ajax web application. Databases Databases allow web applications to store information in a systematic way, and retrieve that information later through the use of a structured query. Before database use became popular on the Web, plain text files were used to store this information. This was slower, not because of read and write access to the files, but because it was difficult to query information contained in the files in a timely manner. Besides being faster for querying, databases also allow many clients to access and save information concurrently. This is very important in the case of web applications, as there is always the potential for hundreds of people to be accessing the application at any one time. Databases are becoming more sophisticated over time, and they are now meeting the demands of the Internet like never before. As they begin to natively support XML, they will increase the speed of Ajax web applications even more than they do today. This is good news, because these web applications are not going to go away, and data storage needs will become greater and greater. 44 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Oracle Oracle has been around for a long time. In 1979, Relational Software, Inc. (RSI) introduced a relational database system called Oracle V2. The product has changed a lot since then, having been rewritten in C and having added a host of enhancements, including transactions, stored procedures and triggers, support for PL/SQL, a native JVM, XML capabilities, cluster support, and grid computing. The current version of Oracle is 10gR2. Oracle (http://www.oracle.com/) is known for its stability and reliability under a heavy workload, and it is deployed often in data warehousing environments because of this. In 1999, Oracle became more Internet-ready, with Oracle 8i, and has since added more enhancements to meet the Internet’s increasing use as a platform. Oracle also is very scalable, having multiple editions to support a wide range of requirements. The major issue with using Oracle on the Web is its inherently high price, with Oracle’s Enterprise Edition costing in the tens of thousands of dollars per processor. This is a deterrent for companies looking for cheaper solutions to their databasedriven Internet applications. Despite the high costs, though, Oracle leads the commercial database market. Microsoft SQL Server The original version of Microsoft SQL Server (http://www.microsoft.com/sql/) was a product of collaboration among Microsoft, Sybase, and Ashton-Tate. They set out to create a database product for the OS/2 operating system, and released SQL Server 1.0 around 1989. It was not until Microsoft SQL Server 6.0 that Microsoft built a product without direction from Sybase. The current version is Microsoft SQL Server 2005. Microsoft SQL Server supports all of the features of relational databases, and adds additional support through its version of SQL called Transact-SQL (T-SQL). Like Oracle, Microsoft SQL Server is scalable, with different editions of the database for different needs. The major limitation to Microsoft SQL Server is that it runs only on Windows, which limits its penetration into the database market. IBM DB2 IBM DB2 (http://www.ibm.com/db2/) was most likely the first database to use SQL. Named System Relational (System R) when it was released in 1978, IBM DB2 probably goes back to the early 1970s, when IBM was working on a relational model it called SEQUEL (Structured English Query Language). The term SEQUEL was already trademarked, so IBM was forced to rename the database, this time to SQL (Structured Query Language). The name has been the same since. Databases | www.it-ebooks.info 45 For years, IBM DB2 was available only on IBM’s mainframes, but throughout the 1990s, IBM slowly began to port the database to other platforms, and now you can find it on many operating systems. Pricing for IBM DB2 is comparable to that for Microsoft SQL Server, costing only in the thousands of dollars per processor. The current version of the database is IBM DB version 9, and it is the first relational database to natively store XML, according to IBM. This support adds to IBM DB2’s ability to handle requests from Ajax web applications. Open Source Databases: MySQL and PostgreSQL Free software implementations of cross-platform relational databases began to spring up in the mid-1990s and have begun to threaten the dominance of larger proprietary giants such as Oracle, IBM, and Microsoft, especially for web applications. The two most popular of these are MySQL (http://www.mysql.com/) and PostgreSQL (http:// www.postgresql.org/). Both are freely available to download and use. The popularity of these databases has forced other companies to make free versions of their software available. Among them are Oracle 10g Express Edition, IBM DB2 Express-C, and Microsoft SQL Server Express Edition (formerly MSDE). MySQL AB released MySQL in 1995; PostgreSQL has an older history, having been released to the public in 1989. At the time of this writing, the current versions of these open source databases are MySQL 5.0 and PostgreSQL 8.2. Both support transactions, stored procedures and triggers, views, and a host of other features. Some features unique to MySQL are its use of multiple storage engines, commit grouping, and unsigned INTEGER values. MySQL supports MyISAM, InnoDB, BDB, and other storage engines, which allows developers to choose whichever engine is most effective for the application’s needs. With commit grouping, MySQL gathers transactions from concurrent connections to the database and processes them together, thereby increasing the number of commits per second. By permitting INTEGER type values to be unsigned, MySQL allows for its different database types to have a greater range of values per type, which can save on database size, depending on the implementation. PostgreSQL has support for XML and Extensible Stylesheet Language Transformation (XSLT) via an add-on called XPath Extensions, which has a GPL license. MySQL will add support for XML functions with the release of MySQL 5.1, which at the time of this writing is still in beta. The XML support enables these databases to work well with the growing demands of Ajax web applications. 46 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Nonrelational Database Models There are other types of database models besides relational databases. They include: • Flat file • Hierarchical • Dimensional • Object • Network Flat file databases are simply plain-text files that contain records (generally one record per line), which separate fields with a fixed width, whitespace, or some special character. There are no structural relationships in the flat file data model, and a flat file database consists of a separate file for every table of data. Implementations of this model include comma-separated value (CSV) files, dBASE, and Microsoft Excel, among others. These don’t tend to work very well for anything more than the simplest of web applications, though they can be useful as an export format when users want to extract data from your application. Hierarchical databases use a tree-like structure of one-to-many relationships to organize data. Information is repeated using parent-child relationships in which each parent may have many children, but each child will have, at most, one parent. A “table” will contain the lists of all attributes for a specific record, where the attributes can be thought of as “columns.” Examples of some hierarchical databases are Adabas, MUMPS, Caché, Metakit, and Berkeley DB. Many “native XML” databases also have hierarchical foundations. Dimensional databases store key data entities as different dimensions instead of in multiple 2D tables (the relational databases we are used to). These databases really just offer an extension to relational databases by providing a multidimensional view of the data. You can implement dimensional databases in multidimensional databases or in relational databases that use a star or snowflake schema. Multidimensional schemas for use in relational databases are an interesting topic, but they are outside the scope of this book. The star schema is more popular than the snowflake schema, but you can find good information on both. Principles and Implementations of Datawarehousing by Rajiv Parida (Laxmi Publications) and The Art of SQL by Stéphane Faroult and Peter Robson (O’Reilly) are good places to start for information on database schemas. Other resources include Advanced Topics in Database Research by Keng Siau (Ed.) (Idea Group Publishing) and Oracle Essentials: Oracle Database 10g, Third Edition, by Rick Greenwald et al. (O’Reilly). Databases | www.it-ebooks.info 47 Object databases represent information in the form of objects, essentially in the same way as objects are used in object-oriented programming. When the data set is complex and high performance is essential, this type of database could be the right choice. You’ll most often find them applied in areas such as engineering, molecular biology, and spatial applications. Languages such as C++, C#, and Java have created a resurgence in object databases because of their object-oriented nature. Implementations of object databases are Perst and db4o (db4objects). Network databases create a lattice structure whereby each record in the database can have multiple parents and multiple children. This model was introduced in 1969 and grew until the early 1980s, with the publication of an ISO specification that had no effect in the industry. Network databases were eventually pushed aside by the growth of relational databases, and now they rarely exist. Getting Data Into and Out of Relational Databases Ajax is about programming on the client and on the server, as I have already discussed. Though this book focuses primarily on the client end of an Ajax application, it still includes some server-side scripting examples. Part of that is interfacing with the database. For good or for bad, as an Ajax developer you must understand at least the basics of database development, unless you are lucky enough to have a database administrator on the project that can do this stuff for you. Even then, it is a good idea to understand how databases can work for you. Because most web applications are built using relational databases, this section focuses on working with that common model. There isn’t room in this book to provide a full tutorial, but if you haven’t worked with relational databases before, this section should at least give you some idea of what they do and how they might store data for your applications. The first thing a developer needs to learn when developing a database is how to create tables. More than that, a developer must learn how to build tables efficiently and in a relational manner. For the following examples, let’s assume that we have been tasked with developing a database based on tabular data that had been kept in a spreadsheet containing a list of books in a personal collection. The spreadsheet includes the following columns: • Title of the Book • Author(s) of the Book • Publishing Date 48 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info • Publisher • ISBN-10 • ISBN-13 • Number of Pages • Original Price of Book • Type of Book • Category of Book • Bought New/Used or Gift That should be enough to get us started. Obviously, if this were a real-world application, we would have a much more comprehensive list of columns to work from. I have always found it easiest to look at a data set and determine what can be separated into look-up and cross-reference tables before tackling the main tables—you may find a different method easier. Looking at the columns in the spreadsheet, it immediately becomes clear to me that I can create several columns as look-up tables, mainly the Type of Book, Category of Book, and Bought New/Used or Gift columns. Let’s look at how we can create these tables in a MySQL database. Look-up tables are useful tables that store records that are common and will be used often, defining an ID for each unique record that the main tables will use instead of the record itself. This can greatly conserve disk space and speed up the execution of SQL queries on tables. Here is the basic SQL syntax to create a new table: CREATE TABLE table_name ( column_name-1 datatype column_name-2 datatype ... ); [modifiers], [modifiers], We will make the Type of Book column into a table called book_type, with ID and description fields using the following SQL query: CREATE TABLE book_type ( type_id TINYINT NOT NULL PRIMARY KEY, type_dsc VARCHAR(15) NOT NULL, UNIQUE KEY _types_key_1 (type_dsc) ); This query uses the CREATE TABLE SQL syntax, which will vary from database to database, making it important to review the documentation for whatever database you are working on. We will create the other look-up tables in much the same way. Getting Data Into and Out of Relational Databases | www.it-ebooks.info 49 We will make the Category of Book column into a table called book_category, with ID and description fields, and the Bought New/Used or Gift column into a table called book_acquired, with ID and description fields, using the following SQL query: CREATE TABLE book_category ( cat_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, cat_dsc VARCHAR(40) NOT NULL, UNIQUE KEY _cat_key_1 (cat_dsc) ); CREATE TABLE book_acquired ( acq_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, acq_dsc VARCHAR(20) NOT NULL, UNIQUE KEY _acq_key_1 (acq_dsc) ); Looking further at our original spreadsheet, we could separate a couple of other columns into their own tables. These are not really look-up tables, which is why I did not create them with the look-up tables in the preceding code. The first is a table that can hold all of the unique publishers that exist. This could technically be considered a look-up table, but considering how large this table could get, it must not be viewed as such. We will create it in the same way as the look-up tables, however, calling the table book_publishers, with ID and description fields. The difference will be in the data type used for the ID in this table. Instead of a TINYINT, we will use a MEDIUMINT: CREATE TABLE book_publishers ( pub_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, pub_dsc VARCHAR(60) NOT NULL, UNIQUE KEY _pub_key_1 (pub_dsc) ); The last column we will separate out is the Author(s) of the Book column. This table, which we will call book_authors, will actually require another table to tie the data to our main table. This other table will be a cross-reference table, and we need it for books that have more than one author; we’ll call it book_author_title_xref. The book_authors table will contain ID and name fields, and the book_author_title_xref table will contain ID, title ID, and author ID fields: CREATE TABLE book_authors ( auth_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, auth_nm VARCHAR(60) NOT NULL, UNIQUE KEY _auth_key_1 (auth_nm) ); CREATE TABLE book_author_title_xref ( title_id BIGINT NOT NULL REFERENCES book_titles (title_id), auth_id MEDIUMINT NOT NULL REFERENCES book_authors (auth_id), UNIQUE KEY _auth_title_key_1 (title_id, auth_id) ); All that is left now is to create a table with the remaining columns that we will call book_titles: 50 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info CREATE TABLE book_titles ( title_id BIG_INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, title_dsc VARCHAR(100) NOT NULL, pub_dte VARCHAR(20) NULL, pub_id MEDIUMINT NOT NULL REFERENCES book_publishers (pub_id), isbn_10 VARCHAR(13) NOT NULL, isbn_13 VARCHAR(18) NOT NULL, num_pages SMALLINT NULL, orig_price FLOAT(2) NULL, type_id TINYINT NOT NULL REFERENCES book_type (type_id), cat_id TINYINT NOT NULL REFERENCES book_category (cat_id), acq_id TINYINT NOT NULL REFERENCES book_acquired (acq_id), UNIQUE KEY _title_key_1 (isdn_10, isdb_13), KEY _title_key_2 (title_dsc, pub_id, pub_dte) ); The hard part is done—creating a database that has good indexing and relational tables yet conserves space wherever possible is a tall order, and should really be considered an art. A database expert could do better, and for a larger project I recommend seeking design assistance, but for our purposes this will suffice. Now, we need to consider how to get functionality out of our database with just the basic functions of Create, Read, Update, and Delete (CRUD). You can create new records in tables with the INSERT statement, read them using the SELECT statement (become friends with this statement, as you will use it most often), update them using the UPDATE statement, and delete them using the DELETE statement. These four commands will accomplish everything necessary in an application. The first thing we need to do with our new database is put some records in our tables, especially the look-up tables. To accomplish this, we will use the INSERT SQL statement, which has a basic syntax of: INSERT INTO table_name (column_name-1, column_name-2, ..., column_name-n) VALUES (value-1, value-2, ..., value-n); To insert records into our database, we will execute the following SQL statements: INSERT INTO book_type (type_dsc) VALUES ('Hard Cover'); INSERT INTO book_type (type_dsc) VALUES ('Paperback'); INSERT INTO book_category (cat_dsc) VALUES ('Computer'); INSERT INTO book_category (cat_dsc) VALUES ('Fiction'); INSERT INTO book_category (cat_dsc) VALUES ('Nonfiction'); INSERT INTO book_acquired (acq_dsc) VALUES ('Bought New'); INSERT INTO book_acquired (acq_dsc) VALUES ('Bought Used'); INSERT INTO book_acquired (acq_dsc) VALUES ('Given As Gift'); Let’s assume the book_title, book_publishers, and book_authors (and book_author_ title_xref) tables have been populated with the following data. Getting Data Into and Out of Relational Databases | www.it-ebooks.info 51 book_title 1 Head Rush Ajax March 2006 1 0-596-10225-9 978-0-59-610225-8 446 39.99 2 3 1 2 The Historian June 2005 2 0-316-01177-0 978-0-316-01177-8 656 25.95 1 1 1 3 3 Nights in August April 2005 3 0-618-40544-5 978-0-618-40544-2 256 25.00 1 2 1 4 Ajax Design Patterns June 2006 1 0-596-10180-5 978-0-59-610180-0 655 44.99 2 3 1 5 CSS: The Definitive Guide November 2006 1 0-596-52733-0 978-0-59-652733-4 536 44.99 2 3 1 6 The Iliad November 1998 4 0-14-027536-3 978-0-14-027536-0 704 15.95 3 2 2 7 Chicka Chicka Boom Boom August 2000 5 0-689-83568-X 978-0-689-83568-1 32 7.99 3 4 3 book_publishers 1 O’Reilly Media 2 Little, Brown and Company 3 Houghton Mifflin 4 Penguin Classics 5 Aladdin Picture Books book_authors book_author_title_xref 1 Brett McLaughlin 1 1 2 Elizabeth Kostova 2 2 3 Buzz Bissinger 3 3 4 Michael Mahemoff 4 4 5 Eric Meyer 5 5 6 Homer 6 6 7 Bill Martin, Jr. 7 7 8 John Archambault 8 8 Note especially the columns with numbers in them. These act as keys, or ways that one table can reference data in another. As we query the database to extract data, the queries will use these keys to create joins across multiple tables. To get records from the database, we execute SELECT statements that have this basic syntax: SELECT columns FROM tables WHERE predicates 52 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info To get a list of books published by O’Reilly, we execute the following SELECT statement: SELECT t.title_dsc, p.pub_dsc, t.isbn_10 FROM book_titles t INNER JOIN book_publishers p ON t.pub_id = p.pub_id WHERE p.pub_dsc = 'O\'Reilly Media'; It takes practice to learn all of the nuances of how to most efficiently pull data from tables, and here is where a database administrator can effectively come to the aid of a developer. There are many things to consider when writing a SELECT statement. You should refer to books specific to the database you are using for more information on this. Deleting records from a table is straightforward using the following syntax: DELETE FROM table_name WHERE predicates; To, say, remove records from the book_category table you would execute the following DELETE statements: DELETE FROM book_category WHERE cat_dsc = 'Science Fiction'; Sometimes records simply need to be updated, and you can use the following syntax for such cases: UPDATE table_name SET column = expression WHERE predicates; To update records in the book_category table you would execute the following UPDATE statements: UPDATE book_category SET cat_dsc = 'Science Fiction & Fantasy' WHERE cat_dsc = 'Science Fiction'; These are the basics of tables and queries in a relational database, and they will get a developer through most of what he will encounter when programming an Ajax application. As applications become more complex, their scope increases in size or the number of users increases; then the developer must take other measures to improve database performance and execution. Getting Data Into and Out of Relational Databases | www.it-ebooks.info 53 For a more thorough introduction to SQL, and MySQL in particular, check out MySQL in a Nutshell by Russell Dyer (O’Reilly). Having SELECT statements (or INSERT, UPDATE, and DELETE, for that matter) inline in your code is fine when the code isn’t used frequently in an application. For scripts that are static with the exception of a few parameters, you will probably see performance gains if you switch these inline SQL statements to stored procedures. Stored procedures have the benefit of being compiled by the database and stored in it, making the execution plans for the script already resident to the database. The database already knows how to execute the script, making the execution that much quicker. The other advantage to using stored procedures instead of inline statements is that all of the data logic can be in one place in the application. This not only facilitates application maintenance, but also allows code reuse in places that need the same SQL statement on different pages. For much more on stored procedures, see MySQL Stored Procedure Programming by Guy Harrison and Steven Feuerstein (O’Reilly). You can learn much more about SQL and databases if you want, but this introduction should help you understand some of what writing an Ajax application encompasses. Interfacing the Interface Covering all of the tools available on the backend of an Ajax application is one thing, but showing how they interact with a client is another. Server-side scripting has changed, not so much in how the developer codes with the language, but in what the client needs or expects to get back from the server. Take, for instance, Example 3-1, which shows the typical server response from a client submitting a form. Example 3-1. A typical server response to a form submit Example 3-1. A typical server response to a form submit.

Query Results

Book Name
There were no results for the specified query.
First, note how this example uses PHP as the server-side scripting language and MySQL as the database. This book’s examples will generally follow this design, not because I believe these are better than the other languages and databases I’ve outlined, but simply because I find them easy to use, especially for demonstration purposes. In this example, the server processes the data posted to it and then creates a response in the form of a full HTML document. What makes this bad in an Ajax application sense is that the browser must load all of the content for the page again. If images, CSS, and JavaScript were included in this file, they would all have to be downloaded again as well. This is why the classic style of building web pages is not ideal for application building. Compare this with Example 3-2, which shows how a typical Ajax response would be generated. Example 3-2. A typical Ajax response to a form submit PROLOG; /* Is there a connection to the database server? */ if (!($conn = @mysql_connect($host, $username, $password))) $xml .= 'Could not connect to the database.'; $author = mysql_real_escape_string(isset($_POST['authorName']) ? $_POST['authorName'] : ''); /* Could the database be selected? */ if (!@mysql_select_db($db, $conn)) $xml .= 'Could not select database.'; $sql = 'SELECT book_id, book_nm FROM books, authors WHERE books.author_id ' .'= authors.author_id'; /* Was the parameter /authorName/ passed to this script? */ if (isset($author)) $sql .= " AND authors.author_nm = '$author'"; $sql .= ' ORDER BY book_nm'; /* Are there results from the query? */ if ($result = @mysql_query($sql, $conn)) { $xml .= ''; /* Loop through the records */ while ($row = @mysql_fetch_assoc($result)) $xml .= "{$row['book_nm']}"; /* Were there any records to loop through? */ if (!@mysql_num_rows($result)) $xml .= 'There were no results for the specified query.'; $xml .= ''; /* Free the mysql result */ mysql_free_result($result); mysql_close($result); } else $xml .= '' .'There were no results for the specified query.' .''; /* * Change the header to text/xml so that the client can use the return * string as XML */ header("Content-Type: text/xml"); echo $xml; ?> 56 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Notice that in this example, the only thing returned with the response is an XML document with the data necessary to be shown on the page, sent in the form of XML. The client will parse this response as needed so that it will appear as though the application just changed content without having to refresh everything. The server will also not kill the page with the die( ) function, leaving the client to decide what to do with an error. This is how server-side applications need to react. Each client request should expect only a minimal amount of data sent back to it. This forces the browser to download less data per request, and speed up the application as a whole. We will see in Chapter 4 how the client makes its requests and manipulates responses, and Chapter 5 will go into more detail on client-side data parsing. For now, we should content ourselves with understanding what is expected of the server side of an Ajax web application, and find ways to increase this performance. This side of the application does all the “dirty work,” and the quicker and more efficiently it does this, the better our Ajax web applications will perform. Frameworks and Languages Frameworks have been getting a lot of press lately, as those such as Ruby on Rails have gained the notice of more and more professionals in the industry. The truth is, however, that frameworks have been around for a while—longer with some languages than others. But what exactly is a framework? In the simplest terms, a framework is a set of components (interfaces and objects) that are put together to solve a particular problem. Frameworks are built to ease the burden of writing all of the low-level coding details that go along with programming an application. An important feature of frameworks is that they should work on a generic level so that they are suited for a multitude of applications. On the Web and the desktop, frameworks allow developers to concentrate on the application’s requirements and on meeting deadlines, instead of on the mundane but necessary components that make applications run. With our focus on Ajax web development, it is important to understand the differences among the various frameworks on the Web, not just within a given language, but among languages as well. Earlier in the chapter, we focused on ASP/ASP.NET, PHP, Python, Ruby, and Java, so the frameworks we discuss here will correspond with these languages. Some of these frameworks follow the Model-View-Controller (MVC) design pattern discussed in Chapter 2, and others are just a whole lot of functionality bundled together. Your choice of framework will depend on how structured you want to be. Frameworks and Languages | www.it-ebooks.info 57 The .NET Framework The Microsoft .NET Framework (http://msdn.microsoft.com/netframework/) is positioned to be the development platform for all new Windows applications, on the Web as well as the desktop. Because of this strategy, it is built as part of the Windows operating system and not as a separate component, as all other frameworks are. And although Microsoft was specifically looking at its flagship Windows operating systems when it designed the .NET Framework, it built the framework to theoretically be a portable language. As we discussed in the section “ASP/ASP.NET,” earlier in this chapter, instead of .NET languages being compiled into machine-level instructions, they are first compiled into a common bytecode and then into a DLL. That is a high-level description of the architecture, but we should delve into it further, and Figure 3-2 does just that. Common Language Infrastructure C# code Common Intermediate Language (CIL) VB.NET code Common Language Runtime (CLR) Compiles Common Language Specified (CLS) Common Type System (CTS) Framework Class Library (FCL) 01000001 01010011 01010000 00101110 01001110 01000101 01010100 Figure 3-2. The .NET Framework architecture When a .NET project is built, each specific .NET language has its own compiler that can interpret the language syntax. These compilers rely on a Common Language Specification (CLS) to govern the rules the languages must live by. They also rely on the Common Type System (CTS), which defines operations and types that the .NET languages share. Finally, the .NET language compilers utilize the Framework Class Library (FCL), a set of more than 600 classes that encapsulates everything from file manipulation to graphics rendering to database interaction. Taking all of these layers of the .NET Framework together, the compilers then compile the code into the bytecode that is called the Common Intermediate Language (CIL). 58 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info This CIL is what programmers generally referred to in .NET as assemblies. When the web server requests an assembly, the CLR is invoked. Within the CLR is where components such as the Just-In-Time (JIT) compiler, garbage collector, and security run. The CLR is the platform-specific part of the .NET Framework, and it compiles the CIL into the operating system’s machine code. The CIL and CLR together are referred to as the Common Language Infrastructure (CLI). The .NET Framework is good for its large library of built-in classes that cover most of what you would need when building an Ajax web application. Plus, developers have their choice of languages to use for programming, allowing different people to be comfortable with their code and generally more productive. On the downside, because of their CLR, .NET applications tend to require more system resources than similar applications that directly access system resources. Also, the FCL has a rather large learning curve. All in all, the .NET Framework is not a bad environment to work in once you know the classes that Microsoft has provided for you. When you throw in Microsoft Visual Studio for development, programming times are reduced thanks to the GUI for designing and building individual pages in an application that it provides. The large available class library and the GUI for designing site pages allow more rapid deployment of Ajax web applications than traditional coding. Ruby on Rails Ruby on Rails (RoR or just Rails), which David Heinemeier Hansson developed while he was working on Basecamp (http://www.basecamphq.com/), is a web-based project collaboration tool. It is an open source framework that is based on the MVC pattern, and you can find it at http://www.rubyonrails.org/. It is considered to be a full-stack framework, meaning that all the components in the framework are integrated, so you don’t have to set anything up manually. Ruby on Rails’ marketing claims that a web programmer can develop 10 times faster than a programmer working from scratch without Rails. How can this be possible? Easily, if the libraries the framework provides are easy to use and are written so that they integrate well with one another. This is just what Rails does, and these libraries are set up to work within the MVC pattern. Read the articles and blogs on Ruby on Rails, and almost all of them will talk about the ActiveRecord library. ActiveRecord makes communicating with a database just plain easy, something anyone trying to build a database-driven web application wants to hear. ActiveRecord acts as the model of the MVC pattern. Rails also has the Action Pack, which consists of two libraries: ActionController and ActionView. ActionController takes care of the pattern’s controller needs, and ActionView handles the view. Frameworks and Languages | www.it-ebooks.info 59 Ruby on Rails allows a web developer to focus on what he needs to: the application’s functionality. All of the details of database queries, hashing, caching, forms, tags, and even Ajax itself are taken care of, leaving you free to program that functionality your boss has been hoping for. Hurting Rails right now is its lack of examples (due to its fledgling nature), incomplete or limited documentation, and lack of support from web hosts and third-party software. For anyone willing to jump right into development with both feet, though, Rails is the framework of choice. I can’t say it enough; Ajax web development with Ruby on Rails is just plain easy. Java Frameworks Some frameworks in Java have been around longer than the frameworks in other languages, though even Java has its youngsters. These frameworks are usually designed for the Java J2EE platform, though frameworks for other platforms also exist. The common ground for these frameworks is that almost all of them follow the MVC design pattern. They all use different techniques to get the job done, but the overall data flow within these applications remains basically the same. Too many Java frameworks are available today to review them all. I’ve chosen to highlight Jakarta Struts, Spring, and Tapestry. And before you complain too much about my choices, you should be aware that I am not a Java programmer, nor will I ever claim to be, so I am not playing favorites here. Jakarta Struts Jakarta Struts (http://struts.apache.org/), or just Struts, was created by Craig McClanahan and donated to the Apache Software Foundation (ASF) in 2000. It was designed to model the MVC design pattern, through the extension of Java servlets. Struts was designed for applications to be built by people with different skill sets. The view of a Struts framework can be any number of XML, JSP, and JavaServer Faces (JSF), whereas the model supports both JavaBeans™ and Enterprise JavaBeans (EJB). Struts has a tag library that holds a large set of functionality, as well as built-in form validation. Plus, it is well documented (check out Chuck Cavaness’s Programming Jakarta Struts from O’Reilly), and its popularity has led to it having a mature code base. But it is starting to see new challenges, not just in other languages, but with lighter-weight MVC frameworks built with Java as well. Spring Rod Johnson wrote the Spring framework, which you can find at http://www. springframework.org/, and released it to the public in 2002, with version 1.0 being released in March 2004. When it was first being designed, its developers were not thinking of the MVC design pattern. They were instead trying to develop a framework 60 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info in response to what they felt was poor design on the part of Jakarta Struts. In the end, though, Spring did wind up with an MVC architecture. Spring is quickly growing out of its reputation as a “lightweight” framework, but not because it is getting bloated with code. It now merely has so much functionality that it is hard to think of it as anything other than a robust framework. Spring has gained popularity because it integrates so well with other things besides the Java Enterprise platform. What may hurt Spring the most is that as a framework, it has almost become too flexible, and it does not have a central controller. Tapestry Tapestry (http://tapestry.apache.org/) is an MVC-patterned framework built on the Java Servlet API that Howard M. Lewis Ship created. It was designed to allow for easy component building and the approach of dividing web applications into individual pages created on these components. Tapestry’s core philosophy is “the simplest choice should be the correct choice.” This is driven by four key principles: simplicity, consistency, efficiency, and feedback. Tapestry is a young framework, but it has the philosophy and MVC design that are driving many Ajax web applications. It is only a matter of time before it becomes a more mature framework and sees the popularity that other Java frameworks have enjoyed. Python Frameworks Just like all of the other server-side scripting languages out there today, Python has its share of frameworks. And like all languages, these frameworks differ in how they are designed. Some follow the MVC design pattern strictly, some follow it loosely, and some do not follow it at all. Django Django (http://www.djangoproject.com/) is a loosely based MVC framework developed by Adrian Holovaty, Simon Willison, Jacob Kaplan-Moss, and Wilson Miner. Django was designed for heavily content-driven web applications, such as news sites, blogs, and forums. Because of this, Django is very good at database communication, specifically CRUD. It also has an excellent built-in administrator interface. When I say loosely based MVC I am echoing what Django’s developers stated: that they “feel like the design of Django has to feel right, and [they] will not be bound to a particular design pattern.” As a result, the controller in a typical MVC framework is the “view” in Django, and the view is instead called the “template.” Even though Django is not a true MVC framework, it still functions very well with Ajax web applications that require rapid creation and robust database controls. Frameworks and Languages | www.it-ebooks.info 61 Zope Zope, which stands for “Z Object Publishing Environment,” is well known as the driving force behind the most popular open source Content Management System (CMS) available on the Web: Plone (http://plone.org/). Created and owned by the Zope Corporation, Zope (found at http://www.zope.org/) is nonetheless an open source product, and is the collaboration of many different people across the Internet. Zope has two stable branches released to the public: Zope 2 and Zope 3. Zope 2 is the code base that most programmers are familiar with, as it is behind many open source CMSs and ERP5 (http://www.erp5.com/), an open source Enterprise Resource Planning (ERP) package. The problem with Zope 2 is that a lot of “magic” code must go along with every distribution. Zope 2 also does a poor job of separating business logic from the presentation layer. Zope 3 is a rewrite of Zope that attempts to fix the problems that exist in Zope 2 while keeping true to the roots that make Zope popular. It is taking a different approach, though, mixing components of various origins to create a faster, stronger, and more reliable Ajax web development framework. PHP Frameworks Being one of the most popular server-side scripting languages on the Web, PHP has a large number of frameworks to choose from. Some of these frameworks are modeled after a generic MVC design pattern, some are modeled after frameworks in different languages, and some have their own unique structure suited for more specific needs. Whatever the design pattern is, PHP frameworks take the already simple-to-use PHP language and make it even easier and faster to develop web applications. CakePHP CakePHP was created in 2005 at a time when Ruby on Rails was seeing a huge boost in popularity. It has seen heavy development since then and is now a robust MVC framework with an active developer community. Ever since CakePHP was released as stable with version 1.1.15.5144 on May 21, 2007, it has shown that it has the capabilities to compete with all of the other frameworks out there. CakePHP, which you can find at http://www.cakephp.org/, has a solid foundation, with modules built on top that add all of the functionality a developer looks for when building an application. It handles database interactions, provides all the Ajax support you need, and includes built-in validation as well as security, session, and request handling. With documentation that is thorough and easy to follow, CakePHP is easy to use and ideal for Ajax web applications. 62 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info Zoop Zoop, which stands for “Zoop Object Oriented PHP,” is a framework comprising many different components and using other projects for added functionality. Zoop has been in development and production since 2001 and has been used in many production environments. Zoop takes advantage of other projects, such as Smarty (http://smarty.php.net/) and PEAR modules (http://pear.php.net/), showcasing its ability to be extensible and versatile. Zoop’s truly unique feature is its GUI controls, something rarely seen in PHP, which give the developer easy access to widgets and a framework in which to build new controls. Zoop is designed with the developer in mind, making application building simple and efficient through the tools that it provides. Zend The Zend Framework (http://framework.zend.com/) is newer than most, but provides some excellent functionality with the components already created. Unlike other frameworks, Zend is built on the true spirit of PHP: delivering easy-to-use and powerful functionality. It does this not through a true design pattern, but rather through the use of separate components for different functionalities. That is not to say it doesn’t follow MVC patterns. Zend does have components for building MVC applications: Zend_View and Zend_Controller. Currently, though, the developer must implement a “model” for the framework. And though it still lacks some functionality, it already contains many useful components, including Database, JavaScript Object Notation (JSON), Logging, Mail, PDF, RSS and Atom feeds, and web services (Amazon, Flickr, and Yahoo!). This framework looks very promising as it continues to grow toward a stable release. When this happens, it may be the framework of choice for building Ajax web applications. What Good Are Frameworks? The title of this section speaks for itself. I have described some of the frameworks that are available for different scripting languages, but just what good are they? Are they more than just a popular buzzword that has been floating around? The answer, in a word, is yes! Frameworks are designed to solve recurring problems in application development. So, instead of just trying to explain their usefulness, I will show you. What Good Are Frameworks? www.it-ebooks.info | 63 One of the problems developers face with any web application is providing dynamic data to the client. This is solved by the interaction of the server-scripting language with a database of some kind. Let’s take another look at Example 3-2: PROLOG; /* Is there a connection to the database server? */ if (!($conn = @mysql_connect($host, $username, $password))) $xml .= 'Could not connect to the database.'; $author = mysql_real_escape_string(isset($_POST['authorName']) ? $_POST['authorName'] : ''); /* Could the database be selected? */ if (!@mysql_select_db($db, $conn)) $xml .= 'Could not select database.'; $sql = 'SELECT book_id, book_nm FROM books, authors WHERE books.author_id ' .'= authors.author_id'; /* Was the parameter /authorName/ passed to this script? */ if (isset($author)) $sql .= " AND authors.author_nm = '$author'"; $sql .= ' ORDER BY book_nm'; /* Are there results from the query? */ if ($result = @mysql_query($sql, $conn)) { $xml .= ''; /* Loop through the records */ while ($row = @mysql_fetch_assoc($result)) $xml .= "{$row['book_nm']}"; /* Were there any records to loop through? */ if (!@mysql_num_rows($result)) $xml .= 'There were no results for the specified query.'; $xml .= ''; /* Free the mysql result */ mysql_free_result($result); mysql_close($conn); 64 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info } else $xml .= '' .'There were no results for the specified query.' .''; /* * Change the header to text/xml so that the client can use the return * string as XML */ header("Content-Type: text/xml"); echo $xml; ?> This is a common technique for querying a database. Here are the steps involved: 1. Connect to the MySQL server. 2. Choose the database to use. 3. Build and execute the query on the database. 4. Fetch the resulting rows from the database. 5. Loop through the records. 6. Free the results. 7. Close the connection to the MySQL Server. I admit that all of these code checks are probably a little bit over the top. It would be fine to just fall through and have one generic catch at the end to alert the client that an error occurred. After all, the client doesn’t need to know exactly what happened; it is the server’s job to log errors and send only meaningful information back. But here is the question you should think about when looking at the code in Example 3-2: would you code a database interaction that way? Chances are, you wouldn’t. You might not follow the same steps, adding or deleting them as necessary. This is where frameworks give the developer such an advantage. When developers use a framework, they are committing to always coding a specific task or problem in the same way. If there is more than one developer, all of the code will be basically the same. This is a wonderful advantage if someone else ever needs to debug your code. Example 3-3 shows how the Zend Framework could solve this problem. It is a pretty straightforward and simple means to database interaction, which is why I chose this framework for the example. Example 3-3. Database interaction using the Zend Framework PROLOG; /* Get the parameter values from the query string */ $author = mysql_real_escape_string(isset($_POST['authorName']) ? $_POST['authorName'] : ''); /* Set up the parameters to connect to the database */ $params = array ('host' => $host, 'username' => $username, 'password' => $password, 'dbname' => $db); try { /* Connect to the database */ $db = Zend_Db::factory('PDO_MYSQL', $params); /* Create a SQL string */ $sql = sprintf('SELECT book_id, book_nm FROM books, authors ' .'WHERE books.author_id = authors.author_id %s ORDER BY ' .'book_nm', (isset($author)) ? " AND authors.author_nm = '$author'" : ''); /* Get the results of the query */ $result = $db->query($sql); /* Are there results? */ if ($rows = $result->fetchAll( )) { $xml .= ''; foreach($rows in $row) $xml .= "{$row['book_nm']}"; $xml .= ''; } } catch (Exception $e) { $xml .= 'There was an error retrieving the data.'; } /* * Change the header to text/xml so that the client can use the return * string as XML */ header("Content-Type: text/xml"); echo $xml; ?> 66 | Chapter 3: Servers, Databases, and the Web www.it-ebooks.info In this case, the framework saves only a few lines of code; there is no great advantage or disadvantage with that. Let’s take another look at the steps involved with this code: 1. Set up the parameters for the database server. 2. Create an instance of Zend_Db_Adapter. 3. Properly format the query string. 4. Execute the query on the database. 5. Fetch the resulting rows from the database. 6. Loop through the records. The difference between the two lists is not what I want you to focus on. The point here is that these will be the same steps any developer working on the application will take, because the framework has a structure for database interaction. Whatever the task in an application, by using a framework, you ensure consistency and efficiency in tackling that task. This is what frameworks are all about: consistently and effectively providing solutions to problems in a structured manner. Once you have that, building an Ajax web application becomes simple—which is how it ought to be. What Good Are Frameworks? www.it-ebooks.info | 67 Chapter 4 4 CHAPTER Foundations: Scripting XML and JSON 4 It’s time to switch gears and look at code for Ajax web applications. The most important part of an Ajax application is the connection between the client and the server. If this code is not solid and optimized, your application could suffer sluggish (or simply broken) behavior as a result. You code the connection between the client and the server using JavaScript, and usually build the data format used to exchange information in XML. I say usually because a new format is on the rise and is fast becoming the new choice for web developers. This new format is JavaScript Object Notation (JSON). In this chapter, we will explore how to use XML and JSON to transmit data. We will also discuss how the client and the server can parse or otherwise manipulate these formats. Of course, a discussion of this nature would be incomplete without some points on the differences among browser versions, and how to make cross-browsercompatible code. XML We will start with XML, as it is part of the original meaning of Ajax. This section will cover the basics of how Ajax works and what to do with the XML that is sent back and forth between the client and the server. First, driving the Ajax component of an Ajax web application is the XMLHttpRequest object. This object allows for asynchronous communication between the client and the server. In other words, the client can start communicating with the server, and instead of the client freezing up and becoming unusable until that communication is complete, the client can continue to function like normal. 68 www.it-ebooks.info Unfortunately for the developer, how an XMLHttpRequest object is implemented is different from one browser to the next. For Safari, Mozilla, Opera, and other likeminded browsers, you create the object like this: var request = new XMLHttpRequest( ); For browsers that use ActiveX controls, you simply pass the name of the object to the ActiveX control: var request = new ActiveXObject('Microsoft.XMLHTTP'); Once the object has been instantiated, whether you are using the XMLHttpRequest object or the ActiveX version, the object has the same basic methods and properties associated with it, as shown in Tables 4-1 and 4-2. Table 4-1. The XMLHttpRequest object’s properties Property Description onreadystatechange The function assigned to this property, which is an event listener, is called whenever the readyState of the object changes. readyState This property represents the current state that the object is in. It is an integer that takes one of the following: • 0 = uninitialized (The open( ) method of the object has not been called yet.) • 1 = loading (The send( ) method of the object has not been called yet.) • 2 = loaded (The send( ) method has been called, and header and status information is available.) • 3 = interactive (The responseText property of the object holds some partial data.) • 4 = complete (The communication between the client and server is finished.) responseText A version of the returned data in a plain-text format. responseXML A version of the returned data that has been instantiated into a Document Object Model (DOM) Document object. status The response status code that the server returned, such as 200 (OK) or 404 (Not Found). statusText The text message associated with the response status code the server returned. Table 4-2. The XMLHttpRequest object’s methods Property Description abort( ) Cancels the object’s current request. getAllResponseHeaders( ) Returns all of the response headers; headers and values as a formatted string. XML www.it-ebooks.info | 69 Table 4-2. The XMLHttpRequest object’s methods (continued) Property Description getResponseHeader(header) Returns the value of the passed header as a string. open(method, URL[, asynchronous flag[, username[, password]]]) Prepares the request by assigning: method The method the request will use, either GET or POST. URL The destination of the request. asynchronous flag Optional Boolean value determining whether to send the request asynchronously or synchronously. username Optional username to pass to the URL. password Optional password to pass to the URL. send([contents]) Sends the request with the optional contents, either a postable string or a DOM object’s data. setRequestHeader(header, value) Sets the request header with the value, but the open( ) method must be called first. But first things first; before we delve into the properties and methods of the XMLHttpRequest object, we must create the object. Example 4-1 shows a crossbrowser-compatible way to create the XMLHttpRequest object. Example 4-1. Creating the XMLHttpRequest object /* * Example 4-1, Creating the XMLHttpRequest object. */ /** * This function, createXMLHttpRequest, checks to see what objects the * browser supports in order to create the right kind of XMLHttpRequest * type object to return. * * @return Returns an XMLHttpRequest type object or false. * @type Object | Boolean */ function createXMLHttpRequest( ) { var request = false; /* Does this browser support the XMLHttpRequest object? */ if (window.XMLHttpRequest) { if (typeof XMLHttpRequest != 'undefined') /* Try to create a new XMLHttpRequest object */ try { request = new XMLHttpRequest( ); 70 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Example 4-1. Creating the XMLHttpRequest object (continued) } catch (e) { request = false; } /* Does this browser support ActiveX objects? */ } else if (window.ActiveXObject) { /* Try to create a new ActiveX XMLHTTP object */ try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { request = false; } } } return request; } var request = createXMLHttpRequest( ); The createXMLHttpRequest( ) function returns an abstract object that functions out of the user’s view. The request object has the methods and properties listed in Tables 4-1 and 4-2. Once you have your XMLHttpRequest object instantiated, you can start to build requests and trap responses. XML Requests and Responses So, we have our XMLHttpRequest object, and now we need to do something with it. This object will control all of the requests that will be communicated to the server, as well as all of the responses sent back to the client. Two methods and one property are typically used when building a request for the server: open( ), send( ), and onreadystatechange. For example: if (request) { request.open('GET', URL, true); request.onreadystatechange = parseResponse; request.send(''); } This is the bare-bones request that can be made to the server. It is not entirely useful, however, until you pass data to the server for it to act on. We need to build a function that accepts as input an XMLHttpRequest object, a URL to send to, parameters to pass to the server, and a function to fire when the readyState of the object changes, as shown in Example 4-2. XML www.it-ebooks.info | 71 Example 4-2. Creating a request function /* * Example 4-2, Creating a request function. */ /** * This function, requestData, takes the passed /p_request/ object and * sends the passed /p_data/ to the passed /p_URL/. The /p_request/ * object calls the /p_func/ function on /onreadystatechange/. * * @param {Object} p_request The XMLHttpRequest object to use. * @param {String} p_URL The URL to send the data request to. * @param {String} p_data The data that is to be sent to the server through * the request. * @param {Object} p_func The function to call on * /onreadystatechange/. */ function requestData(p_request, p_URL, p_data, p_func) { /* Does the XMLHttpRequest object exist? */ if (p_request) { p_request.open('GET', p_URL, true); p_request.onreadystatechange = p_func; p_request.send(p_data); } } As the developer, it is up to you whether you send your request with a GET method or a POST method, unless you wish to send the server some XML. When this is the case, a POST method is required. So, we would want to modify our function to also receive as a parameter the method of the request. The new declaration line would look like this: function requestData(request, url, data, func, method) { The data that is sent can be in the form of passed parameters, or XML. With both a POST and a GET, the data passed would look like this: param1=data1¶m2=data2¶m3=data3 This same data could be passed as an XML document as: data1 data2 data3 If the data you are passing is simple in nature, I recommend sticking with the passed parameter string instead of the XML. Less data is passed to the server, which could lead to a faster response time. 72 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info When the server receives the request, the corresponding script is executed to generate a response. You should build these scripts so that the least possible amount of data is returned. Remember, the idea behind Ajax and Ajax web applications is speed: speed in requests, speed in responses, and speed in displaying the response to the client. Example 4-3 shows how to program a typical script to create a response for the client. Example 4-3. A typical script for creating a server response PROLOG; /* Set up the parameters to connect to the database */ $params = array ('host' => $host, 'username' => $username, 'password' => $password, 'dbname' => $db); try { /* Connect to the database */ $conn = Zend_Db::factory('PDO_MYSQL', $params); /* Get the parameter values from the query $value1 = $conn->quote(($_GET['param1']) ? $value2 = $conn->quote(($_GET['param2']) ? $value3 = $conn->quote(($_GET['param3']) ? string */ $_GET['param1'] : ''); $_GET['param2'] : ''); $_GET['param3'] : ''); /* * Create a SQL string and use the values that are protected from SQL injections */ $sql = 'SELECT * FROM table1 WHERE condition1 = $value1 AND condition2 = $value2' .' AND condition3 = $value3'; /* Get the results of the query */ XML www.it-ebooks.info | 73 Example 4-3. A typical script for creating a server response (continued) $result = $conn->query($sql); /* Are there results? */ if ($rows = $result->fetchAll( )) { /* Create the response XML string */ $xml .= ''; foreach($rows in $row) { $xml .= ""; $xml .= "{$row['column1']}"; $xml .= "{$row['column2']}"; $xml .= ""; } $xml .= ''; } } catch (Exception $e) { $xml .= 'There was an error retrieving the data.'; } /* * Change the header to text/xml so that the client can use the return string as XML */ header("Content-Type: text/xml"); echo $xml; ?> This script does what most simple scripts do. It gets the passed parameters, inserts those values into the SQL query, formats the response as XML, and outputs the results. How data is sent to the server is up to the developer, and probably depends on the server-side scripting language being used. For PHP, for example, it is relatively easy to parse XML coming from the client, just as it is easy to parse a query string, as shown in Example 4-4. Example 4-4. Dealing with an XML data request param as $param) switch ($param['id']) { case 1: $value1 = $param; break; case 2: $value2 = $param; break; case 3: $value3 = $param; break; } /* Output the XML Prolog so the client can recognize this as XML */ $xml = <<< PROLOG PROLOG; /* Set up the parameters to connect to the database */ $params = array ('host' => $host, 'username' => $username, 'password' => $password, 'dbname' => $db); try { /* Connect to the database */ $conn = Zend_Db::factory('PDO_MYSQL', $params); $value1 = $conn->quote($value1); $value2 = $conn->quote($value2); $value3 = $conn->quote($value3); /* * Create a SQL string and use the values that are protected from SQL injections */ $sql = 'SELECT * FROM table1 WHERE condition1 = $value1 AND condition2 = $value2' .' AND condition3 = $value3'; /* Get the results of the query */ $result = $conn->query($sql); /* Are there results? */ if ($rows = $result->fetchAll( )) { /* Create the response XML string */ $xml .= ''; foreach($rows in $row) { $xml .= ""; $xml .= "{$row['column1']}"; $xml .= "{$row['column2']}"; $xml .= ""; } $xml .= ''; } XML www.it-ebooks.info | 75 Example 4-4. Dealing with an XML data request (continued) } catch (Exception $e) { $xml .= 'There was an error retrieving the data.'; } /* * Change the header to text/xml so that the client can use the return string as XML */ header("Content-Type: text/xml"); echo $xml; ?> The server has created a response, and now the client must gather that response for whatever parsing needs to be done. For handling the server response, you use the XMLHttpRequest object’s readyState, status, responseText or responseXML, and statusText. In Example 4-5, we will build our function that was set with the onreadystatechange property during the request. Example 4-5. Handling the server’s response /* * Example 4-5, Handling the server's response. */ /** * This function, parseResponse, waits until the /readyState/ and /status/ * are in the state needed for parsing (4 and 200 respectively), and uses * the /responseText/ from the request. */ function parseResponse( ) { /* Is the /readyState/ 4? */ if (request.readyState == 4) { /* Is the /status/ 200? */ if (request.status == 200) { /* Grab the /responseText/ from the request (XMLHttpRequest) */ var response = request.responseText; alert(response); // here is where the parsing would begin. } else alert('There was a problem retrieving the data: \n' + request.statusText); request = null; } } In this function, if the readyState isn’t equal to 4 (complete), we’re not interested in proceeding. Likewise, if the status returned isn’t 200 (OK), we need to tell the user there was an error. The responseText property is set with a string version of whatever content the server sent. If the server returns XML, the responseXML property is automatically created as a DOM XML Document object that can be parsed like the rest of the DOM. 76 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info That is all fine and dandy for the server side, but what if you need to send XML to the server as part of your request because the data is not so simple? Often, for example, the data you need to send is not part of a form. In these cases, you POST the XML string to the server. Remember the requestData( ) function? Here is a quick alteration of that function: /** * This function, requestData, takes the passed /p_request/ object and * sends the passed /p_data/ to the passed /p_URL/. The /p_request/ * object calls the /p_func/ function on /onreadystatechange/. * * @param {Object} p_request The XMLHttpRequest object to use. * @param {String} p_URL The URL to send the data request to. * @param {String} p_data The data that is to be sent to the server through * the request. * @param {String} p_func The string name of the function to call on * /onreadystatechange/. * @param {String} p_method The method that the request should use to pass * parameters. */ function requestData(p_request, p_URL, p_data, p_func, p_method) { /* Does the XMLHttpRequest object exist? */ if (p_request) { /* Is the posting method 'GET'? */ if (p_method == 'GET') p_request.open('GET', p_URL + '?' + p_data, true); else p_request.open('POST', p_URL, true) p_request.onreadystatechange = p_func; /* Is the posting method 'GET'? */ if (p_method == 'GET') p_request.send(null); else p_request.send(p_data); } } The data that you pass to this function can be an XML string, but in these cases, the method must be 'POST'. Requests and responses using XML are as simple as that. The most important thing a developer must be aware of is how the data is being returned from the server. Parsing Once you have received a responseText or responseXML, you need to be able to parse that response so that it is useful to the application. Many DOM methods are available in JavaScript, but for now we will concentrate on just a couple of them. Chapter 5 will detail the rest of the methods to complete our discussion of XML manipulation within the DOM. The methods we will focus on now are getElementById( ) and getElementsByTagName( ). XML www.it-ebooks.info | 77 The basic syntax for the getElementById( ) method is: var node = document.getElementById(elementId); Just as basic, the syntax for the getElementsByTagName method is: var nodeList = xmlObject.getElementsByTagName(tagName); Developers most often use the getElementById( ) and getElementsByTagName( ) methods to retrieve elements based on the World Wide Web Consortium (W3C) DOM. Befriend these methods; they make dynamic programming in JavaScript what it is, and every developer of an Ajax web application needs to know exactly what she gets back from each method. By using the XML from this chapter’s earlier “XML Requests and Responses” section as our response from the server: data1 data2 data3 we can access our data using the responseXML property from the XMLHttpRequest object, as shown in Example 4-6. Example 4-6. Parsing data sent from the server /* * Example 4-6, Parsing data sent from the server. */ /** * This function, parseResponse, takes the XML response from the server * and pulls out the elements it needs to dynamically alter the contents * of a page. */ function parseResponse( ) { /* Is the /readyState/ 4? */ if (request.readyState == 4) { /* Is the /status/ 200? */ if (request.status == 200) { var response = request.responseXML; var paramList = response.getElementsByTagName('param'); /* This will be the XHTML string to use */ var out = '
    '; for (i = 0, il = paramList.length; i < il;) out += '
  • ' + paramList[i++].firstChild.nodeValue + '
  • '; out += '
'; document.getElementById('list').innerHTML = out; } else alert('There was a problem retrieving the data: \n' + request.statusText); request = null; } } 78 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Here, we get a node list of all the elements with a tag name of param with getElementsByTagName( ), and after looping through the nodes and creating some quick and dirty XHTML, we use getElementById( ) to specify where we want to put our formatted string. The choice of using this to get to the value of the text node: paramList[i].firstChild.nodeValue instead of this: paramList.item(i).firstChild.nodeValue is really a matter of developer taste. I chose the former because it requires fewer keystrokes, and less is almost always more. XML in a String Sometimes the XML you want to dynamically pull comes from an XML file or an XML string. In these cases, you will want to load the file into a DOM Document object so that you can then parse the XML. To load a file you use the load( ) method, which is implemented in all browsers. To load an XML string, however, there is no universal method. Internet Explorer has a method that is part of the Document object, called loadXML( ). Unfortunately, most other browsers do not implement such a method. In these cases, the developer will need to create his own loadXML( ) for crossbrowser compatibility, as shown in Example 4-7. Example 4-7. Adding a loadXML method to the Document object /* * Example 4-7, Adding a loadXML method to the Document object. */ /* Is this a DOM-compliant browser? */ if (!window.ActiveXObject) { /** * This method, loadXML, is a cross-browser method for DOM-compliant * browsers that do not have this method natively. It loads an XML * string into the DOM document for proper XML DOM parsing. */ Document.prototype.loadXML = function (xml_string) { /* Parse the string to a new doc */ var doc = (new DOMParser( )).parseFromString(xml_string, 'text/xml'); /* Remove all initial children */ while (this.hasChildNodes( )) this.removeChild(this.lastChild); /* Insert and import nodes */ for (i = 0, il = doc.childNodes.length; i < il;) this.appendChild(this.importNode(doc.childNodes[i++], true)); }; } XML www.it-ebooks.info | 79 First, let’s look at the code required to load an XML file into the DOM, as shown in Example 4-8. We want to make sure this code is cross-browser-compliant; otherwise, it is useless to us. Example 4-8. Cross-browser code to load an XML file into the DOM /* * Example 4-8, Cross-browser code to load an XML file into the DOM. */ /** * This function, loadXMLFromFile, takes the passed /p_file/ string file name * and loads the contents into the DOM document. * * @param {String} p_file The string file name to load from. */ function loadXMLFromFile(p_file) { /* Does this browser support ActiveX? (Internet Explorer) */ if (window.ActiveXObject) { xmlDoc = new ActiveXObject('Microsoft.XMLDOM'); xmlDoc.async = false; xmlDoc.load(p_file); parseXML( ); } else if (document.implementation && document.implementation.createDocument) { xmlDoc = document.implementation.createDocument('', '', null); xmlDoc.load(p_file); xmlDoc.onload = parseXML( ); } } var xmlDoc = null; loadXMLFromFile('dummy.xml'); With this example, the file dummy.xml is loaded as a DOM Document object before the function parseXML( ) is called to parse the global xmlDoc object. When xmlDoc is created using document.implementation.createDocument('', '', null), the load method is a synchronous call. The client halts everything else until the XML file is loaded. The ActiveX object, however, is not automatically a synchronous call. The async property must be set to false to achieve the same functionality as its counterpart. If you want the ActiveX object to behave asynchronously, you first must set the async property to true. Second, you must set the onreadystatechange property to a function call. The function that is called on every readyState change must then check the state of the document’s loading. The same readyState codes in Table 4-1 that apply to the XMLHttpRequest object also apply to the xmlDoc object. Example 4-9 gives an example of this. 80 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Example 4-9. Asynchronously loading an XML file /* * Example 4-9, Asynchronously loading an XML file. */ /** * This function, loadXMLAsyncFromFile, takes the passed /p_file/ string file name * and loads the contents asynchronously into the DOM document. * * @param {String} p_file The string filename to load from. * @see #verify */ function loadXMLAsyncFromFile(p_file) { xmlDoc = new ActiveXObject('Microsoft.XMLDOM'); xmlDoc.async = true; xmlDoc.onreadystatechange = verify; xmlDoc.load(p_file); } /** * This function, verify, checks to see if the file is ready to be parsed * before attempting to use it. * * @see #loadXMLAsyncFromFile */ function verify( ) { /* Is the /readyState/ 4? */ if (xmlDoc.readyState == 4) parseXML( ); else return false; } var xmlDoc = null; loadXMLAsyncFromFile('dummy.xml'); So, we can load a file now, but sometimes you’ll want to create a DOM Document object from a string, too. Why? Imagine that you are getting your dynamic data from a third-party application. In this scenario, you have no control over the code because it is not open source. This application also sends the client XML data, but does not send the Content-Type of the HTTP header as text/xml. In this case, the responseXML property is set to null and the data is only in the responseText property as a string. This is where the loadXML( ) method comes in handy. Example 4-10 shows how to use this method to load an XML string. XML www.it-ebooks.info | 81 Example 4-10. Loading an XML string into a DOM Document object /* * Example 4-10, Loading an XML string into a DOM Document object. */ /** * This function, parseResponse, takes the XML response from the server * and pulls out the elements it needs to dynamically alter the contents of a page. */ function parseResponse( ) { /* Is the /readyState/ 4? */ if (request.readyState == 4) { /* Is the /status/ 200? */ if (request.status == 200) { var xmlString = request.responseText; var response = null; /* Does this browser support ActiveX? (Internet Explorer) */ if (window.ActiveXObject) { response = new ActiveXObject('Microsoft.XMLDOM'); response.async = false; } else if (document.implementation && document.implementation.createDocument) response = document.implementation.createDocument('', '', null); response.loadXML(xmlString); var paramList = response.getElementsByTagName('param'); /* This will be the XML string to use */ var out = '
    '; /* Loop through the list taken from the XML response */ for (i = 0, il = paramList.length; i < il;) { out += '
  • ' + paramList[i++].firstChild.nodeValue + '
  • '; } out += '
'; document.getElementById('list').innerHTML = out; } else alert('There was a problem retrieving the data: \n' + request.statusText); request = null; } } Once the XML is loaded into a DOM Document object, you can parse it in the same way you would with a responseXML object. XPath The ability to quickly navigate the DOM to the elements you need is an essential part of Ajax web development. This is where the W3C standard, XPath, comes into play. XPath is the syntax a developer can use to define parts of an XML document using 82 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info path expressions to navigate through elements and attributes in the document. More important, it is an integral part of Extensible Stylesheet Language Transformation (XSLT), which we’ll cover in the next section. Now the bad news: DOM Level 3 XPath is fully implemented in Mozilla, but not in Internet Explorer. Are you as sick of writing cross-browser-compatible code as I am? To jump the gun a little bit, what we need is a client framework that can do all of this cross-browser-compatible code for us so that we can concentrate on other things. So, although I cover this topic in more depth later in this chapter (in the section “A Quick Introduction to Client Frameworks”), in this section I want to introduce you to Sarissa (http://sarissa.sourceforge.net/). Sarissa provides a cross-browser solution, not only to XPath but also to XSLT. Jumping right in, first we need to create a DOM Document object using Sarissa: var domDoc = Sarissa.getDomDocument( ); Now we need to load the XML document into the newly created DOM Document object: domDoc.async = false; domDoc.load('my.xml'); Here we set the DOM Document object to load synchronously, and then executed the file load. Now comes the XPath part. For this, we use two methods: selectNodes( ) and selectSingleNode( ). Here is the Internet Explorer gotcha. Before we can use either method, we must call the setProperty( ) method. If we didn’t take this step, Internet Explorer would give an error. To make XPath available to the DOM Document object in Internet Explorer, you do the following: domDoc.setProperty('SelectionLanguage', 'XPath'); And if you want Internet Explorer to resolve namespace prefixes, you do the following: domDoc.setProperty('SelectionNamespaces', 'xmlns:xhtml=\'http://www.w3.org/1999/xhtml\''); The same method called with different parameters sets the different things the DOM Document object needs. This method can also enable the object to resolve multiple namespace prefixes using a space-delimited list: domDoc.setproperty('SelectionNamespaces', 'xmlns:xhtml=\'http://www.w3.org/1999/xhtml\' xmlns:xsl=\'http://www.w3.org/1999/XSL/Transform\''); To use these methods, you must include the sarissa_ieemu_xpath.js file on your page. Mozilla does not need this method and will ignore it if it is called. Finally, we are ready to use the XPath methods. Example 4-11 gives an example of using both the selectNodes( ) and selectSingleNode( ) methods. It assumes that the file being loaded contains the following: XML www.it-ebooks.info | 83

This does not really do much, but it serves our example. Example 4-11. XPath in action with Sarissa /* * Example 4-11, XPath in action with Sarissa. */ /* Create a new Sarissa DOM document to hold the XSL */ var domDoc = Sarissa.getDomDocument( ); /* Load the XSL from the file */ domDoc.async = false; domDoc.load('my.xsl'); /* Set the properties of the XSL document to use XPath */ domDoc.setProperty('SelectionLanguage', 'XPath'); domDoc.setProperty('SelectionNamespaces', xmlns:xsl=\'http://www.w3.org/1999/XSL/Transform\''); var nodeList = null; var element = null; /* Use XPath to get elements from the document */ nodeList = domDoc.selectNodes('//xsl:template'); element = domDoc.documentElement.selectNode('//xsl:template'); The example finds nodes that match the string xsl:template anywhere within the document’s DOM tree. For better information on XPath and how to use expressions to search through the DOM tree, John E. Simpson’s XPath and XPointer (O’Reilly) is a good reference. XSLT As I stated earlier, XSLT relies on XPath in a big way, using it to search the document to extract parts of the DOM tree during a transformation, forming conditional expressions, building sequences, and so forth. XSLT makes good sense in Ajax web development, as it can transform XML data sent from the server into something the client can recognize. Again, an easy solution for this task is using Sarissa. 84 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info The simplest way to use Sarissa is to load the XSL file, create an XLSTProcessor object, and transform the XML in question using the transformToDocument( ) method. Example 4-12 builds off of Example 4-10 where the XML to transform is received from an Ajax call to the server. The XSL document is loaded from a file residing on the server. Example 4-12. Sarissa in action for XSLT /* * Example 4-12, Sarissa in action for XSLT. */ /** * This function, parseResponse, checks the /request/ object * and /status/ to see if the response is complete, and then * returned and does an XSLT transformation using a provided * sets the transformed XML to the /innerHTML/ of the 'list' */ function parseResponse( ) { /* Is the /readyState/ for the /request/ a 4 (complete)? if (request.readyState == 4) { /* Is the /status/ from the server 200? */ if (request.status == 200) { var xmlString = request.responseText; /* Create a new Sarissa DOM document to hold the var xmlDoc = Sarissa.getDomDocument( ); /* Create a new Sarissa DOM document to hold the var xslDoc = Sarissa.getDomDocument( ); for its /readyState/ takes the XML string XSL file. It then element. */ XML */ XSL */ /* Parse the /responseText/ into the /xmlDoc/ */ xmlDoc = (new DOMParser( )).parseFromString(xmlString, 'text/xml'); /* Load the XSL document into the /xslDoc/ */ xslDoc.async = false; xslDoc.load('my.xsl'); xslDoc.setProperty('SelectionLanguage', 'XPath'); xslDoc.setproperty('SelectionNamespaces', xmlns:xsl=\'http://www.w3.org/1999/XSL/Transform\''); /* Create a new /XSLTProcessor/ object to do the transformation */ var processor = new XSLTProcessor( ); processor.importStyleSheet(xslDoc); /* Transform the document and set it to the /innerHTML/ of the list */ var newDoc = processor.transformToDocument(xmlDoc); document.getElementById('list').innerHTML = Sarissa.serialize(newDoc); } else alert('There was a problem retrieving the data: \n' + request.statusText); request = null; } } XML www.it-ebooks.info | 85 I might have oversimplified the process of XSLT transformation using Sarissa. So, I’ll demystify it a little bit. First, we receive the responseText from the server, which we have seen before. The difference from Example 4-10 is that we use Sarissa’s getDomDocument( ) method to create our document and then import the string into XML using the line: xmlDoc = (new DOMParser( )).parseFromString(xmlString, 'text/xml'); Next, we loaded the XSL file using Sarissa’s methods for doing so. After that, we created the XSLTProcessor object, as well as the stylesheet for transforming our XML (the my.xsl file, in this example), using the importStyleSheet( ) method. Finally, we executed the transformToDocument( ) method on the XML, and a transformed XML document was created. We completed the example by serializing the XML document using Sarissa’s serialize( ) method so that the document could be inserted into the XHTML document. In Example 4-12, we instantiated both of the XML documents being used—the response from the server and the XSL file—using Sarissa’s getDomDocument( ) method. This was by design, and not just to show how to load an XML string into a DOM Document using Sarissa. If you were to create the XSL using document.implementation.createDocument( ) or ActiveXObject('Microsoft.XMLDOM'), you would not be able to manipulate that object using Sarissa’s classes and methods. You must use Sarissa to create both DOM objects. JSON JSON is a data exchange format that is a subset of the object literal notation in JavaScript. It has been gaining a lot of attention lately as a lightweight alternative to XML, especially in Ajax applications. Why is this? Because of the ability in JavaScript to parse information quickly using the eval( ) function. JSON does not require JavaScript, however, and you can use it as a simple exchange format for any scripting language. Here is an example of what JSON looks like: {'details': { 'id': 1, 'type': 'book', 'author': 'Anthony T. Holdener III', 'title': 'Ajax: The Definitive Guide', 'detail': { 'pages': 960, 'extra': 20, 'isbn': 0596528388, 'price': { 'us': 49.99, 'ca': 49.99 } } }} 86 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info This is the equivalent in XML:
Anthony T. Holdener III Ajax: The Definitive Guide 960 0596528388
Some developers think JSON is more elegant at describing data. Others like its simplicity. Still others argue that it is more lightweight (we’ll get into that in a bit). Looking at the two preceding examples, you can see that they’re almost identical in size. In fact, the size difference is a mere eight bytes. I won’t tell you which is smaller; keep reading and you’ll find out. I will tell you that you can find more on JSON at http://www.json.org/. JSON Requests and Responses Requests to the server using Ajax and JSON are the same as with XML. We are again looking at this function: function requestData(request, url, data, func, method) { if (request) { if (method == 'GET') request.open('GET', url + '?' + data, true); else request.open('POST', url, true); request.onreadystatechange = func; if (method == 'GET') request.send(''); else request.send(data); } } As with the XML string, your data is the JSON string and the method again must be a 'POST'. That part is simple enough, but what about the server side of things? If JSON is just a notation for JavaScript, how will other languages interpret it? Luckily, JSON has been ported to pretty much every scripting language there is. For a full list, you should refer to the JSON site. Because our examples are in PHP, we have many choices for porting JSON. I will be using JSON-PHP in these examples. The data we are sending to the server will look like this: {'parameters': { 'param': [ {'id': 1, 'value': 'data1'}, {'id': 2, 'value': 'data2'}, {'id': 3, 'value': 'data3'} ] } } JSON | www.it-ebooks.info 87 This is the JSON version of the XML from the “XML Requests and Responses” section, earlier in this chapter. Example 4-13 shows how to handle this request with PHP. Example 4-13. PHP handling a JSON request from the client decode($raw_json); /* Find all of the parameter values */ for ($i = 0, $il = count($data['parameters']['param']); $i < $il;) { $d = $data['parameters']['param'][$i++]; switch ($d['id']) { case 1: $value1 = $d['value']; break; case 2: $value2 = $d['value']; break; case 3: $value3 = $d['value']; } } /* Set up the parameters to connect to the database */ $params = array ('host' => $host, 'username' => $username, 'password' => $password, 'dbname' => $db); try { /* Connect to the database */ 88 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Example 4-13. PHP handling a JSON request from the client (continued) $conn = Zend_Db::factory('PDO_MYSQL', $params); $value1 = $conn->quote($value1); $value2 = $conn->quote($value2); $value3 = $conn->quote($value3); /* * Create a SQL string and use the values that are protected from SQL injections */ $sql = 'SELECT * FROM table1 WHERE condition1 = $value1 AND condition2 = $value2' .' AND condition3 = $value3'; /* Get the results of the query */ $result = $conn->query($sql); /* Are there results? */ if ($rows = $result->fetchAll( )) { /* Create a JSON result string */ $value = array( ); $value['results'] = array( ); $value['results']['result'] = array( ); /* Loop through the results */ foreach($rows in $row) $value['results']['result'][$i] = array('column1' => $row['column1'], 'column2' => $row['column2']); $output = $json->encode($value); } } catch (Exception $ex) { $output = "{error: 'There was an error retrieving the data.'}"; } echo $output; ?> In this example, the JSON string that is passed to the server is read into the variable $raw_data. The string is then decoded using the decode( ) method from the json class. This decoded object looks like this: Array ( [parameters] => Array ( [param] => Array ( [0] => Array ( [id] => 1 [value] => data1 ) [1] => Array ( [id] => 2 [value] => data2 ) JSON | www.it-ebooks.info 89 [2] => Array ( [id] => 3 [value] => data3 ) ) ) ) From here, it is just a matter of looking through the array and pulling out the values of each index. After that, an array is created with the response data. This array is encoded into a JSON string with the encode( ) method, and then it is sent back to the client. The response to the client looks like this: {"results":{"result":[{"column1":12,"column2":13},{"column1":3,"column2":5}]}} It is then up to the client to parse this string. When instantiating the Services_JSON class, the parameter that was passed, SERVICES_JSON_LOOSE_TYPE, forced the decode( ) method to create associative arrays. If this value was not passed, the decode( ) method would have returned objects. This value can be passed with the Boolean OR (|) and the value SERVICES_JSON_SUPPRESS_ERRORS which, you guessed it, suppresses any errors when decoding or encoding. Parsing Back on the client, after the server has done what it needs to do, the response is set in the responseText property of the XMLHttpRequest object. Once the readyState and status are set to 4 and 200, respectively, the JSON string can be saved and eval( )’d, as in Example 4-14. Example 4-14. Getting a JSON string ready to parse /* * Example 4-14, Getting a JSON string ready to parse. */ /** * This function, parseResponse, checks the /request/ object for its /readyState/ * and /status/ to see if the response is complete, and then parses the * /responseText/ (the JSON string) to get the results from the server. */ function parseResponse( ) { /* Is the /readyState/ for the /request/ a 4 (complete)? */ if (request.readyState == 4) { /* Is the /status/ from the server 200? */ if (request.status == 200) { var jsonString = request.responseText; var response = eval('(' + jsonString + ')'); 90 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Example 4-14. Getting a JSON string ready to parse (continued) // here is where the parsing would begin. } else alert('There was a problem retrieving the data: \n' + request.statusText); request = null; } } The response is now a JavaScript object, and the object can be walked, searched, or manipulated just like any other DOM object. Example 4-15 shows some ways to get at the data from the JavaScript object created with a JSON string. Example 4-15. Parsing the JSON response object /* * Example 4-15, Parsing the JSON response object. */ /** * This function, parseResponse, checks the /request/ object for its /readyState/ * and /status/ to see if the response is complete, and then parses the * /responseText/ (the JSON string) to get the results from the server. */ function parseResponse( ) { /* Is the /readyState/ for the /request/ a 4 (complete)? */ if (request.readyState == 4) { /* Is the /status/ from the server 200? */ if (request.status == 200) { var jsonString = request.responseText; var response = eval('(' + jsonString + ')'); var out = '
'; /* Loop through the object and create the checkboxes*/ for (i = 0, il = response.Parameters.param.length; i < il; i++) { var resp = response.Parameters.param[i]; out += '
'; } out += '
'; document.getElementById('choices').innerHTML = out; } else alert('There was a problem retrieving the data: \n' + request.statusText); request = null; } } Looking at this example, you probably see just how easy it is to get to the data you need. That is part of the beauty of JSON. JSON | www.it-ebooks.info 91 Choosing a Data Exchange Format I have shown you how to make Ajax calls between the client and the server with both XML and JSON. So which one should you use? I could tell you that you should use JSON because it is lightweight and easy to use on the client. Or, I could tell you that you should use XML because it is better able to describe data when complicated data sets are moved back and forth between the client and the server. I could tell you these things, but I am not going to. The fact is that it really is up to the developer and the situation that she is in. That’s not to say that you cannot make an informed opinion once I show you the facts about both XML and JSON. One of the arguments for JSON is that it is lightweight in nature. Earlier I said I would tell you whether the JSON example or the XML example was smaller in byte size: the JSON example contains 248 bytes (count them yourself if you like), whereas the XML example contains 240 bytes. So much for JSON being lightweight compared to XML. In reality, the complexity and size of the data being exchanged determines which format is smaller in size. Another argument for JSON is that it is easier to read by both humans and machines. It is true that it takes less time to parse through JSON than XML; thus, JSON is easier for the machine to “read.” It can actually take longer to eval( ) a JSON string than to create a DOM Document object depending on the size of the data. Based on this, you could say that for machines, it is a wash. But what about humans? I think that is a matter of developer opinion. Beauty is in the eye of the beholder, after all. Here are some arguments for XML. XML works as a good data exchange format for moving data between similar applications. XML is designed to have a structure that describes its data, enabling it to provide richer information. XML data is self-describing. XML supports internationalization of its data. XML is widely adopted by the technology industry. You can counter all of these arguments with one simple statement: the same is true for JSON. JSON can provide the same solid data exchange between like systems. JSON is also built on structures (those structures are objects and arrays). JSON is just as self-describing as XML. JSON supports Unicode, so internationalization is not a problem. To be fair, JSON is pretty new, and the industry is already adopting it. Only time will tell which has more widespread adoption. Those arguments could be rebuffed easily. Now, let’s take a look at some other arguments. XML has a simple standard, and therefore you can process it more easily. XML is object-oriented. XML has a lot of reusable software available to developers to read its data. For the first argument, it is true that XML has a simple standard, but JSON actually has a simpler structure and is processed more easily. Let the record show that XML is not object-oriented, but instead is document-oriented. In that same line of thinking, JSON is actually data-oriented, making it easier to map to object-oriented systems. 92 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info As for software, XML needs to have its structured data put into a document structure, and it can be complicated with elements that can be nested, attributes that cannot be nested, and an endless number of metastructures that can be used to describe the data. JSON is based entirely on arrays and objects, making it simple and requiring less software to translate. I could do this all day. However, I hope you now understand that there is no right answer. I cannot tell you which data exchange format is better any more than I could tell you which server-side frameworks to use. Each developer should decide, after asking the following questions: 1. What are my client and server platforms (what languages will I use)? 2. How large are the data sets I will be transferring? 3. Am I more comfortable with JavaScript or XML/XSLT? 4. Will I be using outside web services? If so, what format do they prefer? 5. How complex are the data sets being used? 6. Do I completely control the server that the client will be getting responses from? Regarding question 1, you can decide which format to use simply from the languages you will be using. If you aren’t going to be using JavaScript on the client side, JSON doesn’t make sense. Likewise, the support for XML or JSON on the server side can be a major factor. As for question 2 regarding the size of the data sets that will be transferred, JSON may be a better solution than XML if transferred byte size is a concern. Remember, JSON is also faster for parsing data—larger data sets should be processed faster with JSON than with XML. If you are not passing a large amount of data, XML may be the better alternative. A small, already formatted XHTML data set passed to the client can very quickly be utilized; JSON would have to be formatted. There isn’t much I need to say about question 3. I think it is self-explanatory. Question 4 is good to consider. If you will be using outside web services in your applications, your hands may be tied regarding the format to use to request data, and certainly, your choices will be limited for the data sent back from the web service in its response. Question 5 is pretty easy to answer. JSON works great when the data being described is just that—data. XML is much better suited for handling data such as sounds, images, and some other large binary structures because it has the handy <[CDATA[]]> feature. I am not saying it is a good idea to send this type of data using Ajax. All I am saying is that it is possible with XML and not with JSON. As for question 6, as I just explained, if you do not have complete control of both sides of the data exchange, it could be dangerous to use JSON as the format. This is because JSON requires the eval( ) method to parse its data. The way around this is to use a JSON parser. With a parser, only the JSON text is parsed, making it much safer. The only downside to the JSON parser is that it slows down response object creation. Choosing a Data Exchange Format www.it-ebooks.info | 93 Deciding on a data exchange format is hard and often leads to second-guessing or, worse, rewriting code after switching formats. My advice is to choose a format and stick with it, but remember this: always use the right tool for the right job. A Quick Introduction to Client Frameworks Earlier in the chapter, I used the Sarissa library to aid in XSLT and XPath development. Sarissa is one of many frameworks available for Ajax and JavaScript. It would not be practical to highlight all of them, but in this section I will cover a few of the most popular. The Dojo Toolkit The Dojo Toolkit, which you can find at http://www.dojotoolkit.org/, is a componentbased open source JavaScript toolkit that is designed to speed up application development on multiple platforms. It is currently dual-licensed under the terms of the BSD License and the Academic Free License. Dojo is a bootstrapping system, whereby you can add individual toolkit components once you’ve loaded the base component. Dojo’s components, known as packages, can be single or multiple files, and may be interdependent. Some of the toolkit’s notable features are: • A robust event system that allows for code to execute not only on DOM events, but also on function calls and other arbitrary events • A widget system that allows for the creation of reusable components, and includes a number of prebuilt widgets: a calendar-based date picker, inline editing, a rich-text editor, charting, tool tips, menus and trees, and more • An animation library that allows for the creation of reusable effects, and includes a number of predefined effects, including fades, wipes, slides, drag and drop, and more • A wrapper around the XMLHttpRequest object, allowing for easier cross-browser Ajax development • A library of utilities for DOM manipulation More recent Dojo developments include the announcement of official support by both Sun Microsystems* and IBM† (including code contributions), and the Dojo Foundation’s involvement with the OpenAJAX Alliance (http://www.openajax.org/). As of this writing, the current version of the Dojo Toolkit is 1.3.2. * You can find Sun Microsystems’ article at http://www.sun.com/smi/Press/sunflash/2006-06/sunflash. 20060616.1.xml. † You can find IBM’s article at http://www-03.ibm.com/press/us/en/pressrelease/19767.wss. 94 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Prototype The Prototype Framework, which you can find at http://www.prototypejs.org/, is a JavaScript framework that is used to develop foundation code and to build new functionality on top of it. Sam Stephenson developed and maintains it. Prototype is a standalone framework, though it is part of Ruby on Rails and is found in Rails’ source tree. According to the September 2006 Ajaxian survey, Prototype is the most popular of all the Ajax frameworks. Prototype is a set of foundation classes and utilities, and so it does not provide any of the flashy Web 2.0 components found in other JavaScript frameworks. Instead, it provides functions and classes you can use to develop JavaScript applications. Some of the most notable functions and classes are: • The dollar sign functions—$( ), $F( ), $A( ), $$( ), and so on • The Ajax object • The Element object A number of JavaScript libraries and frameworks are built on top of Prototype, most notably script.aculo.us and moo.fx. In this book, I am using Prototype version 1.5.1.1, though the latest version as of this writing is 1.6. script.aculo.us script.aculo.us, which you can find at http://script.aculo.us/, is a JavaScript library that provides developers with an easy-to-use, cross-browser user interface to make web sites and web applications fly. Thomas Fuchs, a partner at wollzelle, created script.aculo.us, and open source contributors extend and improve it. script.aculo.us is released under the MIT License, and like Prototype, it is also included with Ruby on Rails and extends the Prototype Framework by adding visual effects, user interface controls, and utilities. script.aculo.us features include: • Visual effects, including opacity, scaling, moving, and highlighting, among others • Dragging and dropping, plus draggable sorting • Autocompletion and inline editing • Testing As of this writing, the current version of script.aculo.us is 1.8.2. moo.fx moo.fx, which you can find at http://moofx.mad4milk.net/, is different from the other frameworks that build on Prototype in that it uses a stripped-down version of the Prototype library: Prototype Lite. Valerio Proietti created moo.fx and it is released A Quick Introduction to Client Frameworks www.it-ebooks.info | 95 under the MIT License. moo.fx is said to be a super-lightweight JavaScript effects library. Some of the classes that it has implemented include simple effects on elements (changing height, width, etc.), more complex effects (such as accordion, scrolling, cookie memory, and so on), and an Ajax class. moo.fx is not a replacement for script.aculo.us, and instead creates its own effects for Ajax web applications. As of this writing, the current version of moo.fx is 2. DWR DWR, which you can find at http://directwebremoting.org/dwr/index.html, is a Java open source library that allows developers to write Ajax web sites by permitting code in a browser to use Java functions running on a web server just as though it were in the browser. DWR works by dynamically generating JavaScript based on Java classes. The code then does some “Ajax magic” to make it feel like the execution is happening on the browser, but in reality the server is executing the code and then DWR is shoveling the data back and forth. DWR consists of two main parts: • A Java servlet running on the server that processes requests and sends responses back to the browser • JavaScript running in the browser that sends requests and can dynamically update the web page DWR acts differently than other frameworks and libraries because the pushing of data back and forth gives its users a feel much like conventional RPC mechanisms such as RMI and SOAP, with the added benefit that it runs over the Web without requiring web browser plug-ins. DWR is available under the Apache Software License v2.0. As of this writing, the current version of DWR is 2.0. jQuery jQuery, which you can find at http://jquery.com/, is a new type of JavaScript library that is not a huge, bloated framework promising the best in Ajax, nor just a set of needlessly complex enhancements to the language. jQuery is designed to change the way you write JavaScript code by how the DOM is accessed. John Resig wrote and maintains it, and the developer community contributes to it. jQuery is available under the MIT License. 96 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info jQuery achieves its goal of new JavaScript scripting by stripping all the unnecessary markup from common, repetitive tasks. This leaves them short, smart, and understandable. The goal of jQuery, as stated on its web site, is to make it fun to write JavaScript code. As of this writing, the current version of jQuery is 1.3.2. Sarissa As I explained earlier in the chapter, Sarissa (http://sarissa.sourceforge.net/) is a library that encapsulates XML functionality. It is good for XSLT- and XPath-related problems. It has good DOM manipulation functions, as well as XML serialization. Its major benefit is that it provides cross-browser functionality without the developer having to take care of everything else, and it is small in size. It is an ideal library when a developer needs nothing more complicated than some XML DOM manipulation. Sarissa is distributed under the GNU GPL version 2 and later, the GNU LGPL version 2.1 and later, and the Apache Software License v2.0. Having three licenses to choose from makes Sarissa a flexible library as well. As of this writing, the latest release of Sarissa is 0.9.9.4. Others Of course, you can use many other frameworks to develop Ajax web applications. Frameworks such as Rico (http://openrico.org/), Yahoo! UI (http://developer.yahoo. com/yui/), and Ajax.NET (formerly Atlas; http://ajax.asp.net/) are also popular depending on the development environment, though their use is more in the four to five percent range. The examples in the rest of this book will use many of the frameworks I’ve highlighted here. You can find an exhaustive list of frameworks for Ajax in Appendix A of Ajax Design Patterns by Michael Mahemoff (O’Reilly). His list highlights each framework and explains its licensing terms. Simplifying Development In general, frameworks are meant to ease the grunt work developers usually have to perform when building a foundation before beginning to code. Frameworks allow developers to jump right into the important functional parts of the application they are working on. Beyond that, good foundation frameworks such as Prototype also speed up the time it takes to program through the classes and functions they offer. In this section, we will explore some of the ways these foundations help with Ajax application programming, and how they will crop up throughout the rest of this book. Simplifying Development | www.it-ebooks.info 97 Prototype Helper Functions As I said before, Prototype is most famous for the dollar sign function, $( ). Other frameworks have been duplicating Prototype’s functionality since it was introduced. So, what does it do? $( ) is a helper function that provides references to any element based on the ids passed to it—that’s right, the plural of id. For example: var navigation = $('img1'); In this example, the navigation variable is set to the element with id='img1'. Here is an example of multiple ids being passed: var imageArray = $('img1', 'img2', 'img3'); Now, $( ) returns an array of elements with any ids that match what was passed in. This is just the tip of the iceberg when it comes to what Prototype can help with. We’ll take a look at three other helper functions Prototype provides before we talk about how Prototype helps with Ajax. These helper functions are $F( ), document. getElementsByClassName( ), and, as of Prototype version 1.5.0_rc0, $$( ). $F( ) returns the value of any form field that is identified by the id passed to the function. For example, with the following in a form: it is possible to get the value of food_choice like this: var food_choice = $F('food_choice'); The variable food_choice would be set with filet. Prototype extended the document object with document.getElementsByClassName( ), a method that can be very handy. For example, to get all of the elements that have class='borderless', you simply need to do the following: var imageArray = document.getElementsByClassName('borderless'); This method is even more powerful. Consider the following: var imageArray = document.getElementsByClassName('borderless', $('helpWindow')); In this case, the array would return all elements with class='borderless' that are inside the element with id='helpWindow'. $$( ) is a powerful function that was added to the Prototype library only recently. With this function, using the standard CSS selector syntax allows you to select corresponding elements. For example: var menuItemArray = $$('#menuitem div'); Here, all div elements inside 'menuitem' are returned. Or: var linkArray = $$('a.menuPath'); 98 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info This code returns an array of links that have the class name menuPath. You can probably see how powerful $$( ) is. Prototype has other helper functions as well, such as $H( ), $R( ), and $A( ). The best documentation for all of Prototype’s functions and classes is on its official site at http://www.prototypejs.org/. Prototype and Ajax Prototype has three objects for use with Ajax functionality: Ajax.Request, Ajax.Updater, and Ajax.PeriodicalUpdater. Our main focus will be with Ajax.Request, though we will briefly discuss the other two as well. Here is a basic Ajax request using Ajax.Request: new Ajax.Request(URL, { method: 'get', parameters: 'param1=data1', onSuccess: parseResponse, onFailure: handleError }); The constructor takes a URL and options in the form of an object. In our example, we are sending parameter param1 to URL via the GET method. If the request is successful, it will call parseResponse with an XMLHttpRequest object. If the request were to fail, the function handleError would be called. You can see a list of all available options in Table 4-3. Table 4-3. Optional arguments to pass to the constructor Option Description parameters A URL-encoded string to be sent with the request in the URL. method The type of request for the call. Is post or get, with the default being post. asynchronous Tells the object whether it should make the call asynchronously. Is true or false, with the default being true. requestHeaders An array of request headers to be sent with the call. They should be in the form: postBody Contents that are passed with the body of the request. This applies to a post only. onInteractive, onLoaded, onComplete Assigns a function to call when the XMLHttpRequest object triggers one of these events. The function is passed the XMLHttpRequest object. on404, onXXX Assigns a function to call when the server returns one of these response codes. The function is passed the XMLHttpRequest object. onSuccess Assigns a function to call when the request is completed successfully. The function is passed the XMLHttpRequest object and the returned JSON object (if any). onFailure Assigns a function to call when the server returns a fail code. The function is passed the XMLHttpRequest object. onException Assigns a function to call when there is a client-side error. The function is passed the XMLHttpRequest object. ['header1', 'value1', 'header2', 'value2'] Simplifying Development | www.it-ebooks.info 99 Example 4-16 shows a more complete example of how to call an Ajax request using Prototype. Example 4-16. Prototype in action for an Ajax request /* * Example 4-16, Prototype in action for an Ajax request. */ /* Create an Ajax call to the server */ new Ajax.Request(URL, { method: 'post', parameters: 'param1=data1¶m2=data2¶m3=data3', onSuccess: parseResponse, onFailure: function(xhrResponse) { alert('There was a problem retrieving the data: \n' + xhrResponse.statusText); } }); /** * This function, parseResponse, takes the /xhrResponse/ object that is * the response from the server and parses its /responseXML/ to create a * list from the results. * * @param {Object} xhrResponse The response from the server. */ var parseResponse = function(xhrResponse) { var response = xhrResponse.responseXML; var paramList = response.getElementsByTagName('param'); var out = '
    '; /* Loop through the /param/ elements in the response to create the list items */ for (i = 0, il = paramList.length; i < il;) { out += '
  • ' + paramList[i++].firstChild.nodeValue + '
  • '; } out += '
'; $('list').innerHTML = out; } This example has the same functionality as Example 4-6 does; however, the developer has much less to code. This makes his job easier, and he can concentrate instead on the best way to parse the XML, how to display it, and so on. We set the request to a POST, and then created our URL-encoded parameter string. onSuccess called the function parseResponse, while onError was assigned an inline function definition. The biggest change was in the parseResponse function itself. Notice how we did not have to check the XMLHttpRequest object’s readyState or status. This was already done for us, or we wouldn’t be in the function. All that was left was to parse through the response; no new code here. The last thing to notice is that I used $( ) to get the element with id='list'. 100 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Something you may not realize unless you have traced through the Ajax.Request code is that in the setRequestHeaders( ) method, the object sets certain headers that are set on every HTTP request. They are: X-Requested-With: XMLHttpRequest X-Prototype-Version: 1.5.1.1 The server could check these headers to detect whether the request was an Ajax call and not a regular call. Now we know how Ajax.Request works, but what about Ajax.Updater? The syntax for Ajax.Updater is: new Ajax.Updater('myDiv', URL, { method: 'get', parameters: 'param1=data1' }); Here is the difference. The first parameter passed is the container that will hold the response from the server. It can be an element’s id, the element itself, or an object with two properties: object.success Element (or id) that will be used when the request succeeds object.failure Element (or id) that will be used otherwise Also, Ajax.Updater has options that the normal Ajax.Request does not, as shown in Table 4-4. Table 4-4. Ajax.Updater-specific options Option Description insertion Class telling the object how the content will be inserted. It is one of: • • • • evalScripts Insertion.After Insertion.Before Insertion.Bottom Insertion.Top Tells the object whether a script block will be evaluated when the response arrives. Ajax.Updater works by extending the functionality of Ajax.Request to actually make the request to the server. It then takes the response to insert it into the container. Finally, the syntax for the Ajax.PeriodicUpdater object is: new Ajax.PeriodicUpdater('myDiv', URL, { method: 'get', parameters: 'param1=data1', frequency: 20 }); Simplifying Development | www.it-ebooks.info 101 Like the Ajax.Updater class, the first parameter passed is the container that will hold the response from the server. It can be an element’s id, the element itself, or an object with two properties: object.success Element (or id) that will be used when the request succeeds object.failure Element (or id) that will be used otherwise Also like Ajax.Updater, Ajax.PeriodicUpdater has options that the normal Ajax.Request does not, as shown in Table 4-5. Table 4-5. Ajax.PeriodicUpdater-specific options Option Description decay Tells the object what the progressive slowdown for the object’s refresh rate will be when the response received is the same as the last one. For example, with decay: 2, when a decay is to occur, the object will wait twice as long before refreshing. If a decay occurs again, the object will wait four times as long before refreshing, and so on. Leave this option undefined, or set decay: 1 to avoid decay. frequency Tells the object the interval in seconds that it should wait between refreshes. Ajax.PeriodicUpdater works by calling Ajax.Updater internally on its onTimerEvent( ) method, and does not extend Ajax.Updater like it extends Ajax.Request. In this section, I presented a brief tutorial on how you can use a client-side framework such as Prototype to greatly increase development speed by producing a robust foundation library. This can help with more than just making requests to the server, as I showed here. As you progress through the book, you will find many situations in which a framework made things easier. These frameworks will not necessarily be Prototype, either. Now that you have this background, it is time to manipulate the DOM Document object that an Ajax call, or the DOM object itself, may return to you. 102 | Chapter 4: Foundations: Scripting XML and JSON www.it-ebooks.info Chapter 5 CHAPTER 5 Manipulating the DOM 5 Having an efficient method to send a request to the server and pull back its response without having to refresh the whole page is very important. It is only a small part of Ajax development, though. What is more important to any Ajax web application is what is done with the data the client receives. There’s a lot more work to do than just grabbing the data, formatting it, and setting it equal to the innerHTML of an element. Understanding how the HTML Document Object Model (DOM) works and how to manipulate it is of utmost importance. I like to think that this—manipulating the DOM—is where the magic of Ajax actually happens. This is what gives Ajax life and allows application development on the Web. The first key to understanding how the DOM works is to examine the structure of a DOM object. Then it will become clearer how the methods allow you to manipulate the DOM. Understanding the DOM The structure of any DOM object is its document tree. The document tree is made up of branches and leaves. Let’s look at a simple XHTML document, shown in Example 5-1, to clarify. Example 5-1. A simple XHTML document A Document tree example

A Document tree example

This is just a very simple example.

103 www.it-ebooks.info Example 5-1. A simple XHTML document (continued) Figure 5-1 shows this file as a simple document tree, with an emphasis on simple. html body div head div title h1 “. ” string “A document tree example” “A document tree example” “footer” “This is simple” “simple example” P “This is just a” em “very” Figure 5-1. A simple document tree I ignored the attributes where they would have been in the document tree to keep this example simpler. The first thing to notice is that the DOCTYPE declaration is not part of the document tree—DOCTYPEs and XML prologs are never part of the tree. The first element of the tree, , is known as the tree’s root element or root node. All other elements of the tree branch off from this first element. Any elements that branch from the root element are known as the element’s children. These children can be either branches themselves or simply leaves, meaning that they have no children of their own. The element is a child of the <head> element, and is itself a branch to the content contained within the element. This content would be a leaf on the tree. As I just said, the content contained within the <title> element is also an element. Specifically, it is a text element or text node. The World Wide Web Consortium 104 | Chapter 5: Manipulating the DOM www.it-ebooks.info (W3C) has standardized the list of node types that any element of a document tree can be, as shown in Table 5-1. Table 5-1. W3C node types Node type Numeric type value Description Element 1 Represents an element. Attribute 2 Represents an attribute. Text 3 Represents character data in an element or attribute. CDATA section 4 Represents text that may contain characters that would otherwise be considered markup. Entity reference 5 Represents an entity reference. Entity 6 Represents an entity. Processing instruction 7 Represents a processing instruction. Comment 8 Represents a comment. Document 9 Represents the document (this is the root node of the tree). Document type 10 Represents a list of entities that are defined for this document. Document fragment 11 Represents a document that is “lighter” than a true document node, as it contains only a part of a document. Notation 12 Represents a notation declared in the document type definition (DTD). That is a simple introduction to the structure of a DOM object. Now we need to learn how to traverse the branches of a document tree so that we can manipulate all of the different elements it contains. We’ve Already Met The methods that most greatly facilitate DOM Document object traversal might seem a little familiar, as you already met them in Chapter 4. These are getElementById( ) and getElementsByTagName( ). Add to these the Prototype library’s helper functions, and we have a good foundation for accessing specific elements on a document tree. Just to refresh, here are some common ways to access specific elements: /* Use Prototype's $( ) function to get an element by its id */ var myElement = $('myElement'); /* Get an array of elements based on their tag name */ var myElements = exampleDoc.getElementsByTagName('myTag'); /* Get an array of elements based on their class name */ var myElements = document.getElementsByClassName('myClass'); /* Get an array of link elements based on their class name */ var myElements = $$('a.myClass'); We’ve Already Met | www.it-ebooks.info 105 These methods and functions make Ajax development easy when we know the id, the class name, and so forth that we are looking for. But what if our Ajax web application is more complicated than that and it requires more sophisticated manipulation? It turns out that a host of methods are available for any kind of DOM manipulation you require. Manipulating DOM Elements, Attributes, and Objects Elements are the containers of all the data to be dynamically altered in an Ajax application. They can contain other elements, which contain still others, or they can simply hold a text node with data for the client. When we talk about these elements, we also want to discuss groups of them represented in document fragment objects. To round out this discussion on elements and objects, we will also consider text elements, since the value of these elements is the data in the application. Our discussion cannot center on just XHTML, either. You could need to alter XML received from a server response just as often as you need to alter the client’s page DOM. We will follow the W3C’s DOM Level 2 Recommendation (the standard methods that are available to a developer from the browser) when discussing methods available to a DOM Document object unless I specify otherwise. This allows you to write more robust code utilizing the power of the DOM, instead of writing workarounds for functionality that may be needed in only a particular area. Creating Elements, Attributes, and Objects An important benefit of dynamic content is the ability to create new content from freshly received data. This is necessary in dynamic menu creation, navigation, breadcrumbs, and web services, among other applications. Ajax relies on content changing within the page without having to reload the entire page. To accomplish this, we need to create new parts of the DOM. The first method we will concentrate on is createElement( ), which is used to create a new Element node. An example of this method is: var element = document.createElement('div'); alert(element.nodeName); /* Alerts 'DIV' */ createElement( ) takes as a parameter the name of the element type to instantiate, and creates an element of that specified type. It returns an instance of an Element interface. This is useful as it allows attributes to be directly specified on the returned element node. In this case, we created a new <div> element and used the variable element to store that interface. 106 | Chapter 5: Manipulating the DOM www.it-ebooks.info You didn’t think creating elements would be any more complicated than that, did you? Now, what if you need to add text data to the DOM Document object? The method createTextNode( ) will do the trick. To create a Text node, you do the following: var element = document.createTextNode('Text to create.'); alert(element.nodeValue); /* Alerts 'Text to create.' */ The parameter that createTextNode( ) takes is the data string that you want the node to represent. It then returns a new text node stored in element. Creating a new attribute for a node may be something your application requires. The createAttribute( ) method takes the name of the attribute as a string parameter, and then creates an Attr node of the passed name, as shown in the following: var element = $('elem'); var attribute = document.createAttribute('special'); attribute.value = 'temp'; element.setAttributeNode(attribute); alert(element.getAttribute('special')); /* Alerts 'temp' */ The Attr instance that is created can then be set on an Element using the setAttributeNode( ) method. We will discuss this method in the next section, “Modifying and Removing Elements, Attributes, and Objects.” Adding new elements to a DOM document tree that’s smaller than the page’s document tree can greatly speed up a script if the page is particularly large or complicated. This is where creating a document fragment can come in handy. Creating a new document fragment is as simple as: var fragment = document.createDocumentFragment( ); var titleText = $('title').firstChild; fragment.appendChild(document.createtextNode(titleText); alert(fragment.firstChild.nodeValue); /* alerts /titleText/ */ The createDocumentFragment( ) method does not take any parameters, and it creates an empty DocumentFragment object to which new elements may be added. Many other methods operate in a fashion similar to the methods I just illustrated. Table 5-2 lists all the DOM Document object methods used to create nodes in a document tree. Table 5-2. Creation methods Method Description W3C standard createAttribute(attrName) Creates a new Attr node having the name set to the passed attrName. Yes createAttributeNS(nsURI, qualName) Creates a new Attr node having the namespace URI set to the passed nsURI and the qualified name set to the passed qualName. Yes Manipulating DOM Elements, Attributes, and Objects | www.it-ebooks.info 107 Table 5-2. Creation methods (continued) Method Description W3C standard createCDATASection(textData) Creates a new CDATASection node with the value set to the passed textData. Yes createComment(textData) Creates a Comment node with the data set to the passed textData. Yes createDocumentFragment( ) Creates a new empty DocumentFragment object Yes createElement(elemName) Creates a new Element node with the name set to the passed elemName. Yes createElementNS(nsURI, qualName) Creates a new Element node having the namespace URI set to the passed nsURI and the qualified name set to the passed qualName. Yes createEntityReference(refName) Creates a new EntityReference object with the reference set to the passed refName. Yes createNode(nodeType, nodeName, nsURI) Creates a new node of the passed nodeType, with the name set to the passed nodeName and the namespace URI set to the passed nsURI (this is a Microsoft-specific method). No createProcessingInstruction (targ, data) Creates a new ProcessingInstruction object having the target set to the passed targ and the data set to the passed data. Yes createTextNode(textData) Creates a new Text node having the data set to the passed textData. Yes Modifying and Removing Elements, Attributes, and Objects Being able to create new elements and objects does not do us much good if we have no way to get these new nodes into part of a larger DOM document tree, whether it is a DocumentFragment or a Document. So, in this section we will discuss some methods for appending, removing, and modifying elements, attributes, and objects in a DOM document tree. One of the most common methods used is appendChild( ). It takes a passed node or object, and adds it to the end of the list of children for the node for which the method was called. For example: $('title').appendChild(document.createTextNode('This is an appended text node')); If the passed node is already part of the tree, it is first removed from the tree and then appended to the end of the list. Also remember that if the passed object is a DocumentFragment object, the entire contents of the fragment are appended to the end of the list of children. 108 | Chapter 5: Manipulating the DOM www.it-ebooks.info If the node that needs to be appended to the calling node should not go to the end of the list of children, you use the insertBefore( ) method to specify a location. For example: var element = document.createElement('div'); element.appendChild(document.createTextNode('Some text here.')); $('subHeading').insertBefore(element, $('bodyText')); As with the method appendChild( ), if the passed node is already part of the tree, it is first removed and then inserted before the reference node. Figure 5-2 shows what this would look like before the call to insertBefore( ), and Figure 5-3 shows what it would look like after. Also like appendChild( ), when the passed object is a DocumentFragment object, its children are inserted in the order in which they appear in the fragment and before the reference node. When no reference node is supplied, the passed node is inserted at the end of the list of child nodes. Figure 5-2. The document before any node insertion Figure 5-3. The document after the new node is inserted using insertBefore( ) Manipulating DOM Elements, Attributes, and Objects | www.it-ebooks.info 109 Sometimes nodes need to be removed from the document tree. These cases call for the removeChild( ) method. Here’s an example: document.removeChild($('loading')); removeChild( ) takes the node to be removed from the tree as the parameter, and the method returns the removed node after it has been removed from the tree. At times, you will have built a DocumentFragment that contains a formatted structure from an Ajax feed, and you will need to insert the fragment into the DOM document. The method importNode( ) handles these situations. For example: var response = results.responseXML; response = document.importNode(response.documentElement, true); $('responseDiv').appendChild(response); When it comes to appending, removing, or modifying data, many methods are available. It would be impractical to demonstrate each of them. So instead, I list and describe them in Table 5-3. Table 5-3. Manipulation methods Method Description Available interfaces appendChild(newNode) Appends the node newNode to the end of the list of children of this node. If the newNode already exists in the tree, it is removed first. If newNode is a DocumentFragment, the contents of the entire fragment are appended to the list. All appendData(newData) Appends the newData string to the end of the character data of the node. CDATASection, Comment, Text cloneNode(recursive) Returns a clone of this node with the exception being that the cloned node has no parentNode. If recursive is true, the method also clones any children of the node; otherwise, it clones only the node itself. All deleteData(offset, count) Deletes data from the node in 16-bit increments, starting at the offset and deleting count * 16-bit increments. If the sum of offset and count is greater than the length of the data, all data from the offset is deleted. CDATASection, Comment, Text importNode(node, recursive) Imports a node from another Document to this Document. The source node is not altered or moved from the original document. Instead, a copy of the node is made. If recursive is true, the method also imports any children of the node; otherwise, it imports only the node itself. Document 110 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-3. Manipulation methods (continued) Method Description Available interfaces insertBefore(newNode, refNode) Inserts newNode before the existing refNode. If refNode is null, newNode is inserted at the end of the list of children. All insertData(offset, arg) Inserts arg at the specified 16-bit offset. CDATASection, Comment, Text normalize( ) Pulls all Text nodes in the whole document tree, including Attr nodes, and puts them in a form where only structure separates the nodes. All removeAttribute(attrName) Removes the attribute with the name equal to the passed parameter attrName. Element removeAttributeNode (attrName) Removes the Attr node with the name equal to the passed parameter attrName. Element removeAttributeNS(nsURI, localName) Removes the attribute with the namespace URI equal to the passed parameter nsURI and the local name equal to the passed parameter localName. Element removeChild(nodeName) Removes the child node with the name equal to the passed parameter nodeName from the list of children and returns it. All replaceChild(newNode, oldNode) Replaces the child node oldNode with newNode in the list of children, and returns the oldNode. All replaceData(offset, count, arg) Replaces the data starting at the 16-bit offset, replacing a length of count * 16-bits with the passed arg. If the sum of offset and count is greater than the length, all data from the offset to the end of the data is replaced. CDATASection, Comment, Text setAttribute(attrName, value) Creates or alters the attribute with the passed attrName with the value of the passed value. Element setAttributeNode(newAttr) Adds the newAttr node to the attribute list. If newAttr replaces an existing Attr, the replaced node is returned. Element setAttributeNodeNS(newAttr) Adds the newAttr node to the attribute list. If newAttr replaces an existing Attr with the same namespace URI and local name, the replaced node is returned. Element Manipulating DOM Elements, Attributes, and Objects | www.it-ebooks.info 111 Table 5-3. Manipulation methods (continued) Method Description Available interfaces setAttributeNS(nsURI, qualName, value) Creates or alters the attribute with the namespace URI equal to the passed nsURI and the qualified name equal to the passed qualName with the value of the passed value Element splitText(offset) Splits the node into two nodes at the passed offset, keeping both nodes in the Document tree as siblings CDATASection You will notice all of the references to 16-bit units when talking about character data. This is because XML supports Unicode characters, which are two bytes (16 bits) per character. Element, Attribute, and Object Information Now that it is clear how to create elements, attributes, and objects and how to modify and remove them in the DOM document tree, you need to know how to access the data. And you have probably already seen some, if not most, of the methods that get information from the elements, attributes, and objects within the document tree. These methods are often used together to get information from elements, and they sometimes aid in traversing the DOM. For example: var root = $('bodyContent'); /* Does the root node have childNodes? */ if (root.hasChildNodes( )) { var temp = root.firstChild.nodeType; /* Find the /nodeType/ */ switch (temp) { case 1: /* Does the /firstChild/ have an /id/ attribute? */ if (root.firstChild.hasAttribute('id')) alert(root.firstChild.getAttribute('id'); break; case 3: case 4: alert(root.firstChild.data); break; } } 112 | Chapter 5: Manipulating the DOM www.it-ebooks.info I know this code doesn’t really do anything useful; it is here to show the use of several new methods and properties. The first new method in this code is hasChildNodes( ), which returns a Boolean value that is determined by the node having any child nodes. Next is the property nodeType, which returns a numeric value representing the type of the node. I introduced these numeric values to you in Table 5-1. The first case statement in the code: case 1: /* Does the /firstChild/ have an /id/ attribute? */ if (root.firstChild.hasAttribute('id')) alert(root.firstChild.getAttribute('id'); break; introduces the hasAttribute( ) and getAttribute( ) methods. Just as you probably guessed, hasAttribute( ) returns a Boolean value based on whether the method finds an instance of the attribute being checked against. Likewise, getAttribute( ) returns the value of the attribute being asked for, and if no attribute exists, it returns an empty string. Given the following XHTML snippet, alerting $('myDiv'). childNodes[2].getAttribute('id') would yield Figure 5-4: <div id="myDiv"> <p id="para_1">First paragraph</p> <p id="para_2">Second paragraph</p> <p id="para_3">Third paragraph</p> <p id="para_4">Fourth paragraph</p> </div> Figure 5-4. The value of the id attribute for the selected node alerted to the user Finally, there is the data property, which contains the character data value of the node. The data property is valid only when checking on CDATASection, Comment, and Text node types. Table 5-4 lists the methods available for gathering information about elements, attributes, and objects. In many cases, these methods get the values of the nodes they are part of, whereas in others they are testing values against conditions. Again, this table also lists which DOM interfaces are available to utilize the listed methods. Manipulating DOM Elements, Attributes, and Objects | www.it-ebooks.info 113 Table 5-4. Informational methods Method Description Available interfaces getAttribute(attrName) Gets the value of the attribute with a name equal to the passed attrName. Element getAttributeNS(nsURI, localName) Gets the value of the attribute with a namespace URI equal to the passed nsURI and a local name equal to the passed localName. Element hasAttribute(attrName) Returns whether an attribute with a name equal to the passed attrName is specified on the element or has a default value. Element hasAttributeNS(nsURI, localName) Returns whether an attribute with a namespace URI equal to the passed nsURI and a local name equal to the passed localName is specified on the element or has a default value. Element hasAttributes( ) Returns whether the node has any attributes. All hasChildNodes( ) Returns whether the node has any child nodes. All isSupported(feature, version) Returns whether the passed feature with the passed version is supported on the node. All substringData(offset, count) Returns a substring count * 16-bits in length from the data of the node starting at the passed offset. If the sum of offset and count exceeds the length, all data from the offset is returned. CDATASection, Comment, Text Table 5-5 lists the properties associated with nodes that you can use for informational purposes. You will recognize that most of these properties were used as either the returned value or the subject of a conditional test with the methods in Table 5-4. Table 5-5. Informational properties Property Description Available interfaces data The data set for the node. CDATASection, Comment, Text length The number of nodes in the list, ranging from 0 to length –1 or The number of characters (16-bit per character) available in the data attribute. localName 114 | The local part of the qualified name of the node. Chapter 5: Manipulating the DOM www.it-ebooks.info NodeList or CDATASection, Comment, Text All Table 5-5. Informational properties (continued) Property Description Available interfaces name The name of the attribute. Attr namespaceURI The namespace URI of the node. All nodeName The name of the node. All nodeType Numeric code representing the type of the node. (See Table 5-1.) All nodeValue The value of the node. All prefix The namespace prefix of the node. All specified A value of false if: The Attr has a default value in the DTD, but no assigned value in the document. A value of true if: The Attr has an assigned value in the document. The ownerElement is null (either it was just created or it was set to null). Attr tagName The name of the element. Element value The value of the attribute. Attr Walking the DOM The methods and properties used to walk the DOM document tree are also the most-used and most-recognized of any of the methods and attributes we will see. This is simply a case of the most common tasks related to the DOM using these methods and properties to accomplish them (which is why they are so prevalent). We have already seen some of them in the examples in this chapter—methods such as getElementById( ) and getElementsByTagName( ). The methods used to traverse the DOM are as simple as any of the other methods we have seen. For example: var elements = getElementsByTagName('a'); var array = new Array( ); /* Loop through the <a> elements */ for (i = 0, il = elements.length; i < il; i++) array[i] = elements.item(i).getAttributeNode('href').value; We saw getElementsByTagName( ) already, so we will skip right to the getAttributeNode( ) method. This method returns the Attr node with a corresponding nodeName of the parameter that is passed. If there is no such node, the method returns null. Manipulating DOM Elements, Attributes, and Objects | www.it-ebooks.info 115 Table 5-6 lists the methods you can use to traverse a DOM document tree and which DOM interfaces can use them. Table 5-6. Traversal methods Method Description Available interfaces getAttributeNode(nodeName) Gets the Attr with a name equal to the passed nodeName. Element getAttributeNodeNS(nsURI, localName) Gets the Attr with a namespace URI equal to the passed nsURI and a local name equal to the passed localName. Element getElementById(idName) Gets the Element with an id equal to the passed idName. Document getElementsByTagName (tagName) Gets a NodeList containing Elements with tagNames equal to the passed tagName. Document, Element getElementsByTagNameNS (nsURI, localName) Gets a NodeList containing Elements with namespace URIs equal to the passed nsURI and local names equal to the passed localName. Document, Element item(index) Returns the node in the list with an index equal to the passed index. NodeList Properties are also available to each node for stepping through a DOM document tree element by element. Consider this snippet from an XHTML page: <div id="desserts"> <ul id="cakes"> <li id="cake1">Chocolate</li> <li id="cake2">Lemon</li> <li id="cake3">Cheesecake</li> <li id="cake4">Angelfood</li> </ul> </div> You could reference the third list element by using any of the following examples: $('cakes').childNodes[2]; $('cake2').nextSibling; $('cake4').previousSibling; $('cakes').lastChild.previousSibling; $('cake1').parentNode.childNodes[2]; $('cakes').firstChild.nextSibling.nextSibling; These are just some of the many ways you can get to that third element. Table 5-7 lists the properties you can use to traverse the DOM document tree and the DOM interfaces to which each of them belongs. 116 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-7. Traversal properties Property Description Available interfaces childNodes A NodeList containing all of the children for this node, or an empty NodeList if there are no children. All documentElement The root element of the document. Document firstChild The first child of this node or null if there is no node. All lastChild The last child of this node or null if there is no node. All nextSibling The node immediately after this node or null if there is no node. All ownerDocument The Document that the node is associated with or null if the node is a Document. All ownerElement The Element node that the attribute is attached to or null if the Attr is not being used. Attr parentNode The parent of this node. This attribute may be null if the node was just created or removed from a tree. All previousSibling The node preceding this node or null if there is no node. All Change That Style Just as methods and properties are available to developers to manipulate elements, attributes, and objects, so too are methods and properties available to manipulate the styles on a page programmatically. The methods and properties I describe here are part of the W3C’s Recommendation for the DOM. Note that Internet Explorer does not follow the W3C Recommendation for stylesheets in the DOM. I will cover this later in the chapter, in the section “What About Internet Explorer?” When stylesheets are loaded into the DOM, whether it is by a <link> or a <style> element on the page, each rule that is imported has a rule type associated with it (see Table 5-8). The DOM can then access all of the imported rules and manipulate them according to the developer’s designs. Table 5-8. CSS rule types Rule type Numeric type value Unknown @ rule 0 Normal style rule 1 @charset rule 2 Change That Style | www.it-ebooks.info 117 Table 5-8. CSS rule types (continued) Rule type Numeric type value @import rule 3 @media rule 4 @font-face rule 5 @page rule 6 As you will see in the upcoming “Style Information” section, you can check these values before attempting code that may otherwise fail: var rule = document.styleSheets[0].cssRules[0]; var URI = null; /* Is the type equal to 3? */ if (rule.type == 3) URI = rule.href; Modifying and Removing Style Modifying stylesheets that are already in the DOM makes up a large part of what was coined DHTML (Dynamic HTML) back in 1998. You can use simple methods such as setProperty( ) and removeProperty( ) to do this, as in the following: var styles = document.styleSheets[0].cssRules[0].style; styles.setProperty('color', '#ff0000'); styles.setProperty('font-size', '2em', 'important'); styles.removeProperty('font-size'); styles.removeProperty('color'); The preceding code gets a particular style from the DOM’s stylesheet (in this example, it is arbitrary), and creates rules for the style using setProperty( ) while removing rules with removeProperty( ). The setProperty( ) method takes the name of the style, the value, and an optional priority for the style. To remove a style, whether it was loaded from a CSS file or was set programmatically, simply call the removeProperty( ) method and pass it the name of the style to remove. Table 5-9 lists all the W3C standard style methods. Table 5-9. DOM stylesheet manipulation methods Method Description appendMedium(mediaType) Appends the passed mediaType to the list of media types associated with the stylesheet. deleteMedium(mediaType) Deletes the passed mediaType from the list of media types associated with the stylesheet. cssRules[]. deleteRule(index) Deletes the CSS rule at the passed index within the media block, but only if the parent rule is an @media rule. 118 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-9. DOM stylesheet manipulation methods (continued) Method Description cssRules[].insertRule(rule, index) Inserts the passed rule at the passed index within the media block, but only if the parent rule is an @media rule. If the passed index is equal to cssRules. length, the passed rule will be added at the end. styleSheets[]. deleteRule(index) Deletes the CSS rule at the passed index. styleSheets[]. insertRule(rule, index) Inserts the passed rule at the passed index within the stylesheet. If the passed index is equal to cssRules.length, the passed rule will be added at the end. removeProperty(styleName) Removes the style from the rule where the style equals the passed styleName. setProperty(styleName, styleValue, priority) Creates or replaces the style within the rule to the passed styleValue where the style is equal to the passed styleName. The priority is usually 'important' or an empty string. Of course, using these methods is not the only way to manipulate the style on an element. The CSS2Properties object was made for just this purpose. For example: $('subTitle').style.fontWeight = 'bold'; The CSS2Properties object is a convenient way to retrieve or set properties on an element. Setting an attribute using this method is just like calling the setProperty( ) method. The properties available (fontWeight, in this example) correspond to properties specified in the CSS 2.1 Recommendation. Table 5-10 lists all of these properties, along with the JavaScript-equivalent property and possible values. Table 5-10. CSS2 properties and their JavaScript equivalents CSS2.1 property name JavaScript property name Values azimuth azimuth angle | left-side | far-left | left | center-left | center | center-right | right | farright | right-side | behind | leftwards | rightwards background background background-color | background-image | background-repeat | background-attachment | background-position background-attachment backgroundAttachment scroll | fixed background-color backgroundColor color | transparent background-image backgroundImage URL | none background-position backgroundPosition top left | top center | top right | center left | center center | center right | bottom left | bottom center | bottom right | x-percent y-percent | x-pos y-pos background-repeat backgroundRepeat repeat | repeat-x | repeat-y | no-repeat Change That Style | www.it-ebooks.info 119 Table 5-10. CSS2 properties and their JavaScript equivalents (continued) CSS2.1 property name JavaScript property name Values border border border-width | border-style | border-color border-bottom borderBottom border-bottom-width | border-style | border-color border-bottom-color borderBottomColor border-color border-bottom-style borderBottomStyle border-style border-bottom-width borderBottomWidth thin | medium | thick | length border-collapse borderCollapse collapse | separate border-color borderColor color border-left borderLeft border-left-width | borderstyle | border-color border-left-color borderLeftColor border-color border-left-style borderLeftStyle border-style border-left-width borderLeftWidth thin | medium | thick | length border-right borderRight border-right-width | borderstyle | border-color border-right-color borderRightColor border-color border-right-style borderRightStyle border-style border-right-width borderRightWidth thin | medium | thick | length border-spacing borderSpacing length length border-style borderStyle none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset border-top borderTop border-top-width | borderstyle | border-color border-top-color borderTopColor border-color border-top-style borderTopStyle border-style border-top-width borderTopWidth thin | medium | thick | length border-width borderWidth thin | medium | thick | length bottom bottom auto | percent | length caption-side captionSide top | bottom | left | right clear clear left | right | both | none clip clip shape | auto color color color-rgb | color-hex | color-name 120 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-10. CSS2 properties and their JavaScript equivalents (continued) CSS2.1 property name JavaScript property name Values content content string | URL | counter(name) | counter(name, list-styletype) | counters(name, string) | counters(name, string, list-style-type) | attr(X) | open-quote | closequote | no-open-quote | noclose-quote counter-increment counterIncrement none | identifier number counter-reset counterReset none | identifier number cue cue cue-before | cue-after cue-after cueAfter none | URL cue-before cueBefore none | URL cursor cursor URL | auto | crosshair | default | pointer | move | e-resize | neresize | nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | text | wait | help direction direction ltr | rtl display display none | inline | block | listitem | run-in | compact | marker | table | inline-table | tablerow-group | table-headergroup | table-footer-group | table-row | table-columngroup | table-column | tablecell | table-caption elevation elevation angle | below | level | above | higher | lower empty-cells emptyCells show | hide float float left | right | none font font font-style | font-variant | font-weight | font-size/ line-height | font-family | caption font-family fontFamily family-name | generic-family font-size fontSize xx-small | x-small | small | medium | large | x-large | xxlarge | smaller | larger | length | percent Change That Style | www.it-ebooks.info 121 Table 5-10. CSS2 properties and their JavaScript equivalents (continued) CSS2.1 property name JavaScript property name Values font-size-adjust fontSizeAdjust none | number font-stretch fontStretch normal | wider | narrower | ultra-condensed | extracondensed | condensed | semicondensed | semi-expanded | expanded | extra-expanded | ultra-expanded font-style fontStyle normal | italic | oblique font-variant fontVariant normal | small-caps font-weight fontWeight normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 height height auto | length | percent left left auto | length | percent letter-spacing letterSpacing normal | length line-height lineHeight normal | number | length | percent list-style listStyle list-style-type | list-styleposition | list-style-image list-style-image listStyleImage none | URL list-style-position listStylePosition inside | outside list-style-type listStyleType none | disc | circle | square | decimal | decimal-leadingzero | lower-roman | upperroman | lower-alpha | upperalpha | lower-greek | lowerlatin | upper-latin | hebrew | armenian | georgian | cjkideographic | hiragana | katakana | hiragana-iroha | katakana-iroha margin margin margin-top | margin-right | margin-bottom | margin-left margin-bottom marginBottom auto | length | percent margin-left marginLeft auto | length | percent margin-right marginRight auto | length | percent margin-top marginTop auto | length | percent marker-offset markerOffset auto | length marks marks none | crop | cross max-height maxHeight none | length | percent max-width maxWidth none | length | percent 122 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-10. CSS2 properties and their JavaScript equivalents (continued) CSS2.1 property name JavaScript property name Values min-height minHeight length | percent min-width minWidth length | percent orphans orphans number outline outline outline-color | outlinestyle | outline-width outline-color outlineColor color | invert outline-style outlineStyle none | dotted | dashed | solid | double | groove | ridge | inset | outset outline-width outlineWidth thin | medium | thick | length overflow overflow visible | hidden | scroll | auto padding padding padding-top | padding-right | padding-bottom | padding-left padding-bottom paddingBottom length | percent padding-left paddingLeft length | percent padding-right paddingRight length | percent padding-top paddingTop length | percent page page auto | identifier page-break-after pageBreakAfter auto | always | avoid | left | right page-break-before pageBreakBefore auto | always | avoid | left | right page-break-inside pageBreakInside auto | avoid pause pause pause-before | pause-after pause-after pauseAfter time | percent pause-before pauseBefore time | percent pitch pitch frequency | x-low | low | medium | high | x-high pitch-range pitchRange number play-during playDuring auto | none | URL | mix | repeat position position static | relative | absolute | fixed quotes quotes none | identifier number richness richness number right right auto | length | percent size size auto | portrait | landscape speak speak normal | none | spell-out speak-header speakHeader always | once Change That Style | www.it-ebooks.info 123 Table 5-10. CSS2 properties and their JavaScript equivalents (continued) CSS2.1 property name JavaScript property name Values speak-numeral speakNumeral digits | continuous speak-punctuation speakPunctuation none | code speech-rate speechRate number | x-slow | slow | medium | fast | x-fast | faster | slower stress stress number table-layout tableLayout auto | fixed text-align textAlign left | right | center | justify text-decoration textDecoration none | underline | overline | line-through | blink text-indent textIndent length | percent text-shadow textShadow none | color | length text-transform textTransform none | capitalize | uppercase | lowercase top top auto | length | percent unicode-bidi unicodeBidi normal | embed | bidi-override vertical-align verticalAlign baseline | sub | super | top | text-top | middle | bottom | text-bottom | length | percent visibility visibility visible | hidden | collapse voice-family voiceFamily specific-voice | generic-voice volume volume number | percent | silent | x-soft | soft | medium | loud | x-loud white-space whiteSpace normal | pre | nowrap widows widows number width width auto | length | percent word-spacing wordSpacing normal | length z-index zIndex auto | number Suppose we have the following code: var styles = document.styleSheets[0].cssRules[0].style; styles.setProperty('border', '2px solid #000000'); styles.setProperty('background-color', '#ff0000'); styles.setProperty('font-size', '2em'); styles.setProperty('z-index', 10); styles = document.styleSheets[0].cssRules[1].style; styles.setProperty('background-color', '#0000ff'); styles.setProperty('font-style', 'italic'); This gives us something like Figure 5-5. 124 | Chapter 5: Manipulating the DOM www.it-ebooks.info Figure 5-5. A page manipulated with CSS rules Implementing the following code will change the page to something like Figure 5-6: var styles = document.styleSheets[0].cssRules[0].style; styles.removeProperty('z-index'); styles.addProperty('top', '5px'); styles.addProperty('border-style', 'dashed'); styles = document.styleSheets[0].cssRules[1].style; styles.removeProperty('font-style'); styles.addProperty('background-color', '#00ff00'); Figure 5-6. The page changed programmatically When you’re using the CSS shorthand properties, you should break down the shorthand into the component longhand when appropriate. When getting the values, the shortest form equivalent to the declarations made in the ruleset should be returned. If no shorthand can be added, it should contain an empty string. Change That Style | www.it-ebooks.info 125 For example, this should not be returned: bold normal normal 12pt "Courier New", monospace when this will do: bold 12pt "Courier New", monospace The normals are default values, and they are implied in the longhand properties should they be queried. Style Information Only a few methods are available for getting to the information in a stylesheet or rule. These methods function in basically the same way. Take the following, for example: var styles = document.styleSheets[0].cssRules[0].style; /* Does the style sheet have a color property priority? */ if (styles.getPropertyPriority('color')) alert(styles.cssText); else styles.setProperty('color', styles.getPropertyValue('color'), 'important'); This example checks whether the color style name has been given a priority using the getPropertyPriority( ) method. If it has, it alerts the cssText of the style; otherwise, it sets the property to have a priority of 'important', using its existing value (retrieved using the getPropertyValue( ) method) in the setProperty( ) method. Table 5-11 describes all the methods used to gather information using the CSS DOM. Table 5-11. Informational DOM stylesheet methods Method Description getPropertyPriority(styleName) Gets the priority of the style with a name equal to the passed styleName. getPropertyValue(styleName) Gets the value of the style with a name equal to the passed styleName. media.item(index) Returns the name of the media type at the index equal to the passed index. style.item(index} Returns the style at the index equal to the passed index, within the associated rule. Along with the methods listed in Table 5-11 are properties you can use in both a read and a write manner (see Table 5-12). Reading these properties gives you the information on a stylesheet or rule, while utilizing the property to modify a stylesheet or rule can offer the benefit of direct access that methods do not give. 126 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-12. Informational DOM stylesheet properties Property Description cssRules[].cssText The text that represents the given rule, including the selector and styles. style.cssText The text that represents the style part of the rule. disabled The Boolean value indicating whether the associated stylesheet is disabled. encoding The encoding for the rule, if the rule is an @charset rule. cssRules[].href The URL for the rule, if the rule is an @import rule. styleSheets.href The URL of the stylesheet. media.length The browser’s interpretation of the number of media types to which the associated stylesheet applies. style.length The browser’s interpretation of the number of styles inside the associated rule. mediaText The textual representation of the media types to which the stylesheet applies. nameOfStyle The textual representation of the named style value. cssRules[].selectorText The textual representation of the selector part of the rule, but only if it is a normal rule or an @page rule. rules[].selectorText The textual representation of the selector part of the rule. title The title attribute of the style or link element that creates the associated stylesheet. cssRules[].type The numerical representation of the rule type (see Table 5-8, earlier in this chapter). styleSheets[].type The type attribute of the style or link element that creates the associated stylesheet. An example of using a property for writing follows: document.styleSheets[0].cssRules[5].style.cssText = 'color: #ff0000; ' + 'font-size: 2em !important;'; The preceding line of code takes the place of these lines: var styles = document.styleSheets[0].cssRules[5].style; styles.setProperty('color', '#ff0000'); styles.setProperty('font-size', '2em', 'important'); As I said at the beginning of this section, these methods and properties are part of the W3C Recommendation. So, how do things differ with Internet Explorer? What About Internet Explorer? Internet Explorer 6.0 and earlier do not support many of the DOM 2 stylesheet methods or properties. Their alternatives are not as complete, but they do handle basic manipulation of stylesheet rules. The stylesheet collection itself is the same as all the other standards-compliant browsers, and it works in basically the same way. Change That Style | www.it-ebooks.info 127 The first difference is in referencing the stylesheet’s creator. For standards-compliant browsers the property is ownerNode, but in Internet Explorer the property is owningElement, as in this example: var sheet = document.styleSheets[0]; var element = ((sheet.ownerNode) ? sheet.ownerNode : sheet.owningElement); Internet Explorer does have the same collection as with standards-compliant browsers—the disabled, href, title, and type properties all work in the same manner—but the media property is different. With standards-compliant browsers the property is an object, but Internet Explorer treats it as a string. For this reason, if you wish to alter it, you must alter the string. Internet Explorer has no methods to add, remove, or list media types because it is not an object: var sheet = document.styleSheets[0]; /* Does the media type have a type of /string/? */ if (typeof sheet.media == 'string') sheet.media = 'screen'; else sheet.media.mediaText = 'screen'; The preceding code checks to see what browser is being used so that it knows what property to set. If, however, you are coding for Internet Explorer for the Mac, trying to set the media property to a string will throw an error. Therefore, the code will need to have an additional check to work properly: var sheet = document.styleSheets[0]; /* Does the media type have a type of /string/? */ if (typeof sheet.media == 'string') try { sheet.media = 'screen' } catch(ex) {}; else sheet.media.mediaText = 'screen'; You must do this for Internet Explorer for the Mac because the media property is read-only in this browser. The styleSheet property in Internet Explorer for Windows works in the same way as the sheet property for standards-compliant browsers. This property, however, is not available in Internet Explorer for the Mac. As standards-compliant browsers have the cssRules collection, so too does Internet Explorer provide the rules collection. The methods and properties available to Internet Explorer are not compatible with those of the standards-compliant browsers. It is not possible to index the same rule in each collection, as @charset, @import, @media, @font-face, and @page rules are not included in the rules collection. @media blocks are included in the rules collection of the stylesheet in Internet Explorer for Windows, but in Internet Explorer for the Mac they are ignored, as they are not available to the DOM. For Internet Explorer in Windows, you cannot add new rules into @media blocks. 128 | Chapter 5: Manipulating the DOM www.it-ebooks.info A cssText property in Internet Explorer is available directly in the stylesheet. This includes any @media blocks in a Windows environment; however, this property can create editing difficulties because some sort of pattern-matching is required. Internet Explorer for the Mac has both the rules and the cssRules collections available in the DOM, but they are both treated the Internet Explorer way. Because of this, you should check the rules collection first, and if it’s available, you should use it before you consider the cssRules collection: var sheet = document.styleSheet[0]; var rule = ((sheet.rules) ? Sheet.rules[4] : sheet.cssRules[5]); Internet Explorer provides a removeRule( ) method that functions exactly as the deleteRule( ) method does, and it provides an addRule( ) method. But this method does not function like the insertRule( ) method does: /* Is there an insertRule( ) method available? */ if (sheet.insertRule) sheet.insertRule('div#special { font-size: 1.5em; color: #f00; }', sheet.cssRules.length); /* Is there an addRule( ) method available? */ else if (sheet.addRule) sheet.addRule('div#special', 'font-size: 1.5em; color: #f00;'); This section just scratched the surface regarding the differences between Internet Explorer and standards-compliant browsers. However, it is beyond the scope of this book to discuss all of the differences. You can find more information on how Internet Explorer handles stylesheets on MSDN, at http://msdn.microsoft.com/workshop/ author/css/css_node_entry.asp. Events in the DOM The ability to manipulate events on the client is central to Web 2.0 and Ajax web applications. Whether it is a user moving the mouse over an object on the application, or typing some text, or clicking on a button, the events that fire from these actions are paramount to having any client-application interaction. All client events are broken out by Event modules. These modules are as follows: HTMLEvent module abort, blur, change, error, focus, load, reset, resize, scroll, select, submit, unload UIEvent module DOMActivate, DOMFocusIn, DOMFocusOut, keydown, keypress, keyup MouseEvent module click, mousedown, mousemove, mouseout, mouseover, mouseup Events in the DOM | www.it-ebooks.info 129 MutationEvent module DOMAttrModified, DOMNodeInserted, DOMNodeRemoved, DOMCharacterDataModified, DOMNodeInsertedIntoDocument, DOMNodeRemovedFromDocument, DOMSubtreeModified Nonstandard Event module Nonstandard events that do not really fit in the other modules Before you can use any of these events, you must create and initialize them. The DOM enables developers to fully manipulate an event, no matter what it is. We will look at this next. Creating Events You can create most events by simply attaching the function or JavaScript action you want to fire directly to the event. Consider these examples: <a href="/favorites/" onclick="close_all( );">My Favorites</a> <input id="username" name="nptUsername" type="text" value="" onblur="check_user(this);" /> <body onload="initialize( );"> If, however, you need to synthesize an event from within the application code itself, the DOM provides the createEvent( ) method. For example: var evt = document.createEvent('MouseEvents'); If the browser supports an eventType parameter that is passed to the method, the method will return a new Event of the type passed. After the event is created, you must call the specific Event initiation method to complete the creation. When the browser does not recognize the eventType passed, you can still dispatch it within the client if you implement your own Event initialization method. Initializing, Firing, Adding, and Removing Events Once a new event has been created, it is ready to be initialized and dispatched to the client application. Four methods are available for initializing an Event, each for a specific eventType, as shown in Table 5-13. Table 5-13. Event initialization methods Method Description InitEvent(eventType, bubbles, cancelable) Initializes the event as a generic event, without defining additional properties. InitMouseEvent(eventType, bubbles, cancelable, window, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) Initializes a MouseEvent event as a mouse event. 130 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-13. Event initialization methods (continued) Method Description InitMutationEvent(eventType, bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange) Initializes a MutationEvent event as a mutation event. InitUIEvent(eventType, bubbles, cancelable, window, detail) Initializes the event as a generic UI event, without defining additional properties, and is available for MouseEvent and UIEvent events. This example shows the creation and initialization of a MouseEvent event: var evt = document.createEvent('MouseEvents'); evt.initMouseEvent('click', true, true, window, 20, 200, 26, 208, false, false, true, false, 0, null); $('nptSpecial').dispatchEvent(evt); You will notice that after the initMouseEvent( ) method, a call to the dispatchEvent( ) method is required to actually set the new Event within the client. The dispatchEvent( ) method takes the form dispatchEvent(eventObject). The title of this section may be a bit misleading. The adding and removing actually do not pertain to the event itself; they pertain to event listeners. Adding an event listener to an element is fairly simple. You use the addEventListener( ) method to add a listener to a particular event type. For example: var myElement = $('myDiv'); myElement.addEventListener('click', function(e) { // do something }, true); The addEventListener( ) method takes for parameters the event to listen to, the function to fire when the event occurs, and a phase which can be true for capture and false for bubble. Similarly, to remove an event listener for an object, a developer would use the removeEventListener( ) method. This method takes for parameters the event to stop listening to and a phase that can be true for capture and false for bubble, as follows: myElement.removeEventListener('click', arguments.callee, false); Event Information All Event objects contain a number of methods and properties that you can use to obtain information about the event. For example: var link = $('firstLink'); link.addEventListener('click', function(e) { /* Is the event cancelable? */ if (e.cancelable) e.preventDefault( ); launchWindow( ); }, false); Events in the DOM | www.it-ebooks.info 131 This event listener checks whether the event can be canceled, and if it can, it calls the method preventDefault( ), which prevents the cancellation of the event. Then a function that launches a window is called. Table 5-14 lists the methods contained in the Event, EventCapturer, and EventListener objects, along with descriptions and the object to which each belongs. Table 5-14. Event methods Method Description Object captureEvent(eventType) Captures the particular type of event that is passed in eventType. EventCapturer handleEvent(event) Handles the event whenever it occurs for the EventListener to which it was registered. EventListener preventDefault( ) Prevents any default action from firing as long as the Event is cancelable. Event releaseEvent(eventType) Stops capturing the particular type of event that is passed in eventType. EventCapturer routeEvent( ) Continues the event’s flow to additional event handlers, and if none is present, to the target of the event. EventCapturer stopPropagation( ) Stops any further propagation of an event during any phase of the event. Event Table 5-15 lists the properties contained in an Event object, along with a description and the eventType to which each belongs. Table 5-15. Event properties Property Description Event type altKey The Boolean indicator as to whether the Alt key was pressed when the event was fired. MouseEvent attrChange The indicator of what type of change was triggered with a DOMAttrModified event. Values are: • 1 = Modification • 2 = Addition • 3 = Removal MutationEvent attrName The string of the changed Attr node in a DOMAttrModified event. MutationEvent bubbles The Boolean indicator as to whether the event is a bubbling event. All button The button that was pressed or released when the mouse button changed state. The values for the button can be: • 0 = Left mouse button • 1 = Middle mouse button • 2 = Right mouse button For left-handed mice, the values are reversed. MouseEvent 132 | Chapter 5: Manipulating the DOM www.it-ebooks.info Table 5-15. Event properties (continued) Property Description Event type cancelable The Boolean indicator as to whether the event can have its default action prevented. All clientX The horizontal coordinate at which the event happened, relative to the client area. MouseEvent clientY The vertical coordinate at which the event happened, relative to the client area. MouseEvent ctrlKey The Boolean indicator as to whether the Ctrl key was pressed when the event was fired. MouseEvent currentTarget The reference to the element currently processing the event. All detail The detail information about the Event, depending on its type. UIEvent, MouseEvent eventPhase The phase of the event currently being processed. Phases are: • 0 = A manually created event object that has yet to be fired • 1 = Capture phase • 2 = Bubble phase on the target element • 3 = During the bubbling phase on the target’s ancestors All metaKey The Boolean indicator as to whether the Meta key was pressed when the event was fired. This is the Windows key for Windows and the Apple/Command key for Macs. MouseEvent newValue The new value of the node after a mutation event. MutationEvent prevValue The previous value of the node before a mutation event. MutationEvent relatedNode The secondary node related to the mutation event. MutationEvent relatedTarget The secondary event target related to the mouse event. MouseEvent screenX The horizontal coordinate at which the event happened, relative to the origin of the screen coordinate system. MouseEvent screenY The vertical coordinate at which the event happened, relative to the origin of the screen coordinate system. MouseEvent shiftKey The Boolean indicator as to whether the Shift key was pressed when the event was fired. MouseEvent target The target to which the event was originally dispatched. All timeStamp The time in milliseconds at which the Event was created. All type The XML name of the event. All view The view from which the event was generated. UIEvent, MouseEvent What About Internet Explorer? Part II Internet Explorer simply does not provide any of the DOM 2 Events methods, and there are only a couple of the same properties of the Event object. Versions starting at 5 provide an event system that is similar in nature, but is more limited in functionality. Events in the DOM | www.it-ebooks.info 133 You will recall that for standards-compliant browsers, you use the methods createEvent( ), init*Event( ), and dispatchEvent( ) to successfully create, initialize, and dispatch an event to an element, respectively. In Internet Explorer, similar methods are available, but initializing an Event object is a little cruder, as shown in Example 5-2. Example 5-2. Initializing an Event object for Internet Explorer var special = $('nptSpecial'); /* Does the document have a /createEvent( )/ method? */ if (document.createEvent) { var evt = document.createEvent('MouseEvents'); evt.initMouseEvent('click', true, true, window, 0, 20, 200, 26, 208, false, false, true, false, 0, null); special.dispatchEvent(evt); /* Does the document have a /createEventObject( )/ method? */ } else if (document.createEventObject) { var evt = document.createEventObject( ); evt.detail = 0; evt.screenX = 20; evt.screenY = 200; evt.clientX = 26; evt.clientY = 208; evt.ctrlKey = false; evt.altKey = false; evt.shiftKey = true; evt.metaKey = false; evt.button = 0; evt.relatedTarget = null; special.fireEvent('onclick', evt);' } The createEventObject( ) method creates an empty Event object, unless an existing Event object is passed to it. In this case, the passed object is used as a template when creating the new object. Instead of calling an init*Event( ) method, you must set each property of the Event object individually. Finally, instead of calling the dispatchEvent( ) method, you call the Internet Explorer fireEvent( ) method. This method takes the event type and the event object itself. You cannot find in Internet Explorer the addEventListener( ) and removeEventListener( ) methods that are used in standards-compliant browsers. Instead, you use the attachEvent( ) and removeEvent( ) methods. They function in almost the same way, as shown here: function handleMyEvent(e) { // do something here } 134 | Chapter 5: Manipulating the DOM www.it-ebooks.info var special = $('nptSpecial'); /* Is there an /addEventListener( )/ method? */ if (special.addEventListener) special.addEventListener('click', handleMyEvent, false); /* Is there an /attachEvent( )/ method? */ else if (special.attachEvent) special.attachEvent('onclick', handleEvent); Internet Explorer does not support canceling of an event; it supports only bubbling. Therefore, you cannot call the stopPropagation( ) method. Instead, the cancelBubble property is provided: /* Is there a /stopPropagation( )/ method? */ if (e.stopPropagation) e.stopPropagation( ); else e.cancelBubble = true; Internet Explorer also does not support stopping default actions, so the preventDefault( ) method will not work. Internet Explorer instead provides the returnValue property: /* Is there a /preventDefault( )/ method? */ if (e.preventDefault) e.preventDefault( ); else e.returnValue = false; DOM Stuff for Tables XHTML tables have methods and properties that the other XHTML elements do not have. These special methods and properties are specifically designed for manipulating parts of the table in a more precise manner. To use these methods and properties, however, you must think of a table in the full XHTML specification. An XHTML table contains a <caption>, a <thead>, a <tfoot>, and any number of tbodies: caption References the <caption> of a table thead References the <thead> of a table, if there is one tfoot References the <tfoot> of a table, if there is one tbodies Reference a collection with one entry for every <tbody> that exists for the table (there is usually just one <tbody>, table.tbodies[0]) DOM Stuff for Tables www.it-ebooks.info | 135 A rows collection corresponds to all the rows in each <thead>, <tfoot>, and <tbody> node. Each row has a cells collection, which contains every <td> or <th> element in that given row. Every cell contains all of the normal DOM methods and properties associated with an XHTML element. Consider the following table, which is displayed in Figure 5-7: <table id="oreillyBooks" summary="Some O'Reilly books on Ajax"> <caption>O'Reilly Ajax Books</caption> <thead> <tr> <th>Title</th> <th>Author(s)</th> <th>Published Date</th> </tr> </thead> <tfoot> <tr> <td colspan="3">Ajax books from oreilly.com</td> </tr> </tfoot> <tbody> <tr> <td>Ajax Design Patterns</td> <td>Michael Mahemoff</td> <td>June 2006</td> </tr> <tr> <td>Ajax Hacks</td> <td>Bruce W. Perry</td> <td>March 2006</td> </tr> <tr> <td>Head Rush Ajax</td> <td>Brett McLaughlin</td> <td>March 2006</td> </tr> <tr> <td>Programming Atlas: Rough Cuts</td> <td>Christian Wenz</td> <td>March 2006</td> </tr> </tbody> </table> Using the DOM properties to reference elements in the table, here are some examples of how to reference table nodes: var table = $('oreillyBooks'); x x x x 136 | = = = = table.tBodies[0].rows[0].cells[1].firstChild.value; // Michael Mahemoff table.tHead.rows[0].cells[2].firstChild.value; // Published Date table.tBodies[0].rows[2].cells[0].firstChild.value; // Ajax Design Patterns table.tFoot.rows[0].cells[0].firstChild.value; // Ajax books from oreilly.com Chapter 5: Manipulating the DOM www.it-ebooks.info Figure 5-7. An XHTML table to be manipulated by the DOM Tables, along with their child elements, have methods that you can use for creating, inserting, and deleting, as shown in Table 5-16. Table 5-16. DOM table methods Method Description Element createCaption( ) Creates a new table caption object, or returns an existing one. HTMLTableElement createTFoot( ) Creates a table footer row, or returns an existing one. HTMLTableElement createTHead( ) Creates a table header row, or returns an existing one. HTMLTableElement deleteCaption( ) Deletes the table caption, if one exists. HTMLTableElement deleteCell(index) Deletes a cell from the current row at the passed index. If the index is –1, the last cell in the row is deleted. HTMLTableRowElement deleteRow(index) Deletes a table row found at the passed index. If the index is –1, the last row in the table is deleted. HTMLTableElement, HTMLTableSectionElement deleteTFoot( ) Deletes the footer from the table, if one exists. HTMLTableElement deleteTHead( ) Deletes the header from the table, if one exists. HTMLTableElement insertCell(index) Inserts an empty cell into this row at the passed index. If the index is –1 or is equal to the number of cells, the new cell is appended. HTMLTableRowElement insertRow(index) Inserts a table row found at the passed index. If the index is –1 or is equal to the number of rows, the new row is appended to the last row. HTMLTableElement, HTMLTableSectionElement DOM Stuff for Tables www.it-ebooks.info | 137 The methods are easy to use, as the descriptions in the table of methods show. The following is an example of the createCaption( ) method: var x = $('myTable').createCaption( ); x.appendChild(document.createTextNode('This is my table caption')); Likewise, it’s easy to use methods such as insertRow( ) and insertCell( ), as the following illustrates: var var var var x a b c = = = = $('myTable').insertRow(2); x.insertCell(0); x.insertCell(1); x.insertCell(2); a.appendChild(document.createTextNode('New data in column one')); b.appendChild(document.createTextNode('New data in column two')); c.appendChild(document.createTextNode('New data in column three')); Using the table of O’Reilly books that produced Figure 5-7, this code would produce Figure 5-8. That’s all there really is to manipulating tables using DOM methods and properties. Of course, most of the normal DOM properties and methods could accomplish the same things, but the DOM table methods and properties make things simpler. Figure 5-8. The DOM manipulated table Is innerHTML Evil? The innerHTML property has caused much debate since Microsoft introduced it for Internet Explorer all those years ago. There are usually only two camps on this issue: those that support it wholeheartedly and those that believe it is evil. So, the question that needs to be answered is “Is innerHTML evil?” 138 | Chapter 5: Manipulating the DOM www.it-ebooks.info First, a little bit about innerHTML. innerHTML allows a developer to create a string of XHTML and set the innerHTML property equal to that string. The browser is then tasked with translating all of the XHTML elements to create a DOM document tree out of the string. For example: var string = '<div id="myDiv"><p>Paragraph One</p><p>Paragraph <b>Two</b></p></div>'; $('contentBody').innerHTML = string; Now, consider the DOM methods required to create the same string using only W3C standards. Here is an example: /* Create some new elements? */ var outerDiv = document.createElement('div'); var para1 = document.createElement('p'); var para2 = document.createElement('p'); var bold = document.createElement('b'); /* Create the attributes and nodes */ outerDiv.setAttribute('id', 'myDiv'); para1.appendChild(document.createTextNode('Paragraph One')); para2.appendChild(document.createTextNode('Paragraph ')); bold.appendChild(document.createTextNode('Two')); para2.appendChild(bold); outerDiv.appendChild(para1); outerDiv.appendChild(para2); /* Append the new <div> element to the /contentBody/ element */ $('contentBody').appendChild(outerDiv); Look at how many more lines it took to build this same bit of code as XML! So, why doesn’t everyone just switch to innerHTML and forget about all of those DOM methods? Let’s examine some of the pros and cons of innerHTML. innerHTML does not require nearly as many lines to create a large string of XHTML as the W3C standards do. This can be an advantage when a developer is trying to keep the size of her JavaScript files as small as possible. Also, innerHTML is well supported by all of the major browser makers. It is kind of amusing that innerHTML is, in fact, better supported than some of the W3C standard methods and properties. When it comes to creating Ajax pages, innerHTML can come in very handy. It is extremely easy to take the responseText from an XMLHttpRequest object and set some element’s innerHTML to this response. Isn’t the whole point of Ajax to refresh the content of part of the page as quickly as possible? This is yet another advantage of using innerHTML—it is faster than building the content using the DOM methods and properties. On the other hand, innerHTML is a proprietary property. It may be widely supported now, but that does not mean it will be in the future. Unless innerHTML becomes a W3C standard, there is no way anyone can know whether future browsers will support it. With that aside, using a proprietary property is not so bad. The XMLHttpRequest object that I introduced in Chapter 4 is also proprietary. Is innerHTML Evil? | www.it-ebooks.info 139 Another problem with innerHTML is that whether the string passed to the innerHTML property contains valid and well-formed markup or not, it is still shoved into the property. It may be more time-consuming, but it is safer to use methods such as createElement( ), createTextNode( ), and appendChild( ). Plus, MSDN’s definition of innerHTML is that the property is read-only for the following elements: <col>, <colgroup>, <frameset>, <html>, <style>, <table>, <tbody>, <tfoot>, <thead>, <title>, and <tr>. The problem becomes readily apparent. The innerHTML property does not work when you’re trying to add content from within any of these elements. This makes creating dynamic content within tables an impossible task using innerHTML. Furthermore, innerHTML continues to have problems that are documented in the editorial “innerHTML Gotchas,” which you can find at http:// www.ajaxian.com/archives/innerhtml-gotchas. It will always be up to the developer whether to use innerHTML or W3C standard methods and properties. For my money, I say why not both? Sometimes it makes sense to use innerHTML—when speed is a factor, for example. Other times—such as when data needs to be dynamically appended to a table—using DOM methods and properties is better. So, to answer the question of whether innerHTML is evil: sometimes it is and sometimes it is not. 140 | Chapter 5: Manipulating the DOM www.it-ebooks.info Chapter 6 CHAPTER 6 Designing Ajax Interfaces 6 At this point, we have examined the basics of what it takes to create Ajax web applications—the standards that are used, the design patterns to follow, the server-side languages available, the frameworks, and the Document Object Model (DOM) methods and properties used to fetch data and manipulate the DOM. However, we have not discussed how to design the interface to your application. Just as important as the tools that go into building an application are the components that make up the user interface. The interface is how the end user (your main focus) interacts with and uses the application you have designed. Unless you design the interface with the user in mind from the beginning, parts of your application may be cumbersome to navigate, agitating people and discouraging them from using your Ajax web application in the future. Fortunately, there are ways to prevent this from happening. In addition to the general rules we created more than 15 years ago for desktop applications, other suggestions and guidelines were created specifically for web interfaces. With these as your guide, you should have no problems designing an interface that people find useful and enjoy interacting with. Designing Ajax interfaces covers four distinct yet related components: usability, functionality, visualization, and accessibility. By considering each component and the nuances they bring, you will design and create an application that users find intuitive, user-friendly, and easy to navigate. Usability The usability of an Ajax web application refers mainly to how easy the application is to navigate and manipulate, and how intuitive it is to the end user. If an application is usable, it is: • Structured • Simple • Tolerant 141 www.it-ebooks.info • Reusable • Receptive An Ajax web application that is structured is organized in meaningful and useful ways. Related parts of a page are placed together, and unrelated parts are separated based on a clear model that the user recognizes. A structured application results in more logical site navigation. An Ajax web application should be simple to use. Common tasks should be easy to accomplish. Communication between the application and the user should be basic in nature, avoiding technical and complicated language or jargon. When shortcuts are provided, they should be easy to follow so that the user understands where he is navigating. Statistically speaking, designers and programmers (those who typically develop web applications) have better-than-average spatial conceptualization skills. To put this another way, it’s often easy for web application developers to know where they are in an application without needing additional support. However, an application that a developer finds easy to navigate may not be easy for regular users to navigate. Tolerant Ajax applications are flexible in how they handle mistakes and abuse. A flexible application allows for easy cancellation and backtracking of user submissions and navigation. Furthermore, it gracefully handles incorrect user input, and does not break or produce errors from such cases. Most important, tolerant web applications make every effort to prevent most errors from reaching the user, and instead make reasonable assumptions about user intent and act accordingly. A reusable web application reduces the amount of information the user needs to remember and rethink each time she reacts to a page or control on the application. Consistent navigation tools, site structure, naming conventions, and so on allow the user to navigate the application without stopping to think about every action. Being reusable boils down to being consistent. An Ajax web application should be receptive to user feedback—whether it takes the form of criticism, suggestions, or praise. The developer must accept that in order to make an application usable for the end user, she must be receptive to whatever comments that user may make about the application. A receptive developer strives for quality in the application, thinks about the user, and designs with that user in mind. What Can Go Wrong? It is always good to learn from your mistakes, but a better lesson is to learn from the mistakes of others and not repeat them. So many things can turn an Ajax web application that has a good concept into a bad reality. Understanding the common mistakes designers and developers make should reduce your chances of making the same ones. Remember: whatever can go wrong, will go wrong. 142 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Bloat, bloat, bloat The number one reason a person leaves a web site is that it takes too long for a page to load. The number one reason a page loads too slowly is bloat—too many images, images that are too large, too much content, and so on. Web designers are notorious for designing layouts that are graphics-intensive or have a lot of Flash content. These layouts take time to download to the client, and users are generally not that patient. Figure 6-1, the main page for ESPN (http://www.espn.com/), is a good example of bloat. Figure 6-1. The main page for ESPN.com This is a good example of bloat for several reasons. First, the page is 134 KB in size— that is large just for the site content and its code. The page also has 42 images and 11 embedded objects, all of which the client needs to download. These media files are an additional 317 KB in size. The total size for this site—a site built for the general public to view—is 451 KB. To put this size in context, if I were a user still connecting to the Internet on my 56 Kbps modem, a site this large would take one minute and two seconds to download. Even if I were one of the millions of users now enjoying broadband it would still generally take more than 10 seconds to download the entire contents of the main page. Usability | www.it-ebooks.info 143 As an Ajax web application developer, you have to keep in mind that users do not like to wait. Studies from 1998–2002 showed that more than 80 percent of users abandon a site after 8–10 seconds.* User connection speeds have increased since 2002, and I can only assume that users’ patience levels have decreased concurrently. Poor focus When a web site or web application is dedicated to showcasing a single item, it needs to make sure it succeeds at that task. A site is poorly designed if it has no focus or provides no information. Such sites are generally trying to look “cool” without providing what is helpful—information. A good example of a site with no focus appears in Figure 6-2, which shows Amp’s main page in 2006, found on the Wayback Machine at http://web.archive.org/web/ 20060616160639/http://www.1amp.com/. Figure 6-2. Amp’s main page, which didn’t tell a visitor much of anything What is Amp? What does “Experienced Passion” mean? Is the site’s focus the white background that constitutes the entire page except for the navigation bar? You know nothing about this site until you dig further by following the links on the page. The point of a business site’s main page is to grab your attention with a central focus: We do web design. Our specialty is architectural engineering. We sell fluffy animals. Regardless of the focus, it should be readily apparent. * Chris Roast, “Designing for Delay in Interactive Information Retrieval,” Interacting with Computers 10 (1998): 87–104. “Need for Speed I,” Zona Research, Zona Market Bulletin (1999). “Need for Speed II,” Zona Research, Zona Market Bulletin (2001). Jonathan Klein, Youngme Moon, and Rosalind W. Picard, “This Computer Responds to User Frustration: Theory, Design, and Results,” Interacting with Computers 14 (2) (2002): 119–140. 144 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Obscurity This can cover two different problems you do not want for your application. The first obscurity occurs when you use abbreviations and acronyms without explaining what they mean. This is especially heinous when the acronym or abbreviation is part of the site’s navigation (e.g., in tabs, navigation menus, etc.). Another site obscurity is icons whose purpose is unclear because they have no explanatory text or titles. In addition to these problems, you also want to avoid site structure obscurity. A site that breaks every normal layout convention makes it much harder for the average end user to navigate. Figure 6-3 shows a good example of an obscure site structure, BVS Performance Center for Banks (http://www.bvsinc.com/buick_nash/lobby. asp?goBackTo=bankDoor). Figure 6-3. An obscure site structure from BVS This site probably sounded like a really cool concept when it was first pitched and put on paper, but after implementation—well, it certainly will not win any usability awards. This is a perfect example of a site whose layout is obscure simply because it follows no structure to govern the layout, making it difficult to navigate. In fact, when you first arrive at the site, you may end up being overwhelmed and not know where to even begin. Usability | www.it-ebooks.info 145 Lack of navigation All good web applications should have navigation controls that are easy to find and use; otherwise, they will quickly frustrate end users. It’s easy for you to assume that your users have the same intimate knowledge of your application that you do. If a user has difficulty finding the information she wants, she will not keep hunting for it and will instead depart. As you design your Ajax web application, you should consider how you’re structuring the information and how to navigate through it. This navigation should be clear. Providing site maps or breadcrumbs can help users navigate an application. To better understand what I’m talking about, look at Figure 6-4, which shows the main page for Dalton Mailing Service, Inc. (http://www.daltonmailingservice.com/). Figure 6-4. The Dalton Mailing Service web site, which has no readily apparent navigation Not until you put your mouse over one of the round buttons do you know what the button does or where it will take you. Never use icons that are obscure or that mean something only to you, the developer. Many icons have been developed over time that are almost universally recognized for what they are. We all know an envelope signifies email, just as a left-pointing arrow means back and a right-pointing arrow means forward. 146 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Expecting too much from your end users A good Ajax web application development maxim is “Never overestimate the sophistication of the end user.” Expecting that your users will have the technologies required for your application or site to work is never a good idea. Generally speaking, if a media type does not bring any real value to the page, do not use it. Flash is great; it can bring a page to life. But is it necessary? The same goes for any other type of media that requires the end user to have a plug-in installed on his client so that he can view the page correctly. If the user does not have a necessary plug-in already installed, something like Figure 6-5 will typically appear on the page, encouraging the user to download whatever software is necessary. Figure 6-5. What a user might see when a plug-in is needed Plenty of end users are still not very technologically capable, and having to install some software just to view your application can be daunting for them. Likewise, a site requiring a good understanding of how operating system software works—how to drag and drop, for example—could spell trouble for many users who are not accustomed to such functions. Of course, sometimes it is acceptable to have sophisticated software running on your application. For example, shopping carts could put drag-and-drop technology to good use. Just make sure your audience can handle what you give them. Web reading style How users read online material is different from how they read books. Books (as well as newspapers and other printed material) are written in a narrative style that is optimized for the printed page and for linear reading. Conversely, users do not read web sites so much as scan them. For this reason, pages on the Web are structured differently. Web content should be divided into short, self-contained topics. Each topic should address one main subject, and should never comprise more than a few screens of information. As long as the text is written using direct language and a consistent and transparent style, users will be able to scan for the information they’re looking for. Also, restructuring narrative text into bulleted lists helps with page readability. Of course, all of this depends on the type of site being viewed. Academic sites are slightly off the hook when it comes to web reading style. You should not alter papers, theses, and so on to accommodate online reading. In these cases, it is acceptable to leave the documents in their linear narrative style. Usability | www.it-ebooks.info 147 Principles for the Ajax Web By following certain principles geared specifically toward Ajax web development, you can avoid the pitfalls I mentioned in the preceding section. These principles are designed to provide users with an application that is simple to use and navigate, while maintaining the Ajax edge of site layout. You should follow these six principles when designing Ajax web applications: • Minimalist and aesthetic structure • Flexibility and efficiency • Consistency • Navigation • Feedback • Documentation and help Minimalist and aesthetic structure An application’s structure is the most important aspect of its usability; how everything is laid out on the page determines how easy it is to read, navigate, and use the application. Different structural layouts are “standard” from a user’s point of view, with each having its place depending on the application’s needs. However, keep in mind that nothing is stopping you from combining one or more layouts to create a structure that you find usable. All of the designs we will discuss in this section allow two important qualities that you should consider regarding any structure you use: minimalist and aesthetic. A site can stand on its own without all the frills some designers like to use. By strategically using CSS, even the most minimalist application can be aesthetically pleasing to the end user. In other books, these structures or layouts I am talking about are sometimes called design patterns, and I will switch back and forth between this term and the word structures. Some examples of design patterns are guided tours, wizards, panels, trees, and one-stop shops. Guided tours and wizards are closely related structures. Both guide the user from one part of the application to another. The main difference between the two is that wizards are typically for applications that have procedural rules that must be completed in a specific order, whereas guided tours are looser in structure and allow you to move back and forth through the application in random ways. Anyone who has ever installed a program on a Windows machine knows the basic premise of a wizard. Select an action and click Next. Repeat on the next screen, and the next screen, until you are able to click Finish. It is the same principle on the Web. Guided tours operate similarly. They provide information and an option to find out more. That next screen provides more information and entices you to find out even more, but it does provide navigation to the rest of the application if the user desires. 148 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Panels are probably the most common type of design pattern that developers use. Any site that places an application’s different components in specific spots (or columns) is a paneled design pattern. Slashdot’s web site (http://slashdot.org/) is a good example of a paneled structure, as seen in Figure 6-6. Figure 6-6. Slashdot’s paneled design pattern Looking at Slashdot, you can immediately see a top panel for the site name, slogan, search bar, and so on, and three vertical panels that make up the rest of the site. The left panel is for navigation, the right panel is for information, quick links, and so forth, and the middle panel contains the site content. This pattern does not change as you move through the site. Trees and one-stop shops are two design patterns that work in certain situations, but for the most part you should avoid them as standalone structures. Trees are applications that have links branching to other parts of the application, which branch to yet other parts of the application. Without navigational aids, it is easy to get lost in such a design. A Wiki is a good example of this design pattern. One-stop shops display not only their central focus, but everything you’d want to know about them, right there on the main page. Usability | www.it-ebooks.info 149 The best advice I can give you regarding good design patterns is that your pattern needs to fit your application’s theme and content. There is no one-size-fits-all structure that you can just plug in to any web application. Instead, you must determine the best structures for you and apply them accordingly. A combination of panels and trees is common, as is using a modified one-stop-shop and guided tour main page with panels behind it. Flexibility and efficiency Flexibility is an important characteristic of an Ajax web application. It allows the application to handle any form of input the user sends its way, any GUI manipulation the user attempts, and any data the server receives after a request. This means it needs to handle user inputs and respond to the user when there is a problem, without throwing an error. Likewise, when the user is manipulating the application’s GUI, whatever it may be, it needs to catch and handle any unexpected user action. Finally, it needs to be able to take any data sent from the server and parse the good from the bad without breaking. Efficiency is also important, but more in terms of making the application as fast and as smooth as possible for both novices and experts. For example, a novice user may require a three-step process to complete a given action; an efficient application would enable an expert user to complete the same process in only one or two steps. For this to occur, the user must understand exactly how the application is structured. To copy and paste, for example, a novice user would select what he wants to copy, click the Edit drop-down menu, click Copy, move his mouse to where he wants to paste the selected text, click the Edit drop-down menu again, and then click Paste. That takes six steps. An expert user would select what needs to be copied, press Ctrl-C, move his mouse to where he wants to paste the selected text, and click Ctrl-V—four steps. You can also achieve flexibility and efficiency through other means. Allowing the building of user macros and putting advanced options on a separate page provides flexibility in how the application is used, and improves efficiency for different users with different proficiencies. Consistency Consistency is something most developers think is trivial to implement, and yet time and again I see small inconsistencies in page development that appear trivial but that can impact application use. Consistency is merely choosing to use a standard and sticking with that choice throughout the application. Icons within the application should always perform the same action, no matter what page the user is on. Use one word or phrase to describe something, and then use only that word or phrase throughout the application. Users should not need to guess or wonder whether words, phrases, or icons have multiple meanings and actions. Also, make sure you 150 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info are consistent with buttons the user employs to interact with the client. The biggest form of inconsistency in this regard is following a Submit button with a Cancel button on one page, and then reversing the order of the buttons on the next page. Applying the standards that have been set for the platform being used is also important in terms of consistency—and I am not talking about World Wide Web Consortium (W3C) standards. It is universally accepted that underlined text within a web page means the text is a hyperlink to somewhere else. This use of standards will help to make an Ajax web application consistent. And don’t get me wrong: you can create your own set of standards for any behavior or action within the application— after all, it is your application. You just need to make sure you are consistent throughout. Navigation The navigation found within an Ajax web application is paramount to the application’s success in the eyes of the end user. Menu bars, tabs, navigation boxes, and breadcrumbs are some of the navigation tools that are typically found in an application. On the Web, however, there are others that developers sometimes do not consider, or simply forget about. The names of pages, logos, banners, icons, and backgrounds—these are visual clues that users can employ when navigating the application. When all pages in the application are distinct from one another, it’s easier for users to understand where they are. No two pages should have the same title. This little rule aids in the creation of another type of navigation aid: the site map. A site map is a basic directory listing of all the pages in the application, broken down by their appropriate categories and arranged so that it’s easy to see the parent-child relationship between them. Of course, site maps work only if the application is built with a hierarchy of some sort. If there is no real organization and hierarchical relationship between the pages, you can use a variation on the site map, called a site index. A site index works like any other index, whereby pages in the application are arranged in alphabetical order based on their titles and subtitles. A search bar is also an easy way for the user to navigate a site. Providing a search tool that gives accurate and relevant results based on the query is important. The search should weight results so that the user also knows where each result fits within the query. The final part of web application navigation is the client itself—the browser. This part of navigation is challenging for Ajax web developers because a lot of the simple tricks for creating dynamic content break things, such as the browser’s back button and bookmarks. I will cover this topic, as well as how accessibility relates to Ajax, in this chapter’s upcoming “Accessibility” section, and in Appendix D. For now, I’ll just say that you cannot overlook navigation tools built into the browser. Usability | www.it-ebooks.info 151 Feedback Feedback is sort of a two-way street in terms of Ajax web applications. If you missed some problems when testing the application, you want to know about them. So, you should make sure you provide users with a way to submit feedback about the application. Hopefully, not all of the feedback will be negative! You need to give feedback to the user too, though, in that you need to let the user know what is going on with the application. One part of having Ajax send requests to the server is that the client does not give the user much of a hint that it is requesting data. For that reason, the developer needs to indicate to the user that the application is working and that it hasn’t locked up or broken down. The feedback you give to the user can be as simple as an hourglass (standard Windows fare) or a message that the page is loading data. Alternatively, an indicator bar could report loading status. You can do several things to give the user a sense that things are operating normally. I’ve said it before, but remember that users do not like to wait. They have minimal patience, and if your application does not seem to be working for several seconds, with no indication to the contrary, they may abandon it. The simplest way to give feedback to your users is to let them know what the application is doing. If you are authenticating their username and password, tell them that. If you are grabbing tabular data, they should know. There is seldom a need to hide communication between the client and server. It is a web application, and few users do not understand that this means sending data back and forth between the client and server. Documentation and help Documentation in the form of user manuals, tutorials, online help, and technical manuals can be a good resource regarding your application. This documentation is not just for the end user, though. Think of the different types of people that will interact with your application at some level. This will include end users (your customers), managers, other programmers, database administrators—potentially a long list of people. However, the most important person the documentation is for is you. If there is one lesson all programmers, desktop and web alike, need to learn is to document what they have done. In two months’ time (especially if you haven’t been working with the application), you could forget an important nuance or why you did things the way you did. Documentation is invaluable at this point. If you move on from this application to work on other projects, it is also helpful to the programmer who will be maintaining the application to have some sort of manual that explains how components were put together. After all, if you were in that position, wouldn’t you find it helpful to have some documentation? 152 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Then there is the user of the application. Many people do not like to ask for help (and not just men). However, these people will readily take advantage of help that is provided to them automatically. Online help can be invaluable to your application if it can answer users’ questions. This help can take the form of a user manual, FAQ sheet, or simple tutorial. Functionality Functionality refers to the features in an Ajax application that support a given task. The application’s functionality is directly related to its success on the Internet. If your application does not contain the functionality that web users have come to expect as standard, it cannot succeed. Going beyond what is standard and porting desktop functionality to the Web will set your application apart. Applications provide different types of functionality with the tools they use. Table 6-1 lists the different function types that can appear in an application. Table 6-1. Types of functions in applications Type General function Collecting Accumulate information. Navigating Move around the application. Seeking Locate information. Organizing Group similar information together for easier understanding. Communicating Share ideas with a group. Generating Create content or information. Assisting Ensure equal accessibility to information for those with handicaps. Manipulating Revise or otherwise change information. Storing Store accumulated information. Common Web Tools What standard web tools have users come to expect as commonplace and feel are necessary for all applications on the Web? The following is a list of some of the most common web tools today: • Forms • Menus • Search engines • Shopping carts • Chat services • Forums (bulletin boards) • Blogs Functionality | www.it-ebooks.info 153 Forms are commonplace on the Web. Few interactive sites do not have a form of some kind on one or more of their pages. Forms allow the user to input data that will be sent to the server for parsing and interpretation. Figure 6-7 shows an example of a form on O’Reilly’s Safari Registration page (http://safari.oreilly.com/). Forms are used for collecting information, which is simple and intuitive for almost any user. Figure 6-7. Forms like this are abundant on the Web Menus come in a variety of types, ranging from Windows-like menu bars or toolbars and slide-out menus, to a number of different tab varieties, icon-based menus, navigation boxes, lists of links, and trees. Menus provide the user with ways to navigate to different areas of the application with simple mouse clicks. Figure 6-8, Amazon’s Book page (http://www.amazon.com/), shows examples of tabs and a list of links at the top of the page, as well as a navigation box on the left of the page. The function of menus is to aid in navigating a site, which—if emulating existing software applications—is easy for users to understand. Even menus that are unique in some way are usually easy to use. Search engines have been around on the Internet for a while. They have also been integrated into sites for some time. Search engines provide the user with the functionality of seeking specific information on a site without having to navigate through the whole site to find what is being sought. Take another look at Figure 6-8, and notice the search box at the top of the page. 154 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Figure 6-8. Amazon, a good study in functionality A shopping cart is more like a tool of commerce, a part of the application flow that allows the user to choose and buy something using a web application. Naturally, its function (along with other tools of this type) is to organize the information in an application. Organizing tools can be simple or complex in nature, depending on the application’s needs. Some examples on the Web now are Flickr (http://www.flickr.com/) and Amazon. Chat services, forums, and blogs are designed with certain functionality in mind: communication. Chat services allow users to talk to one another across the Internet in real time. Forums (what used to be called bulletin boards a long time ago) are categorized posts and subposts on all manner of topics, and are prevalent in help systems. Blogs, also known as weblogs, have been around for a long time, but in the past couple of years (when the word blog was coined), they have become tremendously popular. Figure 6-9 is a good example and is from http://www.ajaxian.com/. The ability to store information is an important feature of an application. Just imagine how useful an application such as Microsoft Word would be if you could not store the information you typed into it. Not at all, right? The Web, through the use of databases and text files, is able to store information from users around the globe. This has led to applications such as Wikipedia (http://www.wikipedia.org/) and Flickr, which store information submitted by users that the whole world can view. Functionality | www.it-ebooks.info 155 Figure 6-9. A blog on Ajaxian.com Tools in a Desktop Application Until recently, some tools in a desktop application were not possible to duplicate on the Web. Web applications are slowly closing the gap, however, thanks in part to Ajax and other Web 2.0 methodologies. These desktop tools are what will make your application more appealing to users and make them want to use it. Some of these tools are: • Text editors • Spreadsheets • In-place editing • Spellcheckers • Magnifiers • Drag-and-drop functionality Text editors are more than just text areas used to accept input from a user. They are a means to format text, embed images and links, and style all of it in any way the user likes. A text editor is really just a WYSIWYG editor. WYSIWYG editors are starting to show themselves in web applications, and the gap between desktop and web applications is narrowing in this area. A little more functionality on the web side of things, and the gap will close. 156 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Spreadsheets are used to keep tabular data and perform calculations on that data, storing results along with the input information. They allow the user to edit information in-place, keeping the application efficient and clean. Spreadsheets also provide graphs and charts from the input data, giving visual representations to the tabular data. Text editors, spreadsheets, and in-place editing represent application functionality that supports generating data. The other major functionality of text editors and spreadsheets is storing the information for the user to come back to at another time. Spellcheckers aid users by searching through text and looking for words that are not recognized, and then suggesting alternative spellings for those words. This is most helpful for those of us whose fingers are quicker than the keyboard, who misspell words by inverting characters, and who are just poor spellers! A magnifier is part of an accessibility software suite and does just what its name implies. When you move your mouse over an area of the screen, the portion in the magnification area gets larger. This is good for people with vision problems, or for users who do not want to strain their eyes. Magnifiers and spellcheckers are tools whose functionality is to assist users with tasks. Drag-and-drop functionality is the ability to click on an object with the mouse, and then drag the object anywhere on the screen. Most commonly it’s used to pull information between a set of lists within an application, or to move windows within the application to different places than where they were originally loaded. Drag-and-drop functionality allows you to manipulate the application in set ways to the user’s satisfaction. What Can Be Done? When considering the functionality of an Ajax web application, the first thing the developer should think about is the functions, features, and actions the interface will need. The interface must support the tasks the application is supposed to accomplish. Developers must ask themselves what functions, features, and actions the user expects the application to support. Create a checklist of the functionality that is needed, and then determine all the tools that you must build to fit into the usability features already decided on for the application. Some sample items for a checklist are: • Navigation (menu controls, links, etc.) • Organization (shopping cart, general organization) • Searching requirements (internal pages, database, web) • Forms and storage (user inputs, database storage) • Manipulation (tool tips, drag and drop, etc.) • Content creation (editors, spellcheckers) • Accessibility Functionality | www.it-ebooks.info 157 Once you have determined what tools you need to build, it’s time to start coding them. The biggest issue with porting desktop application tools to web applications is not so much in being able to mimic behavior. With Web 2.0 in full swing, any remaining issues will be gone in no time at all. The real problem with porting desktop applications to the Web is that users are not yet ready to change their usage habits. Until we see a paradigm shift away from the desktop and completely onto the Web, a gap between desktop and Ajax web applications will remain. Visualization Visualization is all about the look of the application. It concerns creating an application that is aesthetically pleasing and that visually keeps the user’s interest, while avoiding any potential distractions by including unnecessary components. Although it can be tempting to add “bells and whistles” to an application just because you can, they will only distract from the application and should be included only if the client has requested them. Layout The application layout is what ultimately determines the application’s usability structure. Layout is the graphical design and structure of the web pages—something that should stay in the background of the application and allow the functionality to be the focus instead. A good application layout will do this, whereas a bad one will constantly distract the user. You should adhere to the following four factors when designing an application’s layout: • Balance • Density • Focal point • Consistency With the classic Web, sites were linear, adhering to the structure of tables and frames. That’s all there was to work with. As CSS became the norm, layout shifted away from this linear approach. Designers were able to develop sites that were organic in their form and flow. Organic design can be a powerful way to lay out an application; after all, advertising has been using an organic technique for a long time simply because it works as a way to attract a user’s attention. 158 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Figure 6-10 shows the organic site layout of script.aculo.us (http://script.aculo.us/). Trying to design an organic layout can, however, be a slippery slope. If the layout is not balanced, it will only be a cluttered and distracting mess of information. Figure 6-10. script.aculo.us, a great example of an organic site An organic layout flows freely throughout the page, and is not constrained by the traditional row and column (table) layout that pages of the past always followed. An organic layout allows a developer to place a broader focus on the application as a whole in a much more creative manner. Always remember that organic does not mean chaotic. You must always maintain some sort of balance. The easiest way to achieve this is to superimpose a letter of the Latin alphabet on your layout. Then imagine that all the objects in the application follow along a line or curve of the letter shape. This technique will ensure that you maintain balance. Visualization | www.it-ebooks.info 159 Of course, an organic design is not a must for modern web applications. It is perfectly acceptable to still use a linear approach. Such layouts are usually built on the principle of creating columns of information in the application. But with CSS to lay out the application, the site can remain linear without having to look linear, as the CSS Zen Garden design in Figure 6-11 illustrates (http://csszengarden.com/ ?cssfile=http://www.tabfolder.com/zengarden/sample.css). Figure 6-11. The modern linear approach of the Blue Earth design at the CSS Zen Garden Several of the JavaScript frameworks, libraries, and toolkits give developers a little extra flair for their linear layout by using rounded corners. In particular, the Rico and MochiKit libraries provide this functionality out of the box. Additions to other libraries can also provide this feature. The density of the text information, images, icons, and features must maintain an average weight, or the application will be so cluttered that the user will feel momentarily overwhelmed when first experiencing the site. This is never the way you want the user to feel. Wholesale computer parts companies are notorious for having 160 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info storefronts that are too dense, as Figure 6-12 clearly shows. To keep the user’s experience with the application a happy one, remember the axiom that “less is more.” Rather than flood the user with information, break that content out in a logical manner, and always allow some whitespace to keep the user from getting claustrophobic while navigating the application. Figure 6-12. The storefront of PC Direct Source (http://www.pcdirectsource.com/), which clearly has density issues Whitespace can also be a good tool in the developer’s bag of tricks to direct a user’s attention to the application’s focal point. The contrast between size and space will naturally draw the user’s attention to the point the developer wants. Advertisers like to use the focal point to draw attention to the product being sold, as shown within the InfoWorld article (http://www.infoworld.com/) in Figure 6-13. I will come back to this again and again, because it is the simplest of principles to implement and often the most forgotten. The key to visualization, usability, and functionality is consistency. The best interfaces for applications maintain a consistent look throughout to avoid any confusion on the user’s part. As we will now see, this applies to the application’s text, color, images, and icons as well. Visualization | www.it-ebooks.info 161 Figure 6-13. The focal point of this page is definitely the advertisement Fonts Text in an Ajax web application is an important component, and it requires some forethought. Size, color, weight, decoration, and family are elements of the font that will be displayed. Remember that the text in the application is the main form of communication between the client and the user. Text formed correctly will enhance the application visually, but badly formatted text will not only detract from the application, it will also adversely affect its usability and functionality. Always think about the families you are going to use in the application, and keep in mind that too many font types on one page or throughout the site are distracting. There is an appropriate time to use different families, too. Sans-serif fonts—fonts without feet—are more appropriate for titles and bulleted items within a page, whereas serif fonts—fonts with feet—are more appropriate as the block content within a page. Table 6-2 lists common font families, the types they belong to, and the operating system(s) where you can find them. 162 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info The “Rule of Thirds” The “rule of thirds”—a common guideline in professional photography—states that if you divide an application into thirds, horizontally and vertically, thereby creating nine equal parts, the viewer’s attention will fall on objects placed near the lines’ four points of intersection. This rule stems from what is known as the golden ratio, golden number, or divine proportion. The golden ratio is known in mathematics, art, and architecture as Φ (Phi), which is an irrational number ≈ 1.618033988749895. Euclid first described Phi in his Elements as a line divided in its mean and extreme ratio by C if AB:AC = AC:CB. For nonmathematicians, the golden ratio is a ratio defined by a geometric construction, in this case the division of lines. Take a line of a given length A and divide it such that the ratio of the length of the entire line (A) to the length of the larger segment (B) is the same as the ratio of the length of the larger line segment (B) to the length of the smaller line segment (C). This can happen only at the point where A is approximately 1.61803398 times B and B is approximately 1.61803398 times C. B C A Dan Brown’s novel The DaVinci Code (Doubleday) made this ratio famous to laypeople, though mathematicians have known it for thousands of years, and it has been applied to art and architecture as early as the design of the great Egyptian pyramids. You can find information on the golden ratio at GoldenNumber.Net (http://www.goldennumber.net/). Table 6-2. Font families and their types Font family Font type Operating system Abadi MT Condensed Light Sans-serif Windows Algerian Fantasy Windows American Typewriter Serif Mac Andale Mono Monospace Windows, Mac, Unix/Linux Apple Chancery Cursive Mac Arial Sans-serif Windows, Mac, Unix/Linux Arial Black Sans-serif Windows, Mac, Unix/Linux Arial Narrow Sans-serif Windows, Mac Arial Rounded MT Bold Sans-serif Windows, Mac Arial Unicode MS Sans-serif Windows Avant Garde Sans-serif Mac, Unix/Linux Visualization | www.it-ebooks.info 163 Table 6-2. Font families and their types (continued) Font family Font type Operating system Baskerville Serif Mac Big Caslon Serif Mac Bitstream Vera Sans Sans-serif Unix/Linux Bitstream Vera Sans Mono Monospace Unix/Linux Bitstream Vera Serif Serif Unix/Linux Book Antiqua Serif Windows Bookman Serif Mac, Unix/Linux Bookman Old Style Serif Windows Braggadocio Fantasy Windows Britannic Bold Fantasy Windows Brush Script MT Cursive Windows, Mac Calisto MT Serif Windows Capitals Fantasy Mac Century Gothic Sans-serif Windows Century Schoolbook (L) Serif Windows, Unix/Linux Charcoal Sans-serif Mac Charter Serif Unix/Linux Charter BT Serif Unix/Linux Chicago Sans-serif Mac ClearlyU Serif Unix/Linux Colonna MT Fantasy Windows Comic Sans MS Cursive Windows, Mac, Unix/Linux Copperplate Fantasy Mac Copperplate Gothic Bold Fantasy Windows Courier Monospace Mac, Unix/Linux Courier New Monospace Windows, Mac, Unix/Linux Courier Regular Monospace Mac Desdemona Fantasy Windows Didot Serif Mac Fixed Monospace Unix/Linux Footlight MT Light Serif Windows Futura Sans-serif Mac Gadget Sans-serif Mac Garamond Serif Windows, Mac Geneva Sans-serif Mac Georgia Serif Windows, Mac, Unix/Linux 164 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Table 6-2. Font families and their types (continued) Font family Font type Operating system Gill Sans Sans-serif Mac Haettenschweiler Fantasy Windows Helvetica Sans-serif Mac, Unix/Linux Helvetica Narrow Sans-serif Mac, Unix/Linux Helvetica Neue Sans-serif Mac Herculanum Fantasy Mac Hoefler Text Serif Mac Impact Fantasy Windows, Mac, Unix/Linux Kino MT Fantasy Windows Lucida Sans-serif Unix/Linux Lucida Console Monospace Windows Lucida Grande Sans-serif Mac Lucida Handwriting Cursive Windows Lucida Sans Unicode Sans-serif Windows Lucidabright Serif Unix/Linux Lucidatypewriter Monospace Unix/Linux Marker Felt Fantasy Mac Matura MT Script Capitals Fantasy Windows Monaco Monospace Mac New Century Schoolbook Serif Mac, Unix/Linux New York Serif Mac News Gothic MT Sans-serif Windows Nimbus Mono L Monospace Unix/Linux Nimbus Roman No9 L Serif Unix/Linux Nimbus Roman Sans L Sans-serif Unix/Linux OCR A Extended Monospace Windows Optima Sans-serif Mac Palatino Serif Mac, Unix/Linux Papyrus Fantasy Mac Playbill Fantasy Windows Sand Cursive Mac Skia Sans-serif Mac Tahoma Sans-serif Windows Techno Sans-serif Mac Terminal Monospace Windows Terminal Monospace Unix/Linux Visualization | www.it-ebooks.info 165 Table 6-2. Font families and their types (continued) Font family Font type Operating system Textile Cursive Mac Times Serif Mac, Unix/Linux Times New Roman Serif Windows, Mac, Unix/Linux Trebuchet MS Sans-serif Windows, Mac, Unix/Linux Univers Sans-serif Mac URW Antiqua T Serif Unix/Linux URW Bookman L Serif Unix/Linux URW Chancery L Cursive Unix/Linux URW Gothic L Sans-serif Unix/Linux URW Grotesk T Sans-serif Unix/Linux URW Palladio L Serif Unix/Linux Utopia Serif Unix/Linux Verdana Sans-serif Windows, Mac, Unix/Linux VT 100 Monospace Mac Wide Latin Fantasy Windows Zapf Chancery Cursive Mac Zapfino Cursive Mac The safest fonts to use are the ones that all operating systems support; otherwise, your application may not appear as you intended on some systems. Of course, whether this consideration is critical is entirely up to the developer. Also with fonts, avoid the use of all capital letters for block content, but note that this is fine for highlighting items and for titles. The general rule of thumb is that if the text in question constructs a sentence (with a period) or a long paragraph, you should not use capital letters. Text spacing is also important in an application. Text is most legible when the separation between lines is one and a half times the average letter height. When lines are spaced too far apart, the text will seem disconnected, whereas compacting lines on top of each other makes the text difficult or impossible to read. When text in the application must command attention, you can employ several techniques. Capitalizing all the letters in a word or phrase will draw attention, as will changing the font weight to bold, italics, or some other font style, such as underlining. Creating a contrasting color may also attract the desired attention. The final consideration for font and color is the contrast in color between the text in the application and the background on which it sits. Table 6-3 lists some color combinations and whether they work well. 166 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Table 6-3. Color contrasts Foreground Background Adequate contrast Any light color Any light color No Black Orange Yes Black White Yes Dark color Dark color No Red Green No White Black Yes White Green Yes Yellow Dark blue Yes When picking colors for the application, it’s important that you think about color contrast, not just for the contrast with text and backgrounds, but also between components. Images and Icons An icon in an application will mean nothing to the user if the graphic is an arbitrary symbol, or if the user has never been exposed to the icon’s meaning. It is most appropriate to use symbols that have universal meaning. For instance, the universal symbol for a doctor or aid is a red cross on a white field. By using symbols that have been globally accepted on the Web, you can ensure that your icons will not confuse the user. Another important characteristic of icons and symbols is that they vary by age, education, and even culture. It is important to remember this when designing an icon in an application. Lastly, experienced web users readily recognize icons as navigation tools, and they expect small symbols to allow navigation. A layout that incorporates a line of small images will tempt these users to click on the images. If the images have no navigation function, they will likely frustrate users. Larger images in the application will attract the most attention. You should remember this when designing the layout. Also remember that a user is more likely to focus on a large image before anything else, so it is wise to choose images that merge well with the application’s overall theme. Accessibility Accessibility in web design is a hot topic, and for good reason. The Internet was born only recently, and now gives us unprecedented access to information, news, entertainment, and commerce at our fingertips—so much so that most of us cannot imagine life without the Web. Accessibility | www.it-ebooks.info 167 The Internet changed how we conduct business, get news, stay in touch with friends, and are entertained. As much as our lives have changed, though, the lives of people with disabilities have changed much more. Think about what it is like now for this group of people. A blind person who could never read a book or newspaper can now do so using text-to-speech synthesizers (screen readers). A deaf person can download transcripts from events he could not participate in, or view multimedia with captioning. A person with motor disabilities may have special technologies available to her that allow her to navigate without the use of her hands or a mouse. W3C-WAI The W3C hosts the Web Accessibility Initiative (WAI), found at http://www.w3.org/ WAI/, which is sponsored by government entities such as the U.S. Department of Education’s National Institute on Disability and Rehabilitation Research and the European Commission’s Information Society Technologies Programme, as well as technology industry support from businesses such as Microsoft and IBM. The WAI believes that accessibility on the Web is a problem in many areas. It is currently working on the following five areas: • Developing accessibility guidelines • Ensuring accessibility support in web technologies • Improving accessibility evaluation and repair tools • Developing accessibility education and outreach materials • Coordinating accessibility research and development The WAI has created several guidelines, though the most important for a web application developer is the Web Content Accessibility Guidelines 1.0 (WCAG 1.0). The WCAG 1.0 guideline became a W3C Recommendation in May 1999, and explained how web sites should be made accessible. This guideline has three priorities that are used as checkpoints against the recommendation. You can find a checklist for this recommendation at http://www.w3.org/TR/WAI-WEBCONTENT/full-checklist.html. By reviewing W3C specifications during the Last Call Working Draft stage and having technical experts participate in W3C working groups, the WAI ensures that there is support for accessibility. The WAI has a working group for evaluating and repairing accessibility, called the Evaluation and Repair Tools Working Group. This group maintains a list of references to tools for evaluating and repairing accessibility, and has developed the Evaluation and Report Language. Another working group that the WAI uses is the Education and Outreach Working Group. This group helps develop education and outreach material, such as the Quick Tips Reference Card, Curriculum for Web Content Accessibility Guidelines, and Policies Relating to Web Accessibility. Meanwhile, the Research and Development Interest 168 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info Group researches potential topics and gets public opinion on them, as well as holds seminars on topics in research and development. The main thing to remember about the WAI is that its goal is a Web that everyone can access, regardless of ability. Is This Important? Each disability requires some kind of change in the design of a web application. This can be costly if a disability is not recognized and coded from the beginning. Some businesses may believe it would not be cost-effective to make a site accessible. However, the simple truth is that disabilities affect a significant percentage of the population, making disabled people a large portion of potential customers. In 2002, 18 percent of Americans were disabled—12 percent with a severe disability, according to the U.S. Census Bureau.* Twelve percent of the U.S. population in 2002 was estimated at 34.5 million people; 12 percent of the world population would be quite large. Not taking the time to make the Internet accessible to this many people would be incomprehensible, and in some cases against the law. Adapting a web site to make it accessible to disabled people is not always a major issue. People with cognitive disabilities benefit from graphics, organized headings and lists, and navigation that has visual cues. Likewise, people with hearing disabilities can benefit from video content that is captioned. These additions and changes to a web site are helpful to everyone, not just those who are disabled. Therefore, implementing them should not be such a big deal. That being said, when it comes to Ajax web applications, sometimes the design should change to accommodate the disabled, but other times it just does not make sense. Ajax Accessibility Issues The problem with Ajax is its dynamic nature. Ajax uses JavaScript and CSS extensively, technologies which screen readers, text browsers, and other disability aids do not support very well. So, trying to write an application that is Ajax in nature and yet accessible to everyone is pretty much impossible. Therefore, when designing an Ajax web application, you should ask yourself these two questions: • Is this functionality required for the application to work? • Is there no JavaScript and CSS alternative? * U.S. Census Bureau News, http://www.census.gov/Press-Release/www/releases/archives/aging_population/ 006809.html. Accessibility | www.it-ebooks.info 169 If you answer “yes” to both questions, you must accept that the application cannot meet WCAG recommendations and move on. If you answer “no” to the second question, you should decide what it means to you to have an application that meets WCAG 1.0 recommendations. This is not as tricky as it might look. If you’re designing a web site, it should meet all the WCAG 1.0 recommendations. If you’re designing a web application, it should not be difficult to get it to comply. Although most software vendors will try to make sure their applications are released on all major platforms—Windows, Mac OS X, various Linux distributions, and so on—it is not always possible to do so. The same applies here. Accessibility can be a legal requirement for a site, particularly those involving government agencies. Using Ajax will either break accessibility or make it very difficult to achieve. In these instances, Ajax may not be the best solution for the application. Now, this is strictly for accessibility guidelines. You still need to address issues with Ajax breaking the browser’s functionality, as you will see later in the book. I am not advocating that you forget about accessibility. There is no excuse for not following most of the WCAG 1.0 recommendations, as they benefit anyone viewing the application, not just the disabled. There simply has to be a point when you cut your losses, or you accept the fact that you want this functionality in your application, and not everyone is going to be able to view it. After all, by developing with Ajax, you are already excluding the group of people still using browsers from the good old days. When All Else Fails So, what should a developer do when his Ajax application cannot meet accessibility guidelines? Though I know some of you may view this as a cop-out, I say developers in this position should create an alternative version. This requires more work, of course. Plus, it may not be feasible in all situations. How do you create a non-JavaScript/CSS spreadsheet application, for example? Creating alternative pages with the same content, even dynamic content, satisfies the following WAI-WCAG 1.0 guidelines: • Priority 1 checkpoint 11.4: If, after best efforts, you cannot create an accessible page, provide a link to an alternative page that uses W3C technologies, is accessible, has equivalent information (or functionality), and is updated as often as the inaccessible (original) page. • Priority 2 checkpoint 6.5: Ensure that dynamic content is accessible or provide an alternative presentation or page. 170 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info I do not like the idea of not allowing everyone access to an application I build, but sometimes you just cannot please everyone. Besides, not all of the responsibility for making accessible content should fall on the developer’s shoulders (only most of it should). At some point, the languages need to be addressed, and even more so, the tools used to view the web content. So, until browsers become more accessible or other disability technology aids gain in functionality, do your best. That is all anyone can ask. The Ajax Interface What does all this have to do with Ajax applications? Everything. An Ajax application encompasses all of these different ideas—usability, functionality, visualization, and accessibility. An Ajax application should follow all of these web design issues, as all of these are important for any application on the Web. Remember what I said in Chapter 2, though: Ajax has its place. Do not force an application to have an Ajax interface just to say that it has one. You should use Ajax when it is necessary or required. You are defeating the purpose of its very design if you do not follow this and instead create an application that is not usable or functional, is visually unappealing, or has accessibility issues. A good example of an Ajax design working on the Web is Gmail, found at http:// www.gmail.com/. Figure 6-14 shows what the application looks like from the browser. Figure 6-14. Google’s Gmail mail service, a good example of a Web 2.0 Ajax application The Ajax Interface | www.it-ebooks.info 171 Notice how this application follows the recipe for a good design, and makes good use of Ajax. There is no clutter, and its simple design makes it easy to use. It is integrated well with the rest of Google’s suite of web applications. It is extremely fast, and it gives the user the feeling that he is using a desktop application. In a nutshell, that is what an Ajax interface is all about—mimicking a desktop interface. Google did not have to use Ajax when developing its mail client. After all, Microsoft has been providing a mail client for years that did not use any Web 2.0 features, and it is a hugely popular application to this day. Using Ajax and allowing the user to navigate through the application without all of the reloading and page flashing did set Google apart, though. There are many reasons to use Ajax when you can, and Gmail shows you why. It sets the application apart. It makes the application feel like a desktop application. It makes the application faster. Ajax, when used correctly and for the right functionality, provides you with so much more than a traditional web design that it is helping to change the feel of the Web to what it is today. 172 | Chapter 6: Designing Ajax Interfaces www.it-ebooks.info PART II II. Ajax Foundations Chapters 7 through 15 cover the foundational components that you can use in an Ajax application. This part of the book looks at each component of a web page or application and shows how you can use Ajax to enhance it. These chapters will introduce many new objects that can be plugged in to a web site, and should give developers ideas on how to create their own objects. You may find it useful to refer to these chapters, as I use many of the examples throughout the rest of the book. Chapter 7, Laying Out Site Navigation Chapter 8, Fun with Tables and Lists Chapter 9, Page Layout with Frames That Aren’t Chapter 10, Navigation Boxes and Windows Chapter 11, Customizing the Client Chapter 12, Errors: To Be (in Style) or Not to Be Chapter 13, This Ain’t Your Father’s Animation Chapter 14, A Funny Thing Happened on the Way to the Form Chapter 15, Data Validation: Client, Server, or Both www.it-ebooks.info www.it-ebooks.info Chapter 7 CHAPTER 7 Laying Out Site Navigation 7 Where does Ajax come into play with web site navigation? You can use it to get the list of data for a submenu, build a hierarchical list for use as breadcrumbs on the page, or create a navigation tree. In short, you can apply Ajax in many creative ways for web site navigation. I am sure that by the time this book hits the shelves, developers will be implementing a few more navigation techniques that use Ajax. That is part of the beauty of building Web 2.0 applications—they can always change. An important part of an Ajax web application is the way the user gets from one place to another within its pages. This is site navigation in its simple terms, and it can take many forms, from plain navigation bars with text links to complicated file menus. Site navigation can also take the form of tabs separating content within the application, or links on the bottom of a page that take the user back to the top of the page. Whatever form it takes, site navigation must serve one purpose: to take the user somewhere else in the application. Menus One of the most popular navigation techniques is the menu, whether it is a navigation menu or a navigation bar. Menus are lists of links for use within the application that are put in some kind of logical grouping. The CSS that is applied to these menus determines how they will look. First, we will look at some simple navigation menus and bars and then we will discuss how to apply Ajax to them to make them more interactive. Simple Navigation Bar A simple navigation bar can have a variety of looks based on the CSS that is applied to it. The bar is built with lists to represent the menu. The old way of creating a menu looked like the following XHTML markup: <a href="file/">File</a>    <a href="edit/">Edit</a>    <a href="view/">View</a>    175 www.it-ebooks.info <a <a <a <a <a <a href="insert/">Insert</a>    href="format/">Format</a>    href="table/">Table</a>    href="tools/">Tools</a>    href="window/">Window</a>    href="help/">Help</a> This old method of building a navigation bar was inflexible, and making it minimally presentable using style rules was difficult. Years ago, this is what developers used for menu bars, but today they use a much better method to create a navigation system. They use XHTML lists for the basic structure and then style them into whatever type of navigation bar they want. Figure 7-1 shows several examples of navigation bars with different CSS associated with them. Figure 7-1. Examples of a simple navigation bar styled in a variety of ways All of the navigation bars in Figure 7-1 use the same XHTML list; the difference in styles is in the CSS associated with each of them: <div id="navigationMenu"> <ul id="menuList"> <li id="active"> <a id="current" href="file/" accesskey="F" hreflang="en" tabindex="1"> File </a> </li><li> <a href="edit/" accesskey="F" hreflang="en" tabindex="2"> Edit </a> </li><li> <a href="view/" accesskey="E" hreflang="en" tabindex="3"> View </a> </li><li> <a href="insert/" accesskey="I" hreflang="en" tabindex="4"> Insert </a> </li><li> <a href="format/" accesskey="M" hreflang="en" tabindex="5"> Format </a> </li><li> 176 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info <a href="table/" accesskey="A" hreflang="en" tabindex="6"> Table </a> </li><li> <a href="tools/" accesskey="T" hreflang="en" tabindex="7"> Tools </a> </li><li> <a href="window/" accesskey="W" hreflang="en" tabindex="8"> Window </a> </li><li> <a href="help/" accesskey="H" hreflang="en" tabindex="9"> Help </a> </li> </ul> </div> Adding the accesskey, hreflang, and tabindex in the <link> elements satisfies the following Web Accessibility Initiative-Web Content Accessibility Guidelines (WAI-WCAG) 1.0 guidelines: • Priority 1 checkpoint 4.1: Clearly identify changes in the natural language of a document’s text and any text equivalents (e.g., captions). • Priority 3 checkpoint 9.4: Create a logical tab order through links, form controls, and objects. • Priority 3 checkpoint 9.5: Provide keyboard shortcuts to important links (including those in client-side image maps), form controls, and groups of form controls. Menu #1 is styled with the following CSS rules: #menuList { background-color: #396; color: #fff; list-style-type: none; margin: 0; padding: .3em 0; text-align: center; } #menuList li { display: inline; padding: 0 .5em; } #menuList li a { background-color: transparent; color: #fff; padding: .1em .5em; text-decoration: none; } Menus | www.it-ebooks.info 177 #menuList li a:hover { background-color: #0c0; color: #fff; } There is no real trick to making a navigation menu with lists. You will notice that the list-style-type of the <ul> element is set to none and the display of all <li> elements is set to inline. These are the two CSS rules that create the horizontal list, and everything else is styling the navigation menu to whatever look is desired. Menu #2 is styled with the following CSS rules:* #menuList { background-color: #396; border-top: 1px solid #063; color: #fff; list-style: none outside; margin: 0; padding: 0; text-align: center; } #menuList li { background-color: #000; bottom: .75em; color: #fff; display: inline; line-height: 1.2em; margin: 0 3px 0 0; padding: 4px 0; position: relative; } #menuList li a { background-color: #090; border: 1px solid #fff; bottom: 2px; color: #fff; display: inline; height: 1em; margin: 0; padding: 3px 5px; position: relative; right: 2px; text-decoration: none; } #menuList li a:hover { background-color: #0c0; bottom: 1px; color: #fff; position: relative; * The CSS rules for menu #2 style the menu bar to replicate the look of ZDNet (http://www.zdnet.com/). 178 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info right: 1px; } #menuList li#active { background-color: #396; bottom: 13px; color: #fff; display: inline; margin: 0 4px; padding: 0; position: relative; } #menuList #active a { background-color: #396; border-bottom: none; border-left: 1px solid #063; border-right: 1px solid #063; border-top: 1px solid #063; bottom: 0; color: #fff; cursor: text; margin: 0; padding: 2px 7px 0 7px; position: relative; right: 0; } The second menu shows off some of what you can do with a little CSS and an XHTML list. The trick here is to move all of the <li> elements up from the baseline, which we achieved with these two rules: position: relative; and bottom: .75em;. We then offset the <a> elements from this <li> base to create the shadow effect using the right and bottom rules on #menuList li a. Menu #3 is styled with the following CSS rules:* #menuList { background-color: #396; border-bottom: 1px solid #063; border-top: 1px solid #063; color: #fff; list-style-type: none; margin-left: 0; padding: .3em 0; text-align: center; } #menuList li { border-top: 1px solid #ccc; display: inline; * The CSS rules for menu #3 come from Eric Meyer’s navigation bar, which he made in his “Minimal Markup, Surprising Style” presentation (http://meyerweb.com/eric/talks/2003/commug/commug.html). Menus | www.it-ebooks.info 179 margin: 0; } #menuList li a { background-color: #ada; border-left: .5em solid #9b9; color: #000; padding: .1em .5em .1em .75em; text-decoration: none; } #menuList li a:hover { background-color: #0c0; color: #fff; border-color: #fff; } Like menu #1, this menu also has a simple design. We create the effect of having almost a pseudoindicator on each menu item via the simple use of a border: border-left: .5em solid #9b9;. The rest of the changes needed for styling this menu concern changing background colors like the other menu bars. The good thing about creating a menu bar using CSS is that it will degrade nicely into a simple list of links for browsers that cannot handle the CSS—screen readers, Lynx, and so on. This is important from an accessibility point of view, and is something developers should strive for. Menu #3 could (arguably) be more of a navigation bar with buttons, rather than a simple menu navigation bar. The variety of effects available to implement with CSS makes it hard to distinguish between a menu and a navigation bar, as you will see later in this chapter. Button and Image Navigation Navigation bars with buttons are similar to the simple navigation bars we discussed in the preceding section. The main difference is in the look of the individual navigation elements, or links. Buttons are not flat to the navigation bar. Instead, they will appear to be raised off of or sunk into the navigation bar. When CSS is not used, you can instead employ the method of using images that look like buttons. You can use a single image to represent all of the buttons in your application by adding text on top of the image as part of the XHTML, or you can use a different image to represent each function in the application. Figure 7-2 shows examples of different types of button and image navigation bars. The first navigation bar shows all the images used as buttons for a good bit of functionality. The second navigation bar shows image buttons we are all familiar with—a browser’s navigation (in this case, Firefox). The third navigation bar shows some of the button navigation in Hotmail, which adds images as part of the buttons. 180 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Figure 7-2. Button and image navigation bars Advanced buttons The simplest way to create buttons and not actually use the <input> element with type equal to button is to put stylized borders around the element that will provide the link. Normally, the easiest element to use is the <a> element, because you already have the navigation built into it. In other words, you do not have to use a JavaScript technique to navigate to the button’s destination. For example, look at the following CSS rules: #menuList li a { background-color: #4a7; border: 2px solid; border-color: #cec #464 #575 #dfd; color: #fff; padding: .1em .5em; text-decoration: none; } #menuList li a:hover { background-color: #396; color: #fff; border-color: #575 #dfd #cec #464; } Figure 7-3 shows what these CSS rules would produce on an <a> element. Figure 7-3. A button (mouseout and mouseover) using CSS rules Menus | www.it-ebooks.info 181 Internet Explorer does not natively support :hover on any element other than <a>. It is a World Wide Web Consortium (W3C) standard that says that the :hover pseudoclass should be available to all XHTML elements, and :hover does work appropriately in all other modern browsers. The trick is in the colors used for the button’s four borders. The desired effect is to create a beveled edge for the button. It would be just as easy to use the outset and inset values on a border-style rule. If I had done that, however, I would have no control over how the browser rendered the bevels. Internet Explorer, Mozilla, and other browsers render the inset and outset borders in different ways. This, of course, comes down to personal preferences. The following CSS may suit a developer: #menuList li a { background-color: #4a7; border-style: outset; color: #fff; padding: .1em .5em; text-decoration: none; } #menuList li a:hover { background-color: #396; color: #fff; border-style: inset; } Figure 7-4 shows a final working navigation bar with buttons. This navigation bar uses the same XHTML list as the previous examples, and the following CSS rules: #menuList { background-color: #396; color: #fff; list-style-type: none; margin: 0; padding: .3em 0; text-align: center; } #menuList li { display: inline; padding: 0 .5em; } #menuList li a { background-color: #4a7; border: 2px solid; border-color: #cec #464 #575 #dfd; color: #fff; padding: .1em .5em; text-decoration: none; } 182 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info #menuList li a:hover { background-color: #396; color: #fff; border-color: #575 #dfd #cec #464; } Figure 7-4. A navigation bar using buttons Perceiving 3D All buttons displayed in any desktop application on any operating system have one thing in common. All of them, regardless of shape or size, create the illusion of depth by placing a lighter colored edge on the left and top, and a darker colored edge on the right and bottom. This gives a visual cue to the brain that the object in question is three-dimensional and is protruding from the surface. In the same manner, switching the light and dark areas cues the brain into seeing a 3D object that is recessed into the surface. But why? Our brain instinctively recognizes that light sources come from above and not below, which would create the shadows on the button’s “surface.” The left side versus the right side is a little trickier. If you were to reverse the color of the left and right sides, they would still appear to protrude or recess in turn. It is universally acceptable to think that a light source will be coming from the left side of the object only because of the precedent set by all major operating systems in regard to light sources. What is truly interesting, though, is that although almost all of us would view the buttons in Figure 7-3 as the Edit button protruding and the View button being pressed, a very small minority would see the opposite. This comes down to how the brain perceives 2D space in three dimensions, and a rare phenomenon called multistable perception. This phenomenon is a visual perception characterized by unpredictable changes in how the brain spontaneously views patterns that can be considered ambiguous. Probably the most famous pattern is the Necker cube (http://www. hypnosisnetwork.com/articles/a/76/The-Necker-Cube:-An-Experiment-in-Perception)— a line drawing of a cube that does not give any visual cue as to which lines are in front of the others when they cross. The brain prefers to see patterns and images in certain ways based on experiences in life—people view objects from above and not below, light sources come from above and not below, and so on. The unpredictable changes occur the longer a pattern is viewed. As you look at the Necker cube, suddenly your brain will pick up that you could view the cube from below. Many examples of this illusion exist, and they are part of what is known as the Gestalt effect. You can see more Gestalt images at http://www. illusion-optical.com/Optical-Illusions/. Though there is a widely accepted way to create buttons in an application, be aware that some users may interpret the visual cues you provide in ways other than what you intended. Keeping navigational designs simple and straightforward will help you avoid ambiguous user interpretations. Menus | www.it-ebooks.info 183 For a final touch to our CSS buttons, we will add images to them to better represent their functionality. Figure 7-5 shows what one of these buttons looks like. Figure 7-5. An image built into a CSS button We will build this button using the following CSS rule: #menuList li a { background: no-repeat 3px 4px url('save.png'); background-color: #4a7; border: 2px solid; border-color: #cec #464 #575 #dfd; color: #fff; padding: .1em .5em .15em 1.5em; text-decoration: none; } The addition is background: no-repeat 3px 4px url(save.png);. In addition, we increased the padding of the <a> element to accommodate the image. All we need to do now is create a small image for each button on the navigation bar, and add the corresponding CSS. To do this, we need to give each button a unique id attribute to which to attach the proper image, as the following code illustrates: #menuList li a { background-color: #4a7; border: 2px solid; border-color: #cec #464 #575 #dfd; color: #fff; padding: .1em .5em .15em 1.5em; text-decoration: none; } #menuList li a#save { background: no-repeat 3px 4px url('save.png'); } #menuList li a#saveAll { background: no-repeat 3px 4px url('saveall.png'); } #menuList li a#cancel { background: no-repeat 3px 4px url('cancel.png'); } 184 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Image rollovers the Ajax way What if, instead of using CSS buttons, you wanted to have images represent your navigation bar’s buttons? Putting on an image is easy, but how do you make it change when the mouse moves over it to indicate to the user that the button is pressed? The old way to do this was to use JavaScript to change the image when certain MouseEvents occur. Example 7-1 shows the JavaScript needed to accomplish this. Example 7-1. rollover.js: JavaScript to handle image rollovers /** * Example 7-1, rollover.js: JavaScript to handle image rollovers. */ /* Preload the images for a faster rollover */ if (document.images) { /* This represents the save image when active */ var saveImg_on = new Image( ); saveImg_on.src = 'saveImg_on.png'; /* This represents the save image when inactive */ var saveImg_off = new Image( ); saveImg_off.src = 'saveImg_off.png'; } /** * This function, turnImageOn, is called when there is a mouseover event on the * affected element and sets the /src/ attribute to the "on" image. * * @param {String} p_id The id attribute for the affected image. */ function turnImageOn(p_id) { document.getElementById(p_id).src = eval(p_id + '_on.src'); } /** * This function, turnImageOff, is called when there is a mouseout event on the * affected element and sets the /src/ attribute to the "off" image. * * @param {String} p_id The id attribute for the affected image. */ function turnImageOff(p_id) { document.getElementById(p_id).src = eval(p_id + '_off.src'); } The JavaScript function turnImageOn( ) is called on all mouseover events attached to an image button to change the image. The function turnImageOff( ) is called on all mouseout events attached to the image to return the button to the original image. Menus | www.it-ebooks.info 185 All we need now are images named with the _on and _off extensions for the JavaScript to work. Here is an example: <div id="navigationMenu"> <ul id="menuList"> <li> <a href="save/" accesskey="S" hreflang="en" tabindex="1" onmouseover="turnImageOn('saveImg');" onmouseout="turnImageOff('saveImg');"> <img id="saveImg" src="saveImg_off.png" alt="Save" title="Save" /> </a> </li><li> <a href="saveall/" accesskey="A" hreflang="en" tabindex="2" onmouseover="turnImageOn('saveAllImg');" onmouseout="turnImageOff('saveAllImg');"> <img id="saveAllImg" src="saveAllImg_off.png" alt="Save All" title="Save All" /> </a> </li><li> <a href="cancel/" accesskey="C" hreflang="en" tabindex="3" onmouseover="turnImageOn('cancelImg');" onmouseout="turnImageOff('cancelImg');"> <img id="cancelImg" src="cancelImg_off.png" alt="Cancel" title="Cancel" /> </a> </li> </ul> </div> If JavaScript were turned off in the browser, the image buttons would simply not change on mouse movements, and the image link would take them to the destination. By using CSS, however, you can still change the images for browsers that do not have scripting capabilities. The CSS for this technique is: a div#saveImg { background: no-repeat url('saveImg_off.png'); height: 20px; width: 50px; } a div#saveImg:hover { background: no-repeat url('saveImg_on.png'); } a div#saveAllImg { background: no-repeat url('saveAllImg_off.png'); height: 20px; width: 80px; } a div#saveAllImg:hover { background: no-repeat url('saveAllImg_on.png'); } 186 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info a div#cancelImg { background: no-repeat url('cancelImg_off.png'); height: 20px; width: 65px; } a div#cancelImg:hover { background: no-repeat url('cancelImg_on.png'); } The CSS rules apply to the following XHTML: <div id="navigationMenu"> <ul id="menuList"> <li> <a href="save/" accesskey="S" hreflang="en" tabindex="1"> <div id="saveImg"> </div> </a> </li><li> <a href="saveall/" accesskey="A" hreflang="en" tabindex="2"> <div id="saveAllImg"> </div> </a> </li><li> <a href="cancel/" accesskey="C" hreflang="en" tabindex="3"> <div id="cancelImg"> </div> </a> </li> </ul> </div> As mentioned earlier, Internet Explorer does not natively support :hover on elements other than <a>. For this reason, instead of using the CSS that will work for all other browsers, we must use this: a div#saveDiv { height: 20px; width: 50px; } a#saveImg { background: no-repeat url('saveImg_off.png'); } a#saveImg:hover { background: no-repeat url('saveImg_on.png'); } a div#saveAllDiv { height: 20px; width: 80px; } a#saveAllImg { background: no-repeat url('saveAllImg_off.png'); } Menus | www.it-ebooks.info 187 a#saveAllImg:hover { background: no-repeat url('saveAllImg_on.png'); } a div#cancelDiv { height: 20px; width: 65px; } a#cancelImg { background: no-repeat url('cancelImg_off.png'); } a#cancelImg:hover { background: no-repeat url('cancelImg_on.png'); } The workaround is to change the <a> element to suit our needs as an image. Specifically, it needs a <div> element to hold the size of the image that will be the <a> element’s background-image. Then :hover will work correctly and we’ll get the desired effect. Of course, our XHTML must change as well: <div id="navigationMenu"> <ul id="menuList"> <li> <a id="saveImg" href="save/" accesskey="S" hreflang="en" tabindex="1"> <div id="saveDiv"> </div> </a> </li><li> <a id="saveAllImg" href="saveall/" accesskey="A" hreflang="en" tabindex="2"> <div id="saveAllDiv"> </div> </a> </li><li> <a id="cancelImg" href="cancel/" accesskey="C" hreflang="en" tabindex="3"> <div id="cancelDiv"> </div> </a> </li> </ul> </div> You can read about a different workaround for this Internet Explorer issue in Peter Nederlof’s excellent article, “be gone evil scriplets!” (http://www.xs4all.nl/~peterned/ hovercraft.html). This article discusses a hack to get Internet Explorer to behave as other browsers do with :hover. Drop-Down Menus It’s easy to create drop-down menus with a combination of CSS and JavaScript. But for this application, we want a drop-down menu that we can create with only CSS. 188 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info This kind of menu would be faster, as it would require no parsing of scripting code, and it would also degrade the way we want it to. First, let’s make our menu a little more complicated, because a drop-down menu should handle nested menus without a hitch, right? The new menu will be using id and class identifiers different from the other menus to enable the drop-down part of the menu: <div id="navigationMenu"> <ul id="topMenu"> <li class="sub"> <a href="file/" accesskey="F" hreflang="en" tabindex="1">File</a> <ul> <li><a href="open/" hreflang="en" tabindex="2">Open</a></li> <li><a href="save/" hreflang="en" tabindex="3">Save</a></li> <li><a href="saveall/" hreflang="en" tabindex="4">Save All</a></li> <li class="sub"><a href="export/" hreflang="en" tabindex="5"> <span class="rightArrow">▶</span>Export</a> <ul> <li> <a href="text/" hreflang="en" tabindex="6"> Export as Text </a> </li><li> <a href="html/" hreflang="en" tabindex="7"> Export as HTML </a> </li> </ul> </li> <li> <a href="http://www.google.com/" accesskey="X" hreflang="en" tabindex="8"> Exit </a> </li> </ul> </li> <li class="sub"> <a href="edit/" accesskey="E" hreflang="en" tabindex="9">Edit</a> <ul> <li><a href="copy/" tabindex="10">Copy</a></li> <li><a href="cut/" tabindex="11">Cut</a></li> <li><a href="paste/" tabindex="12">Paste</a></li> </ul> </li> <li class="sub"> <a href="file/" accesskey="N" hreflang="en" tabindex="13"> Find </a> </li> </ul> </div> Menus | www.it-ebooks.info 189 The menu still uses XHTML lists, which will degrade nicely in browsers that cannot support the CSS rules. Now we need our CSS for the drop-down menu. Example 7-2 shows the CSS required to give us a working drop-down menu. Example 7-2. A CSS solution to drop-down menus /* * Example 7-2, A CSS solution to drop-down menus */ ul#topMenu { background-color: #bbb; border: 2px solid; border-color: #ede #777 #888 #ddd; color: #000; font: 1em Arial, sans-serif; list-style-type: none; padding: 6px; text-align: left; } ul#topMenu li { display: inline; padding-right: 1em; position: relative; } ul#topMenu li a { background-color: transparent; border-color: 1px solid #bbb; color: #000; cursor: default; left: 0px; margin: 1px; padding: 2px 2px; position: relative; text-decoration: none; top: 0px; z-index: 1000000; } ul#topMenu li a:hover { background-color: #bbb; border-color: #888 #ddd #ede #777; color: #000; left: 0px; top: 0px; } ul#topMenu li:hover > ul { background-color: #bbb; 190 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-2. A CSS solution to drop-down menus (continued) border: 2px solid; border-color: #ede #777 #888 #ddd; color: #000; display: block; left: 1em; padding: 2px; position: absolute; width: 8em; z-index: 1000001; } ul#topMenu ul > li { display: block; margin: 0; padding: 0; } ul#topMenu ul > li a { border: none; display: block; text-decoration: none; } ul#topMenu ul > li a:hover { background-color: #33a; color: #fff; } ul#topMenu li:hover > ul li:hover > ul { left: 100%; top: 0; z-index: 1000002; } ul#topMenu ul { display: none; } .rightArrow { float: right; } Now it’s time for the caveats. Yes, there are always caveats in the world of standards compliance. Example 7-2 will not work in Internet Explorer because Internet Explorer does not support the CSS2 rules that are used to make this work. The best solution for getting drop-down menu support that is fully cross-browser-compliant— other than lobbying the world to drop Internet Explorer—is to use CSS in combination with JavaScript. Menus | www.it-ebooks.info 191 The File Menu XHTML lists are the best method for building a menu structure simply because browsers that do not support the CSS and JavaScript thrown at them can still use the underlying structure to present navigation to the user. We will use this principle throughout the rest of this book to make Ajax application controls, widgets, content, and so on a little bit more accessible. Example 7-3 shows the XHTML we can use to make the menu structure that we will enhance through CSS and JavaScript. Example 7-3. filemenu.html: The basic structure for a file menu <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title> Example 7-3, filemenu.html: The basic structure for a file menu This structure is similar to the drop-down menu example from the preceding section. The differences are in the calling of methods from the menu object on the click, mouseover, and mouseout MouseEvents. We’ll discuss these methods in more detail in a minute, but first we must style the menu to look like a file menu instead of nested XHTML lists. Example 7-4 provides the CSS rules for the file menu. These rules attempt to make a file menu that looks like the one you find in Windows applications. It is easy enough to change these rules to match your style needs. Example 7-4. filemenu.css: The CSS styles for a Windows-like file menu /* * Example 7-4, filemenu.css: The CSS styles for a Windows-like file menu */ /* * This is the container for the menu itself, and it creates a little buffer space * under the menu to keep things from looking too crowded */ #fileMenu { padding-bottom: 1em; } /* * The file menu should have a standard sans-serif font for itself and all of * its children */ ul.filemenuBar, ul.filemenuBar a.fileMenuButton, ul.fileMenuChild, ul.fileMenuChild a.fileMenuItem { font: 1em Arial, sans-serif; } /* * This is the menu bar itself, with the colors and borders attempting to replicate * the theme from the default Windows environment */ ul.fileMenuBar { background-color: #bbb; border: 2px solid; border-color: #ede #777 #888 #ddd; color: #000; Menus | www.it-ebooks.info 195 Example 7-4. filemenu.css: The CSS styles for a Windows-like file menu (continued) list-style-type: none; margin: 0; padding: 6px; text-align: left; } ul.fileMenuBar li { display: inline; padding-right: 1em; } ul.fileMenuBar a.fileMenuButton { background-color: transparent; border: 1px solid #bbb; color: #000; cursor: default; left: 0px; margin: 1px; padding: 2px 2px; position: relative; text-decoration: none; top: 0px; z-index: 1000000; } /* Highlight the choice the mouse is over */ ul.fileMenuBar a.fileMenuButton:hover { background-color: transparent; border-color: #ede #777 #888 #ddd; color: #000; } /* * Indent any choice that is selected or that the mouse is over if another * choice is selected */ ul.fileMenuBar a.fileMenuButtonActive, ul.fileMenuBar a.fileMenuButtonActive:hover { background-color: #bbb; border-color: #888 #ddd #ede #777; color: #000; left: 0px; top: 0px; } /* Define all of the children of the menu bar */ ul.fileMenuChild { background-color: #bbb; border: 2px solid; border-color: #ede #777 #888 #ddd; color: #000; display: none; left: 0px; padding: 1px; 196 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-4. filemenu.css: The CSS styles for a Windows-like file menu (continued) position: absolute; top: 6px; z-index: 1000001; } /* * Here is the one hack that was necessary simply because IE does not render * the drop-down menus with enough space, so a default width is set here. * This number can be anything the developer wants/needs for a width that will * accommodate all of the text lengths in the drop downs. */ ul.fileMenuChild li { display: block; padding: 0; width: 10em; } /* * IE will ignore this rule because it does not recognize the > in the rule. * This sets the width back to an auto value for other browsers. */ ul.fileMenuChild > li { width: auto; } ul.fileMenuChild a.fileMenuItem { color: #000; cursor: default; display: block; padding: 1px 1em; text-decoration: none; white-space: nowrap; } /* Highlight the choices in the child menus */ ul.fileMenuChild a.fileMenuItem:hover, ul.fileMenuChild a.fileMenuItemHighlight { background-color: #000; color: #fff; } ul.fileMenuChild a.fileMenuItem span.fileMenuItemArrow { margin-right: -0.75em; } /* * Create the separator bars in the menus. Once again, IE does not render this * quite right, as it adds more margin underneath the bar than it should. */ ul.fileMenuChild div.fileMenuItemSeperator { border-bottom: 1px solid #ddd; border-top: 1px solid #777; margin: 2px; } Menus | www.it-ebooks.info 197 There is nothing extraordinary about any of the CSS rules in the example, and this CSS should be 100 percent cross-browser-compliant. (I tried to avoid CSS2 rules whenever I could, but sometimes it is just necessary because of Internet Explorer.) CSS2 is not implemented as completely in Internet Explorer as it is in other browsers. Because Internet Explorer currently has the largest market share among the available browsers, using too much of this standard could be problematic. This file menu example uses the Prototype library as its base, as you saw in the script elements from Example 7-3. After the Prototype library is loaded, a file that contains code for browser detection is loaded. It is shown in Example 7-5. Example 7-5. browser.js: Code for browser detection /** * @fileoverview Example 7-5, browser.js: Code for browser detection * * This file, browser.js, contains the Browser object, which can be used for browser * detecting on the client. */ /** * This object, Browser, allows the developer to check the user's client against * specific browser clients. Currently, the following checks are supported: * - isIE (is the browser an Internet Explorer browser) * - isMoz (is the browser a Mozilla-based browser) * - isOpera (is the browser an Opera browser) * - isSafari (is the browser a Safari browser) * - isOther (is the browser an unknown browser) */ var Browser = { /** * This variable stores the browser's agent. * @private */ _agent: navigator.userAgent.toLowerCase( ), /** * This variable stores the browser's version/ * @private */ _version: navigator.appVersion.toLowerCase( ), /** * This variable stores whether the browser is an Internet Explorer browser * or not. */ isIE: false, /** 198 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-5. browser.js: Code for browser detection (continued) * This variable stores whether the browser is a Mozilla-based browser or not. */ isMoz: false, /** * This variable stores whether the browser is an Opera browser or not. */ isOpera: false, /** * This variable stores whether the browser is a Safari browser or not. */ isSafari: false, /** * This variable stores whether the browser is some unknown browser or not. */ isOther: false, /** * This method, initialize, sets the boolean members of the class to their * appropriate values based on the values of the /_agent/ and /_version/ members. * * @member Browser * @constructor */ initialize: function( ) { this.isOpera = (this._agent.indexOf('opera') != -1); this.isIE = ((this._agent.indexOf('mac') != -1) && (this._version.indexOf('msie') != -1)); this.isOther = (this._agent.indexOf('konqueror') != -1); this.isSafari = ((this._agent.indexOf('safari') != -1) && (this_.agent.indexOf('mac') != -1)); this.isIE = ((this._version.indexOf('msie') != -1) && !this.isOpera && !(this._agent.indexOf('mac') != -1) && !this.isOther && !this.isSafari); this.isMoz = (!this.isOther && !this.isSafari && navigator.product && (navigator.product.toLowerCase( ) == 'gecko')); this.isOther = (!this.isIE && !this.isMoz && !this.isOpera && !this.isSafari); } }; /* use Prototype's cross-browser event handling methods for ease of use. */ try { /* * Call the initialize method of the Browser object when the load event * fires in the document */ Event.observe(document, 'load', Browser.initialize, false); } catch (ex) {} Finally, there is the JavaScript for all of the menu manipulation, shown in Example 7-6. Menus | www.it-ebooks.info 199 Example 7-6. filemenu.js: Code for manipulating the file menu /** * @fileoverview Example 7-6, filemenu.js: Code for manipulating the file menu * * This file, filemenu.js, contains the fileMenu object which is used to create * instances of a file menu on the page. */ /* Create a new class using Prototype's Class object */ var fileMenu = Class.create( ); /** * This object, fileMenu, creates the functionality for a file menu on the page. */ fileMenu.prototype = { /** * This member, _menu, holds the id of this file menu. * @private */ _menu: null, /** * This member, _activeButton, holds the element that is currently active. * @private */ _activeButton: null, /** * This method, initialize, is the constructor for the class. Any members * that need to be initialized should be here. * * @member fileMenu * @constructor * @param {String} p_element The element that represents the file menu. */ initialize: function(p_element) { /* * Currently unused, but nice to have for multiple instances of * the object */ this._menu = p_element; }, /** * This member, pageMousedown, is called on every mousedown event on the page * and determines if the menu should be reset based on where the user clicks on * the page. * * @member fileMenu * @param {Object} e The event that called the method. * @return Returns false so that no other event will be triggered. * @type Boolean * @see #getContainerWith * @see #resetButton */ pageMousedown: function(e) { var target = null; 200 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-6. filemenu.js: Code for manipulating the file menu (continued) /* Is the file menu active? */ if (!this._activeButton) return; /* Is the client Internet Explorer? */ if (Browser.isIE) target = window.event.srcElement; else target = (e.target.tagName ? e.target : e.target.parentNode); /* Is the event target the active button? */ if (this._activeButton == target) return; /* Is the target not part of the file menu? */ if (!this.getContainerWith(target, 'UL', 'fileMenuChild')) { this.resetButton(this._activeButton); this._activeButton = null; } return (false); }, /** * This method, buttonClick, is called when the user clicks on one of the * buttons that are on the main menu bar. It determines if where the user * clicked on the menu bar is the active button, or if it is a different button * and another button's drop-down menus may need to be cleaned up and reset. * * @member fileMenu * @param {Object} e The event that called the method. * @param {String} p_fileMenuId The id of the file menu that is being used. * @return Returns false so that no other event will be triggered. * @type Boolean * @see #fileMenuInit * @see #resetButton * @see #depressButton * @see #buttonMouseover */ buttonClick: function(e, p_fileMenuId) { var button = null; /* Is the client Internet Explorer? */ if (Browser.isIE) button = window.event.srcElement; else button = e.currentTarget; /* Blur the focus of the button here to remove the annoying outline */ button.blur( ); /* Is this button part of the file menu already? */ if (!button.fileMenu) { button.fileMenu = $(p_fileMenuId); /* Is this button already initialized? */ if (!button.fileMenu.isInitialized) this.fileMenuInit(button.fileMenu); } /* Is there an active button already? */ Menus | www.it-ebooks.info 201 Example 7-6. filemenu.js: Code for manipulating the file menu (continued) if (this._activeButton) this.resetButton(this._activeButton); /* Is the button already activated? */ if (button != this._activeButton) { this.depressButton(button); this._activeButton = button; } else this._activeButton = null; return (false); }, /** * This member, buttonMouseover, is called on a mouseover event on a button on * the main menu bar of the file menu. If a different button was already active, * then activate the current one instead. * * @member fileMenu * @param {Object} e The event that called the method. * @param {String} p_fileMenuId The id of the file menu that is being used. * @see #buttonClick */ buttonMouseover: function(e, p_fileMenuId) { var button = null; /* Is the client Internet Explorer? */ if (Browser.isIE) button = window.event.srcElement; else button = e.currentTarget; /* Should this button be activated? */ if (this._activeButton && this._activeButton != button) this.buttonClick(e, p_fileMenuId); }, /** * This method, depressButton, is called on a buttonClick when a new drop-down * menu needs to be activated and positioned. * * @member fileMenu * @param {Object} p_button The button that has been pressed. * @see #getPageOffsetLeft * @see #getPageOffsetTop * @see Element#addClassName * @seee Element#setStyle * @see #buttonClick */ depressButton: function(p_button) { var x, y; /* * Make the button look depressed (no, not sad) and show the drop down * associated with it */ $(p_button).addClassName('fileMenuButtonActive'); 202 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-6. filemenu.js: Code for manipulating the file menu (continued) /* Position any associated drop down under the button and display it */ x = this.getPageOffsetLeft(p_button); y = this.getPageOffsetTop(p_button) + p_button.offsetHeight; /* Is the client Internet Explorer? */ if (Browser.isIE) { x -= p_button.offsetWidth; y += p_button.offsetParent.clientTop; } $(p_button).setStyle({ left: x + 'px', top: y + 'px', display: 'block' }); }, /** * This method, resetButton, does what it says; it resets the button, closing * all submenus. * * @member fileMenu * @param {Object} p_button The button that has been pressed. * @see #closeSubFileMenu * @see Element#removeClassName * @see Element#setStyle * @see #pageMousedown * @see #buttonClick */ resetButton: function(p_button) { $(p_button).removeClassName('fileMenuButtonActive'); /* Does the button have a file menu? */ if (p_button.fileMenu) { this.closeSubFileMenu(p_button.fileMenu); $(p_button).setStyle({ display: 'none' }); } }, /** * This method, fileMenuMouseover, is called on a mouseover MouseEvent over any * of the drop-down menus in the file menu bar. Its main purpose is to close * submenus when they should no longer be active. * * @member fileMenu * @param {Object} e The event that called the method. * @see #getContainerWith * @see #closeSubFileMenu * @see Element#hasClassName */ fileMenuMouseover: function(e) { var fileMenu; /* Is the client Internet Explorer? */ if (Browser.isIE) fileMenu = this.getContainerWith(window.event.srcElement, 'UL', 'fileMenuChild'); Menus | www.it-ebooks.info 203 Example 7-6. filemenu.js: Code for manipulating the file menu (continued) else fileMenu = e.currentTarget; /* Does this menu have submenus? */ if (fileMenu.activeItem && ($(fileMenu.activeItem).hasClassName('fileMenuButton') && $(fileMenu.parentNode.firstChild).hasClassName('fileMenuItem'))) this.closeSubFileMenu(fileMenu); }, /** * This method, fileMenuItemMouseover, is called when there is a mouseover event * on one of the menu items that has a submenu attached to it. The method * calculates the position where the submenu should be placed in relation to the * menu item of the event. * * @member fileMenu * @param {Object} e The event that called the method. * @param {String} p_fileMenuId The id of the file menu that is being used. * @see #getContainerWith * @see #closeSubFileMenu * @see #fileMenuInit * @see #getPageOffsetLeft * @see #getPageOffsetTop * @see Element#hasClassName * @see Element#addClassName * @see Element#setStyle */ fileMenuItemMouseover: function(e, p_fileMenuId) { var item, fileMenu, x, y; /* Is the client Internet Explorer? */ if (Browser.isIE) item = this.getContainerWith(window.event.srcElement, 'A', 'fileMenuItem'); else item = e.currentTarget; fileMenu = this.getContainerWith(item, 'UL', 'fileMenuChild'); /* Does the file menu have an active item? */ if (fileMenu.activeItem) this.closeSubFileMenu(p_fileMenuId); /* Is there a file menu id? */ if (p_fileMenuId) { fileMenu.activeItem = item; /* Does the class name already exist? */ if (!$(item).hasClassName('fileMenuItemHighlight')) $(item).addClassName('fileMenuItemHighlight'); /* Has the sub file menu been attached already? */ if (item.subFileMenu == null) { item.subFileMenu = $(p_fileMenuId); /* Has the sub file menu already been initialized? */ if (!item.subFileMenu.isInitialized) this.fileMenuInit(item.subFileMenu); } 204 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-6. filemenu.js: Code for manipulating the file menu (continued) /* Calculate the x and y positions where the submenu should be placed */ x = this.getPageOffsetLeft(item) + item.offsetWidth; y = this.getPageOffsetTop(item); /* Is the client Opera? */ if (Browser.isOpera) { x = item.offsetWidth; y = item.offsetTop; } /* Is the client Internet Explorer? */ if (Browser.isIE) { x -= (this._activeButton.offsetWidth * 2); y -= this._activeButton.offsetHeight; } var maxX, maxY; /* Is the client Internet Explorer? */ if (Browser.isIE) { maxX = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft) + (document.documentElement.clientWidth != 0 ? document.documentElement.clientWidth : document.body.clientWidth); maxY = Math.max(document.documentElement.scrollTop, document.body.scrollTop) + (document.documentElement.clientHeight != 0 ? document.documentElement.clientHeight : document.body.clientHeight); } /* Is the client Opera? */ if (Browser.isOpera) { maxX = document.documentElement.scrollLeft + window.innerWidth; maxY = document.documentElement.scrollTop + window.innerHeight; } /* Is the client Mozilla? */ if (Browser.isMoz) { maxX = window.scrollX + window.innerWidth; maxY = window.scrollY + window.innerHeight; } maxX -= item.subFileMenu.offsetWidth; maxY -= item.subFileMenu.offsetHeight; /* Is the x coordinate bigger than the maximum it can be? */ if (x > maxX) x = Math.max(0, x - item.offsetWidth – item.subFileMenu.offsetWidth + (menu.offsetWidth – item.offsetWidth)); y = Math.max(0, Math.min(y, maxY)); /* Show the submenu */ $(item).setStyle({ left: x + 'px', top: y + 'px', display: 'block' }); Menus | www.it-ebooks.info 205 Example 7-6. filemenu.js: Code for manipulating the file menu (continued) /* Is the client Internet Explorer? */ if (Browser.isIE) window.event.cancelBubble = true; else e.stopPropagation( ); } }, /** * This method, closeSubFileMenu, hides the submenu from the user and then * turns off all references to the submenu and that it was active. * * @member fileMenu * @param {Object} p_fileMenu The file menu that is to be closed. * @see #resetButton * @see #fileMenuMouseover * @see #fileMenuItemMouseover */ closeSubFileMenu: function(p_fileMenu) { /* Does the file menu not exist or is it not active? */ if (!p_fileMenu || !p_fileMenu.activeItem) return; /* Does the file menu have an active sub file menu? */ if (p_fileMenu.activeItem.subFileMenu) { this.closeSubFileMenu(p_fileMenu.activeItem.subFileMenu); $(p_fileMenu.activeItem.subFileMenu).setStyle({ display: none }); p_fileMenu.activeItem.subFileMenu = null; } $(p_fileMenu.activeItem).removeClassName('fileMenuItemHighlight'); p_fileMenu.activeItem = null; }, /** * This method, fileMenuInit, goes through a submenu and associates all of * the children to the menu as a part of the menu. It also sets up the * offset sizes of the submenu for positioning of the menu. * * @member fileMenu * @param {Object} p_fileMenu The file menu that is to be closed. * @see Element#setStyle * @see Element#hasClassName * @see #buttonClick * @see #fileMenuItemMouseover */ fileMenuInit: function(p_fileMenu) { var itemList, spanList, textElement, arrowElement, itemWidth, w, dw; /* Is the client Internet Explorer? */ if (Browser.isIE) { $(p_fileMenu).setStyle({ lineHeight: '2.5ex' }); spanList = p_fileMenu.getElementsByTagName('span'); /* Loop through the elements */ for (var i = 0, il = spanList.length; i < il; i++) /* Does the element have the class name? */ 206 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-6. filemenu.js: Code for manipulating the file menu (continued) if ($(spanList[i]).hasClassName('fileMenuItemArrow')) { $(spanList[i]).setStyle({ fontFamily: 'Webdings' }); spanList[i].firstChild.nodeValue = '4'; } } itemList = p_fileMenu.getElementsByTagName('a'); /* Does the itemList have any elements? */ if (itemList.length > 0) itemWidth = itemList[0].offsetWidth; else return; /* Loop through the elements */ for (var i = 0, il = itemList.length; i < il; i++) { spanList = itemList[i].getElementsByTagName('span'); textElement = null; arrowElement = null; /* Loop through the elements */ for (var j = 0, jl = spanList.length; j < jl; j++) { /* Does the element have the class name? */ if ($(spanList[j]).hasClassName('fileMenuItemText')) textElement = spanList[j]; /* Does the element have the class name? */ if ($(spanList[j]).hasClassName('fileMenuItemArrow')) arrowElement = spanList[j]; } } /* Do the /textElement/ and /arrowElement/ exist? */ if (textElement && arrowElement) { $(textElement).setStyle({ paddingRight: (itemWidth - (textElement.offsetWidth + arrowElement.offsetWidth)) + 'px' }); /* Is the client Opera? */ if (Browser.isOpera) $(arrowElement).setStyle({ marginRight: '0' }); } /* Is the client Internet Explorer? */ if (Browser.isIE) { w = itemList[0].offsetWidth; $(itemList[0]).setStyle({ width: w + 'px' }); dw = itemList[0].offsetWidth - w; w -= dw; $(itemList[0]).setStyle({ width: w + 'px' }); } p_fileMenu.isInitialized = true; }, /** * This method, getContainerWith, finds the element with a given tag and * class name and returns the discovered element or or null. * * @member fileMenu Menus | www.it-ebooks.info 207 Example 7-6. filemenu.js: Code for manipulating the file menu (continued) * @param {Object} p_element The element to check. * @param {String} p_tagname The tag name to look for. * @param {String} p_className The class name to look for. * @return Returns the discovered element, if found, or null. * @type Object * @see Element#hasClassName * @see #pageMousedown * @see #fileMenuMouseover * @see #fileMenuItemMouseover */ getContainerWith: function(p_element, p_tagname, p_className) { /* Traverse the element tree while there are elements */ while (p_element) { /* Does the element have the correct tag name and class name? */ if (p_element.tagName && p_element.tagName == p_tagname && $(p_element).hasClassName(p_className)) return (p_element); p_element = p_element.parentNode; } return (p_element); }, /** * This method, getPageOffsetLeft, returns the left offset of the element * in relation to the page. * * @member fileMenu * @param {Object} p_element The element to get the offset from. * @return Returns the left page offset of the element. * @type Integer * @see #getPageOffsetLeft * @see #depressButton * @see #fileMenuItemMouseover * @see #fileMenuItemMouseover */ getPageOffsetLeft: function(p_element) { var x; x = p_element.offsetLeft; /* Is the client not Mozilla and does the element have a parent offset? */ if (!Browser.isMoz && p_element.offsetParent) x += this.getPageOffsetLeft(p_element.offsetParent); return (x); }, /** * This method, getPageOffsetTop, returns the top offset of the element in * relation to the page. * * @member fileMenu * @param {Object} p_element The element to get the offset from. * @return Returns the left page offset of the element. * @type Integer * @see #getPageOffsetLeft 208 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-6. filemenu.js: Code for manipulating the file menu (continued) * @see #depressButton * @see #fileMenuItemMouseover * @see #fileMenuItemMouseover */ getPageOffsetTop: function(p_element) { var y; y = p_element.offsetTop; /* Is the client not Mozilla and does the element have a parent offset? */ if (!Browser.isMoz && p_element.offsetParent) y += this.getPageOffsetTop(p_element.offsetParent); return (y); } }; /* Create a new instance of the fileMenu class (this calls fileMenu.initialize( )) */ var menu = new fileMenu('fileMenu'); try { /* * Set an event listener on the document for all mousedown events and * have the system call the /pageMousedown( )/ method, binding it to the menu * object that was just created. This allows for the creation of multiple * file menus, if there is ever a need. */ Event.observe(document, 'mousedown', menu.pageMousedown.bind(menu), true); } catch (ex) {} I have checked the XHTML, CSS, and JavaScript for this file menu and they work with Opera 9.01+, Firefox 1.5+, and Internet Explorer 6+. Netscape Browser 8.1 has problems rendering submenus from the drop-down menus with the proper CSS rules applied to them. Figure 7-6 shows how this file menu would normally appear in the browser. Figure 7-6. A file menu that emulates the Windows file menu Menus | www.it-ebooks.info 209 Creating a file menu that functions like those that most users are comfortable with in Windows applications is a good step toward providing a seamless transition from the desktop to Ajax web applications. But now we need some Ajax for the file menu, right? If this file menu was a bit more complicated, it would add some size to the page download, which adversely affects download times. Slowing down application speed is never a good thing. Adding Ajax to the menu A way to reduce this file size is to load only the main part of the menu, and load the other parts of the menu when the user clicks to activate them. Here is where we can use some Ajax. By adding additional event listeners to the click MouseEvent, we can grab the necessary drop-down and submenus that are requested. That way, once they have been loaded the first time, there is no need to load them again, providing the speed we want from our application. First, we need to alter the XHTML page, filemenu.html, like this: Alteration offilemenu.html

This is a File Menu example

All of the submenu data has been removed, making this download a little bit smaller. As I said, the benefits come when you have larger and more complex file menus in your application. Nothing changes with the CSS rules in filemenu.css, or in the filemenu.js JavaScript file. The other change, you may have noticed, is that we added another JavaScript file to handle the Ajax, shown here: /** * This function, loadMenu, calls the server via an /XMLHttpRequest/ for a new * menu if one has not already been called for with the passed /p_id/ parameter. * * @param {String} p_id The id of the container to hold the menu. */ function loadMenu(p_id) { /* Is the /innerHTML/ blank? */ if ($(p_id).innerHTML == '') new Ajax.Request('get_sub_menu.php', { method: 'post', parameters: 'id=' + p_id, onSuccess: function(xhrResponse) { $(p_id).innerHTML = xhrResponse.responseText; }, Menus | www.it-ebooks.info 211 onFailure: function(xhrResponse) { $(p_id).innerHTML = xhrResponse.statusText; } }); } The only assumption this JavaScript makes is that the get_sub_menu.php file called on the server must return formatted XHTML that can be directly inserted into the innerHTML of the submenu. For example, the response for loadMenu('editSub'); would look like this:
  • Copy
  • Cut
  • Paste
  • The other thing to notice with the loadMenu( ) function is that it first checks whether the submenu being requested has already been loaded. This keeps the application from repeatedly calling the Ajax function and bogging down the server with unwanted requests. One last thought, and then we will be done with menus. To take the file menu example fully to the Web 2.0 level, we could set some CSS rules to make the submenus slightly less opaque. This will also enhance the idea that the web application can do everything (and sometimes more) than its desktop counterpart can do. Tabs One good way to separate related content on your site is to use tabs. You can create tabs simply by using CSS to style an XHTML list, or you can make them with images. Images were always the way to create tabs in a classic web development environment, but I will show you a newer spin on the image tab technique. The goal of all tab navigation, as with menu navigation, is to allow the tabs to degrade with browsers that do not support the CSS techniques that are used. 212 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info CSS to the Rescue The simplest way to build tabs is to add a little style to an XHTML list. Starting to notice a theme yet? By not using JavaScript, the developer makes his application more accessible to users. Obviously, once we throw Ajax into the mix, we will use JavaScript; however, even if the Ajax fails, the tabs might still work separately. Figure 7-7 shows a few examples of what tabs can look like using CSS and XHTML lists. These tabs are no more complicated than the first menus I showed earlier in this chapter. Figure 7-7. Sample CSS tabs using XHTML lists Both of the lists in Figure 7-7 use the following XHTML list as their underlying structure: Tab #1 is created using the following CSS rules: #tabList { border-bottom: 1px solid #787; font: bold 1em Arial, sans-serif; margin-left: 0; padding: 3px 0 3px 1em; } Tabs | www.it-ebooks.info 213 #tabList li { display: inline; list-style: none; margin: 0; } #tabList li a { background-color: #bfb; border: 1px solid #787; border-bottom: none; margin-left: 3px; padding: 3px .5em; text-decoration: none; } #tabList li a:link { color: #484; } #tabList li a:visited { color: #676; } #tabList li a:hover { background-color: #ada; border-color: #272; color: #000; } #tabList li a#current, #tabList li a#current:hover { background: white; border-bottom: 1px solid #fff; color: #000; cursor: default; } This first tab navigation is very simple in nature. The trick is in switching colors based on where the mouse is, and changing the borders along with it. Making the top border of #current white gives the illusion that it is part of the rest of the page, while the other tabs sit behind it. Tab #2 uses the following CSS rules:* #tabMenu { background: #7a7; border-top: 1px solid #333; height: 2.5em; padding: 0; } * The CSS rules for tab #2 were originally from Copongcopong’s Under Tabs (http://web.archive.org/web/ 20050221053356/www.klockworkx.com/css/under-tab.htm). 214 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info #tabList { display: block; font: 1em Arial, sans-serif; margin-top: -1px; padding: 0 0 0 1em; } #tabList li { float: left; list-style: none; } #tabList a { background-color: #cfc; border: 1px solid #aca; border-top: 1px solid #333; color: #000; display: block; margin: 0; padding: 1px 6px; text-decoration: none; } #tabList a:hover { background-color: #9b9; border: 1px solid #333; color: #333; padding: 1px 6px; } #tabList li a#current { background: #fff; border: 1px solid #333; border-top: 1px solid #fff; cursor: default; } #tabList li#active { border-bottom: 2px solid #777; border-right: 2px solid #777; } With this tab navigation, I wanted to give the illusion of depth by dropping a shadow on the active tab. To do this, I had to float the individual
  • elements to the left, and then shift the entire
      element up one pixel. I created the tabs in much the same way as I did in tab #1. I created the shadow by putting right and bottom borders on the #active li element. The floating nature of the
    • elements allowed the border of the #active element to be visible from underneath. What’s frustrating when creating tabs using CSS is that all tabs are rectangular. Until CSS3 style rules become a recommendation and we can create curves using CSS, we have only one option: images. Tabs | www.it-ebooks.info 215 Image Tabs I told you before that we will not be able to use images for tabs in the same way we did in the earlier days of web design. One option is to use a technique similar to the one we used for our image navigation bar: creating multiple images and changing them using :hover in CSS. Primarily we want to avoid having to rely on JavaScript to change the images (the old rollover technique). We will create all views of our tab in one image, and clip out everything but the part of the image we want for any given tab state. It sounds much more complicated than it is. Figure 7-8 shows an example of a tab with all states in one image. Figure 7-8. A tab with multiple states in one image First we need to set up our XHTML structure as follows: This is almost the same XHTML list as before, but this time the id attribute was added to each element. To utilize our image correctly, we need to know the size of an individual tab state. In this case, it is 27 pixels high and 95 pixels wide. The CSS rules for this technique look like this: #tabMenu { border-bottom: 1px solid #700; padding: 1em 1em 0 1em; 216 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info margin: 0; } #tabList { height: 26px; margin: -1px; overflow: hidden; padding: 0px; } #tabList a { background: top left no-repeat url('tabs.png'); background-position: 0 -54px; color: #fff; float: left; margin-right: 5px; overflow: hidden; padding: 6px 0px; text-align: center; text-decoration: none; width: 95px; } #tabList a:hover { background-position: 0 -27px; color: #ccc; } #tabList a:active, #tabList a.selected { background-position: 0 0px; color: #000; cursor: default; } The
        element #tabList is set to a height of 26 pixels (not 27 pixels, because we want the image to meet up with the bottom horizontal line). Then the links in the #tabList are set. The element has its background set with the tab image, and then it is positioned down to the bottom of the three tabs. The width of the tab is set to 95 pixels here, and the other important rule is to set the overflow to hidden. Because we’re doing this, the user can view only one part of the image at any time. For the a:hover, the background-position is shifted down 27 pixels, which is to the middle tab in the image. Finally, in the a:active and a.selected rules, the background-position is set to the top of the image. Everything but the top tab is hidden (clipped) from view. Figure 7-9 shows how these tabs would look. Now, it is one thing to build tabbed navigation—or menu bars, for that matter—but they need to do something. Browsers that do not support the CSS to build the tabs can still follow the link to navigate the application. This is also the case for users who have JavaScript turned off. For the rest of us, we need these tabs to be more functional. Tabs | www.it-ebooks.info 217 Figure 7-9. Image tabs using CSS The Tab Content I will refer to the area in which the data is placed or viewed as the tab content. These
        elements contain everything the developer wants the user to see when the user selects one of the navigation tabs. Therefore, we need to change the display of the element in relation to the tab that the user clicks. But first things first; we need to construct the tab content. For example:

        XHTML

        Cascading Style Sheets (CSS)

        JavaScript

        The DOM

        XML

        This structure part is easy enough, but next you need to have a CSS rule to hide all of the tab content sections from view. Something like this would work: #tabContents div { display: none; } 218 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info This hides all of the
        elements that are contained in the #tabContents div. We will rely on JavaScript to highlight one of the tabs when the page loads. Example 7-7 shows this JavaScript code for making the tabs dynamically functional. This JavaScript requires the Prototype library to be loaded in order to work. Example 7-7. tabs.js: The JavaScript for dynamic tab content /** * @fileoverview Example 7-7, tabs.js: The JavaScript for dynamic tab content. * * This file, tabs.js, contains the tabNavigation object which is used to create * instances of a tab navigation system on the page. */ /* Create a new class using Prototype's Class object */ var tabNavigation = Class.create( ); /** * This object, tabNavigation, provides the developer with the means of * creating a tabbed navigation system on the page. */ tabNavigation.prototype = { /** * This member, _tabs, holds the id of the tabbed navigation. * @private */ _tabs: null, /** * This member, _previousTab, holds the id of the last tab clicked by the user. * @private */ _previousTab: null, /** * This member, initialize, is the constructor for the class. Any members that * need to be initialized are done here, as well as any other initial * housecleaning. * @member tabNavigation * @constructor * @param {String} p_id The id of the element that represents the tabbed * navigation. * @param {String} p_startTab The id of the starting tab element. * @see #expandTab */ initialize: function(p_id, p_startTab) { this._tabs = p_id; /* Get a list of link elements found in the tab list */ var tabLinks = $(this._tabs).getElementsByTagName('a'); /* Add a click event to all of the link elements */ for (var i = tabLinks.length - 1; i >= 0;) tabLinks[i--].setAttribute('onclick', 'return tabs.expandTab(this.id);'); /* Expand the starting tab */ this.expandTab(p_startTab); }, /** Tabs | www.it-ebooks.info 219 Example 7-7. tabs.js: The JavaScript for dynamic tab content (continued) * This method, expandTab, is called on all click MouseEvents associated with * the tab navigation and hides the contents of the previous tab before showing * the contents of the current tab. * * @member tabNavigation * @param {String} p_linkId The id of the tab to expand. * @return Returns false so that no other events fire after this one. * @type Boolean * @see #highlightTab * @see Element#setStyle */ expandTab: function(p_linkId) { var catId; this.highlightTab(p_linkId); /* Is there a previous tab selected */ if (this._previousTab) $(this._previousTab).setStyle( { display: 'none' }); catId = p_linkId + 'Content'; $(catId).setStyle({ display: 'block' }); this._previousTab = catId; return (false); }, /** * This member, highlightTab, is called from the expandTab method and removes the * CSS rule for highlighting on all of the tabs and then sets it on the current * tab. * * @member tabNavigation * @param {String} p_linkId The id of the tab to expand. */ highlightTab: function(p_linkId) { var tabLinks = $(this._tabs).getElementsByTagName('a'); /* Loop through the list of elements */ for (var i = tabLinks.length - 1; i >= 0;) $(tabLinks[i--]).removeClassName('selected'); $(p_linkId).addClassName('selected'); } }; var tabs; try { /* * Set an event listener on the document for the load event and have the system * create a new instance of the tabNavigation class (this calls * tabNavigation.initialize( )). This allows for the creation of multiple file * menus, if there is ever a need. */ Event.observe(window, 'load', function( ) { tabs = new tabNavigation('tabList', 'xhtml'); }, true); } catch(ex) {} 220 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Separating the content of the page into tabbed sections is a start to satisfying the following WAI-WCAG 1.0 guideline: • Priority 2 checkpoint 12.3: Divide large blocks of information into more manageable groups where natural and appropriate. This exposes tab content that is associated with a tab in the navigation. For now, let’s just assume that the content was already there at page load. We will discuss Ajax solutions to this in Chapter 8. Navigation Aids We have discussed the major navigation aids—menus and tabs—but plenty of other navigational components can appear in a web application. Think of a site that you believe has good navigation, i.e., it’s easy to navigate and find things in it because it is organized and gives you the tools needed for navigation. Did you think of a site that used a tree of links or vertical links? Maybe the site broke a page into smaller chunks with page links, what I call paged navigation. More than likely, the site had some simple tools such as breadcrumbs and in-site links. A good site will have some combination of navigational components to provide smooth and easy navigation. Users never want to feel lost in an application, and any visual cue (breadcrumbs, a tree of links) is welcomed. Breadcrumbs Breadcrumbs are visual aids that help the user keep track of where he is and how he got there. Figure 7-10 shows an example of breadcrumbs found on Amazon.com. Figure 7-10. Breadcrumbs on Amazon.com Navigation Aids | www.it-ebooks.info 221 These breadcrumbs link back to the preceding sections relative to the user’s current location. In this case, the user is under the JavaScript section, and the links take him back to Scripting & Programming, Web Development, Computers & Internet, and Books. Breadcrumbs are simple to create using XHTML lists and a little CSS. Consider the following: We can style this list into a list of breadcrumbs, but that is not what we want to concentrate on. How is the list created? If the entire page is loaded, any server-side script can generate the correct XHTML list to display the breadcrumbs. What happens, however, when the entire page is not refreshed? This book is about Ajax, after all. First, let’s be complete in our discussion of building breadcrumbs. You can style the preceding list into breadcrumbs using the following CSS rules: #breadContainer { margin-left: 10px; } #breadList { list-style: none; margin: 0; padding: 0; } #breadList li { display: inline; margin: 0; padding: 0; } #breadList li:before { content: "\00BB\0020"; } #breadList li:first-child:before { content: ""; } /* * The following is an ugly IE hack that I wish I didn't have to do, but IE and * CSS don't mix well yet */ 222 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info /* This rule is for all IE browsers*/ * html #breadList li { border-left: 1px solid black; margin: 0 0.4em 0 -0.4em; padding: 0 0.4em 0 0.4em; } /* Win IE browsers - hide from Mac IE\*/ * html #breadList { height: 1%; } * html #breadList li { display: block; float: left; } /* End the hide from Mac*/ /* This rule is for Mac IE 5*/ * html #breadList li:first-child { border-left: 0; } Fat Erik 5 wrote this CSS, and you can find it on Listamatic (http://css.maxdesign.com. au/listamatic/index.htm). The only problem with this CSS is that it does not work in Internet Explorer because IE does not support many pseudoselectors, and to be more specific, it does not support the :first-child pseudoselector. All we can do is hope that Internet Explorer 8 (or whatever the next fix/version of IE is called) fixes this dilemma. As you can see from the preceding example, the cross-browser solution is to use a CSS hack to get Internet Explorer to produce something between the elements. The left border of the element does the trick in this case. It isn’t pretty, but then again, not much about Internet Explorer’s CSS2 support is pretty. The other thing you might have noticed is that the solution to putting something between elements is to use the content property to include the characters \00BB\0020 before the
      • element. \00BB is the hexadecimal equivalent of the right-angle doublequote character >>, and \0020 is a space. A good list of ASCII character codes and their decimal and hexadecimal values is available at http://ascii.cl/htmlcodes.htm. OK, now that we’ve styled the list, let’s get back to the Ajax part of this. The simplest solution is for the response from the server to supply the breadcrumbs needed for the page. Something like this will do: Navigation Aids | www.it-ebooks.info 223

        All about the content property

        With this kind of response, our JavaScript simply needs to grab the different sections of code and put the contents where they need to go. There is one problem with this approach, though; Internet Explorer does not follow the Document Object Model (DOM) 2 core recommendations from the W3C. At least, Internet Explorer does not give a developer the means to import a node from one namespace into the hierarchy of another namespace. So, we first need to write a function that mimics the standard method that the other browsers use, importNode( ). Example 7-8 shows the function for Internet Explorer. Example 7-8. An importNode( ) function for Internet Explorer that mimics what the standard importNode( ) method does /* * Example 7-8, An importNode( ) function for Internet Explorer that mimics what the * standard /importNode( )/ method does. */ /* Can we use /importNode( )/? [if not, this must be an IE client] */ if (!document.importNode) { /* * Create a function that does what should already be part of IE's * implementation of the DOM. */ /** * This function, importNode, does what should already be part of IE's * implementation of the DOM. * * @param {Object} p_element The element to be imported. * @param {Boolean} p_allChildren Variable to tell the function if all * childNodes should also be imported. * @return The newly imported node. * @type Object */ function importNode(p_element, p_allChildren) { /* Find the element's type */ switch (p_element.nodeType) { case 1: /* NODE_ELEMENT */ var newNode = document.createElement(p_element.nodeName); /* Does the element have any attributes to add? */ if (p_element.attributes && p_element.attributes.length > 0) /* Loop through the element's attributes */ for (var i = 0, il = p_element.attributes.length; i < il;) newNode.setAttribute(p_element.attributes[i].nodeName, p_element.getAttribute( p_element.attributes[i++].nodeName)); 224 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Example 7-8. An importNode( ) function for Internet Explorer that mimics what the standard importNode( ) method does (continued) /* Are we going after children too, and does the node have any? */ if (p_allChildren && p_element.childNodes && p_element.childNodes.length > 0) /* Loop through the element's childNodes */ for (var i = 0, il = p_element.childNodes.length; i < il;) newNode.appendChild(importNode(p_element.childNodes[i++], p_allChildren)); return newNode; break; case 3: /* NODE_TEXT */ case 4: /* NODE_CDATA_SECTION */ return document.createTextNode(p_element.nodeValue); break; } }; } Now that we have a way to import nodes for all browsers, let’s look at the code for dynamically creating our breadcrumbs and data: new Ajax.Request('getData.php', { method: 'post', parameters: 'data=' + dataId, onSuccess: function(xhrResponse) { var response = xhrResponse.responseXML; var newNode; /* Is this browser not IE ? */ if (!window.ActiveXObject) { newNode = document.importNode(response.getElementsByTagName( 'breadcrumbs')[0].childNodes[1], true); $('breadContainer').appendChild(newNode); newNode = document.importNode(response.getElementsByTagName( 'page')[0].childNodes[1], true); $('page').appendChild(newNode); } else { newNode = importNode(response.getElementsByTagName( 'breadcrumbs')[0].childNodes[0], true); $('breadContainer').appendChild(newNode); newNode = importNode(response.getElementsByTagName( 'page')[0].childNodes[0], true); $('page').appendChild(newNode); } }, onFailure: function(xhrResponse) { $('page').innerHTML = xhrResponse.statusText; } }); Navigation Aids | www.it-ebooks.info 225 You can put this XMLHttpRequest wherever it needs to go when the request for new data is made. If this technique is not for you, you may want to consider this alternative. The data comes back via an XMLHttpRequest, and once it has been received, you launch a second XMLHttpRequest asking for the breadcrumbs to the data just loaded. This way, the data is sent in smaller chunks and can be less complicated. That means the client code will be faster and easier as well. Both methods achieve the same goal: dynamic breadcrumbs for the user. Links at the Bottom Another useful navigation aid is links found at the bottom of a page. Instead of forcing the user to scroll to the top of a page that may be a few screens long, the developer can allow the user to navigate from the bottom as well. These types of links are usually lists separated by a pipe character (|). A typical list looks like this: The CSS rules you can use to style a list like this are the same as the rules for breadcrumbs, except for what is displayed as content between the list elements and some other basic style changes. These are easy changes, as shown here: # linksContainer { margin-top: 2em; text-align: center; } # pipeList { list-style: none; margin: 0; padding: 0; } 226 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info # pipeList li { display: inline; margin: 0; padding: 0; } # pipeList li:before { content: "| "; } # pipeList li:first-child:before { content: ""; } /* * The following is an ugly IE hack that I wish I didn't have to do, but IE * and CSS don't mix well yet. */ /* This rule is for all IE browsers*/ * html # pipeList li { border-left: 1px solid black; margin: 0 0.4em 0 -0.4em; padding: 0 0.4em 0 0.4em; } /* Win IE browsers - hide from Mac IE\*/ * html # pipeList { height: 1%; } * html # pipeList li { display: block; float: left; } /* End the hide from Mac*/ /* This rule is for Mac IE 5*/ * html # pipeList li:first-child { border-left: 0; } The JavaScript and Ajax portions of the code for creating dynamic lists at the bottom of a page are similar to the code for creating breadcrumbs. The only changes are where the different XHTML sections are placed in the application. In fact, you could add the bottom links to the feed that sends breadcrumbs and data so that you need to import only one more element. Navigation Aids | www.it-ebooks.info 227 It’s your choice how many XMLHttpRequest calls you want to make to the server, and how complicated you want the server scripting to be. Keep in mind that the server scripting must be more sophisticated if you make separate calls, and will have to extrapolate what it needs to send back multiple times—once for the data, once for the breadcrumbs, and once for the links at the bottom. Paged Navigation The premise behind paged navigation (as I like to call it) is that the user does not have to scroll through pages and pages of content. Rather, the content is broken up into pages, and navigation is given in the form of page numbers (usually) that allow the user to move through the information one page at a time. Of course, you could use other forms of navigation as well, but the point is that scrolling is minimized for easier reading. And everyone has seen and used this technique, maybe without realizing it—search engines use it for all of their search results. Separating the page content into multiple navigable pages satisfies the following WAI-WCAG 1.0 guideline: • Priority 2 checkpoint 12.3: Divide large blocks of information into more manageable groups where natural and appropriate. You can accomplish this kind of presentation and navigation using different methods. The methods I will discuss rely on a server-side script being able to split up the content for the client. It is easier for the server side of things to know where to split up text than it is for the client. Perhaps the easiest way is to have a single page to dispense the information one chunk at a time based on the variable passed to it in the query string. This technique is applied in many places, especially on sites that specialize in articles, essays, and other such papers. An example would be a link that looks like this: http://www.oreillynet.com/pub/a/oreilly/tim/news/2005/09/30/what-is-web-20.html ?page=2 In this case, the variable is page, and the user has requested page 2. We’ll come back to this method in a second, but first we should talk about another method. The second way you can achieve paged navigation is to have the page load with all the data at once, separating it by page into
        elements. Then, using CSS and JavaScript, the client can hide and show the page that is requested. The advantage to this method is that once the data is on the client, hiding and showing pages is nearly instantaneous. The downside is that the user must wait until all the data is loaded before viewing it. Let’s set this up for all the viewers at home. First, this is how part of the page will look when it is loaded: 228 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info

        This is page one.

        This is page two.

        This is page three.

        This is page four.

        This is page five.

        onclick="return turnPage(2);">2 onclick="return turnPage(3);">3 onclick="return turnPage(4);">4 onclick="return turnPage(5);">5 The CSS code to make this navigation look correct is the same as what Fat Erik 5 used for the breadcrumbs example discussed earlier. Now we add the function turnPage( ): /** * This function, turnPage, changes the contents on the page to the desired "page" * number that is passed to it. * * @param {Integer} p_number The number of the page to go to. * @return Returns false so that no other event is fired after this one. * @type Boolean * @see Element#setStyle */ function turnPage(p_number) { var pages = $('article').getElementsByTagName('div'); /* Loop through the list of
        elements */ for (var i = 0, il = pages.length; i < il; i++) { $(pages[i]).setStyle({ display: 'none' }); Navigation Aids | www.it-ebooks.info 229 $('l' + (i + 1)).innerHTML = '' + (i + 1) + ''; } $('l' + p_number).innerHTML = p_number; $('page' + p_number).setStyle({ display: 'block' }); return (false); } This is an example of a paged navigation solution using DHTML techniques. The premise is to have everything on the page, and show only what the user asks for. For an Ajax solution, a good approach is to combine the premise of both of these techniques. The trick is to combine the page request from our first example—passing the page number in the query string—with the idea of hiding all content but the requested page. For this to work, we need to alter the turnPage( ) function to call an XMLHttpRequest for the page information and then display it. The good news with this kind of technique is that there won’t be any blank pages or flickering, as something else will always be on the screen until the client has downloaded the new data. We should change the turnPage( ) function like this: /** * This function, turnPage, changes the contents on the page to the desired * "page" number that is passed to it. * * @param {Integer} p_number The number of the page to go to. * @return Returns false so that no other event is fired after this one. * @type Boolean * @see Element#setStyle * @see Ajax#Request */ function turnPage(p_number) { var pages = $('pagedNavList').getElementsByTagName('li'); /* Loop through the list of
      • elements */ for (var i = 0, il = pages.length; i < il; i++) { $(pages[i]).setStyle({ display: 'none' }); $('l' + (i + 1)).innerHTML = '' + (i + 1) + ''; } /* Has this page already been fetched once? */ if ($('page' + p_number).innerHTML == '') { new Ajax.Request('article.php', { method: 'post', parameters: { page: p_number }, onSuccess: function(xhrResponse) { var response = xhrResponse.responseXML; var newNode; /* Is this browser not IE ? */ if (!window.ActiveXObject) { 230 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info newNode = document.importNode(response.getElementsByTagName( 'page')[0].childNodes[1], true); $('page' + p_number).appendChild(newNode); } else { newNode = importNode(response.getElementsByTagName( 'page')[0].childNodes[0], true); $('page' + p_number).appendChild(newNode); } $('l' + p_number).innerHTML = p_number; $('page' + p_number).setStyle({ display: 'block' }); }, onFailure: function(xhrResponse) { $('page').innerHTML = xhrResponse.statusText; } }); } else { $('l' + p_number).innerHTML = p_number; $('page' + p_number).setStyle({ display: 'block' }); } return (false); } This function loads the different pages only when they are requested, and only once per request. This cuts down on the initial server download, speeding up the application as a whole. This also uses the importNode( ) function from earlier in cases where the browser is Internet Explorer. You will want to remember this function because it will pop up a lot throughout the rest of this book. Navigation Boxes There is one more popular navigational aid that I have seen on numerous web sites: navigation boxes, the boxes on the left or right side of a page that contain vertical lists or trees of links to aid in site navigation. The navigation boxes are usually easy to spot; in fact, the user’s eyes might be drawn to them based on how they are styled and placed in the application. These lists and trees are usually more detailed links to pages in the application that can be found at a lower level in the site hierarchy. Trees, trees, trees The first navigation box solution that I will discuss is the tree of lists. These trees usually function in the same manner that users are familiar with in the file explorers they use to navigate their operating systems. A hierarchy of lists is displayed, and a plus sign (+) and minus sign (–) are usually delineated to alert the user that part of the hierarchy can be shown or hidden. Figure 7-11 shows an example of a typical tree. Navigation Aids | www.it-ebooks.info 231 Figure 7-11. A typical tree of lists used to navigate an application As with all of the other navigation aids we’ve looked at so far, the ideal way to build a tree is to use an XHTML list. This ensures that we have a degree of backward compatibility with browsers that can’t or don’t support JavaScript or CSS. The list for our tree looks like this: Trees can be complicated widgets to build. Going into the details of building a tree using CSS and JavaScript is beyond the scope of this book. Instead, I will focus on the Ajax part of dealing with trees, but first we must have a tree before we can build Navigation Aids | www.it-ebooks.info 233 it dynamically. To this end, I have decided to use the Zapatec DHTML Tree (http:// www.zapatec.com/website/main/products/prod3) as the basis for this example. The Zapatec DHTML Tree uses an XHTML list for its structure, which simplifies tree creation and facilitates control of list item content (you can use any XHTML markup). Also, a variety of browsers support this software, and those that do not will at least view the list. First, we must include the proper Zapatec header files on our page: Also assume that the Prototype library was loaded before these files are introduced to the page. Then, once the page has loaded, the Zapatec.Tree object is created, and the tree will function on the page: var navTree; /* hold the Zapatec.Tree object */ /** * This function, bodyOnLoad, is called on the load event of the document and * creates a new instance of the Zapatec.Tree object. * * @see Zapatec#Tree */ function bodyOnload( ) { navTree = new Zapatec.Tree('navTree', { initLevel: 0 }); } /* use Prototype's cross-browser event handling methods for ease of use. */ try { /* Call the bodyOnload function when the load event fires in the document */ Event.observe(window, 'load', bodyOnload( ), false); } catch(ex) {} For a list of all the features and functions available with the Zapatec DHTML Tree, see the documentation that is on the web site and that accompanies the software download. This documentation describes how to programmatically manipulate the tree both at page load and during the lifetime of the application. 234 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info We are most interested in loading parts of our tree through an XMLHttpRequest object. As with our past navigational aids, by creating our subtrees only when they are requested, we dramatically reduce the tree load time. The technique for tree navigation with Ajax is just like that for file menu navigation. Every submenu in our tree will need to have a unique id for our code to work. Then we can use our same loadMenu( ) function from the file menu navigation to load the individual submenus. All we have to change is where the XMLHttpRequest call goes in the function. There is only one downside to this Ajax technique. The root of the submenu (the link you click to open the submenu) can still contain a link to a different page within the application or outside on the Web only if the loadMenu( ) function does not return false. Otherwise, the function will inadvertently cancel the click event on the link. Vertical lists Vertical lists are basically trees with different style rules, though they are rarely more than two levels deep. You can also think of vertical lists as menu bars flipped on their sides. By changing the style rules on the Zapatec DHTML Tree, you can easily create a vertical list, as Figure 7-12 shows. Figure 7-12. An example of a vertical list using the “wood” Zapatec theme Even if the vertical list needs to be a little more complicated, such as with a more complex hierarchy, we can use the rules we applied to all our other navigation aids. Everything we did to create an Ajax-enabled tree also applies to a vertical list. This type of navigation is, in many ways, a smaller version of another type of page navigation: accordion navigation. Navigation Aids | www.it-ebooks.info 235 Accordion Navigation Accordion navigation is much like paged navigation, but instead of numbers acting as the navigation aids, some kind of bar separates the content. That is the only real difference, though accordion navigation has a Web 2.0 feel that paged navigation does not. This is because some kinds of effects usually accompany the switching of content from one part to the next. Accordions push content up and down as it is exposed and hidden, creating an effect that marginally resembles an accordion. To create this type of navigation—which is graphically more challenging (only because of the effects attached to it)—we will abandon the use of XHTML lists in favor of a more chunks-of-data type structure. The following markup shows what I mean by chunks-of-data structuring:

        Curabitur pharetra, nunc vitae pellentesque ultrices, ligula tortor mollis eros, et mattis sem diam ac orci. Aenean vestibulum aliquam enim. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec accumsan, enim sit amet aliquet congue, massa ante iaculis sem, id dictum augue ligula sit amet elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed sodales massa sit amet eros. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur gravida. Vivamus mollis. Proin leo pede, tincidunt id, porttitor quis, pharetra sit amet, quam. Quisque a odio sed augue varius ultrices. Praesent odio. Mauris viverra nunc in lacus. Fusce in mi. Nullam urna sapien, porttitor sit amet, facilisis nec, congue quis, pede.

        Vestibulum nec pede. Fusce dui ipsum, imperdiet gravida, interdum eu, imperdiet a, nisl. Fusce in enim. Suspendisse non velit. Mauris rhoncus dictum quam. In mollis. Etiam eu erat in nisi luctus scelerisque. Nulla facilisi. Nam mattis auctor nulla. Aenean risus lacus, consequat eget, consequat sit amet, scelerisque vitae, turpis. Suspendisse tortor elit, pellentesque id, suscipit at, consequat ac, elit. Sed massa leo, molestie sed, fermentum non, dignissim ac, nisi. Sed tincidunt. Suspendisse tincidunt congue nisl.

        236 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info

        Nullam varius rhoncus urna. Aliquam erat volutpat. Integer pulvinar scelerisque purus. Sed euismod erat in mi. Nam dolor odio, ullamcorper nec, mattis eu, placerat a, ipsum. Curabitur ut quam. Fusce vitae neque. Donec nec mi eu orci auctor facilisis. Vivamus porta. Donec tincidunt. Sed varius, neque sed placerat egestas, arcu massa feugiat diam, nec ultricies elit diam sed lectus. In hac habitasse platea dictumst. Quisque id ante. Ut vulputate, magna a convallis tincidunt, leo eros ullamcorper turpis, lacinia lacinia urna erat quis pede. Fusce eleifend, tellus eu sollicitudin dapibus, eros tellus fringilla libero, sed facilisis lacus felis sit amet lacus. Integer ullamcorper turpis scelerisque massa pellentesque hendrerit. Donec dapibus lorem quis massa. Sed in ante non leo tristique suscipit. Cras eu magna elementum mauris venenatis sagittis. Nulla euismod justo sit amet elit.

        This is the general idea, and there would obviously be more sections. The chunks I am referring to are the
        elements that are labeled with the id attribute part1..partn. Each represents a chunk of data that can stand on its own, away from everything else. The idea behind accordion navigation is that a header of some sort represents the section. In our example, the header is the
        element with id attribute values that start with nav. These will be the only parts that are shown until they are clicked. Once they are clicked, the content of the section slides down to be displayed to the user. The accordion comes in when one section of content slides up and out of view as another section slides down and into view. This first thing we need to do is to hide the content sections once the page has loaded. You must wait until after the page is loaded so that the browser knows the height of each section. Then you can hide them: /** * This function, bodyOnload, hides all of the content from the user. * * @see Element#setStyle */ function bodyOnload( ) { $('content1').setStyle({ display: 'none' }); $('content2').setStyle({ display: 'none' }); $('content3').setStyle({ display: 'none' }); $('content4').setStyle({ display: 'none' }); $('content5').setStyle({ display: 'none' }); } You may have noticed that the structure has click MouseEvents attached to it, and these call the Effect object’s Accordion( ) method. The Effect object is part of the Navigation Aids | www.it-ebooks.info 237 script.aculo.us JavaScript library, which is based on the Prototype library. So, the first thing we must do is load the libraries: With script.aculo.us, we need only the effects features from the library, so that is all we load. Now we can write our Accordion( ) method, which is created as an addition to the Effect object, as shown in Example 7-9. Example 7-9. An accordion object, Prototype style /* * Example 7-9. An accordion object, Prototype style. */ /* Global scoped variable to hold the current object opened in the accordion */ var currentId = null; Effect.Accordion = function(contentId) { var slideDown = 0.5; /* The speed at which the contents should slide down */ var slideUp = 0.15; /* The speed at which the contents should slide up */ /* Get the object associated with the passed contentId */ contentId = $(contentId); /* Is the currentId object different from the passed contentId object? */ if (currentId != contentId) { /* Is the currentId object null? */ if (currentId == null) /* Nothing else is open, so open the passed object */ new Effect.SlideDown(contentId, {duration: slideDown}); else { /* Close the current object that is open and open the passed object */ new Effect.SlideUp(currentId, {duration: slideUp}); new Effect.SlideDown(contentId, {duration: slideDown}); } currentId = contentId; /* Set the passed object as the current object */ } else { /* Close the current object, as it was clicked */ new Effect.SlideUp(currentId, {duration: slideUp}); currentId = null; /* Nothing is open now */ } }; The speeds at which the content sections open and close vary according to the variables set. The smaller the number, the faster the effect occurs. This is how a traditional accordion navigation system works. Figure 7-13 shows how this accordion navigation would look. Figure 7-13 shows the second section closing as the fourth section is opened and displayed. If you want more than one section to be open at any given time, you need to make some minor changes to the Effect.Accordion( ) method. Remove all references to the currentId, because it does not need to be checked, and modify the 238 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Figure 7-13. An example of accordion navigation checks so that content sections are closed only when they are clicked twice. Here is the modified code: Effect.Accordion = function(contentId) { var slideDown = 0.5; /* The speed at which the contents should slide down */ var slideUp = 0.15; /* The speed at which the contents should slide up */ /* Get the object associated with the passed contentId */ contentId = $(contentId); /* Is the passed object already visible? */ if (contentId.visible(contentId)) new Effect.SlideUp(contentId, {duration: slideUp}); else new Effect.SlideDown(contentId, {duration: slideDown}); }; Setting up an accordion for use with Ajax is much tougher using this method because the Effect.SlideUp( ) and Effect.SlideDown( ) methods expect that the object passed to them will be of a fixed height. If the client has no prior knowledge of the contents in a section, it is hard to make the height fixed in size. There are at least two possible solutions to this problem. One is to get the size dynamically once it has been loaded. This method requires that the accordion effect not move until the content has been fully loaded. Unfortunately, this means that the first time the content is requested, the accordion will hesitate. Another possible solution is to set all the content areas to a fixed height in the CSS rules, and force overflow to scroll within that size. Then the accordion can start its effect while the content is being loaded, and there will be no hesitation. This method is probably the preferred method for smoothness and simplicity. Navigation Aids | www.it-ebooks.info 239 Ajax and Page Loading Because we were talking about application hesitations that may occur when Ajax is implemented, now is probably a good time to talk about Ajax loading. When the browser is loading a page, the client indicates to the user what it is doing. Usually some sort of icon or image is animated at the top-right corner of the client, an example of which appears in Figure 7-14. This tells the user that the client is working on something, and is not idle or frozen. The client also has some kind of loading indicator on its status bar (provided that the status bar can be viewed) letting the user know how much more of the page still needs to be loaded before it is complete, as shown in Figure 7-15. Figure 7-14. Netscape’s animated browser icon, which lets the user know when the client is working Figure 7-15. Firefox’s status bar showing how much of the page still needs to load The problem with Ajax is that the browser does not indicate to the user how much of the request still needs to load, or whether it is even working. That is good in one sense, because asynchronous jobs allow the user to do other things in the application, which they most likely will not do if the browser tells them it is working. But users are impatient in general, and will click the refresh (reload) button or the back button if nothing lets them know the browser responded to their actions. All in all, something needs to indicate to the user that there is an Ajax action in the client. The easiest indicator is an icon or image that appears while the request is being processed and disappears when processing is complete. The best icons and images are hourglasses and basic clocks that let the user know something is processing. This icon should be off to the side somewhere so that the user knows she can perform other actions while processing occurs. Even something as simple as displaying the words “Browser working...” can have the desired effect. In fact, a combination of the two works best, as the user’s eye is drawn to the icon and the words indicate what is happening. So, how do we accomplish this? Look at this modified version of the turnPage( ) function from the “Paged Navigation” section, earlier in this chapter: /** * This function, turnPage, changes the contents on the page to the desired * "page" number that is passed to it. * * @param {Integer} p_number The number of the page to go to. * @return Returns false so that no other event is fired after this one. 240 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info * @type Boolean * @see Element#setStyle * @see Ajax#Request */ function turnPage(p_number) { var pages = $('pagedNavList').getElementsByTagName('li'); /* Loop through the list of
      • elements */ for (var i = 0, il = pages.length; i < il; i++) { $(pages[i]).setStyle({ display: 'none' }); $('l' + (i + 1)).innerHTML = '' + (i + 1) + ''; } /* Has this page already been fetched once? */ if ($('page' + p_number).innerHTML == '') { new Ajax.Request('article.php', { method: 'post', parameters: { page: p_number }, onCreate: function( ) { Element.show('loadingIcon'); Element.show('loadingText'); }, onComplete: function( ) { Element.hide('loadingIcon'); Element.hide('loadingText'); }, onSuccess: function(xhrResponse) { var response = xhrResponse.responseXML; var newNode; /* Is this browser not IE ? */ if (!window.ActiveXObject) { newNode = document.importNode(response.getElementsByTagName( 'page')[0].childNodes[1], true); $('page' + p_number).appendChild(newNode); } else { newNode = importNode(response.getElementsByTagName( 'page')[0].childNodes[0], true); $('page' + p_number).appendChild(newNode); } $('l' + p_number).innerHTML = number; $('page' + p_number).setStyle({ display: 'block' }); }, onFailure: function(xhrResponse) { $('page').innerHTML = xhrResponse.statusText; } }); } else { $('l' + p_number).innerHTML = number; $('page' + p_number).setStyle({ display: 'block' }); } return (false); } Navigation Aids | www.it-ebooks.info 241 The Element.show( ) method displays the element that is passed to it, while the Element.hide( ) method hides the element that is passed to it. Both methods are part of the Prototype library. Simply by creating
        elements to encapsulate the icon image and loading text, you can use the Element.show( ) and Element.hide( ) methods to show the
        elements right when the XMLHttpRequest object is created, and hide the
        elements after the request has completed. You use CSS rules to place the elements where you want within the application. The other indicator I mentioned is a status bar. Status bars are slightly more difficult to implement because it is harder for the client to know how much of a request has been loaded to calculate a percentage. One way around this is to use the readyState from the XMLHttpRequest object and update a status bar at each state change. The Ajax.Request( ) method can take as parameters all of the different readyStates just as it does onSuccess or onFailure. There would be four readyState changes during the request, and the call would have something like this added to it: var callStatus = new Status('statusBar'); onLoading: function( ) { callStatus.increment( ); }, onLoaded: function( ) { callStatus.increment( ); }, onInteractive: function( ) { callStatus.increment( ); }, onComplete: function( ) { callStatus.increment( ); callStatus = null; } Then you need to create a new Status object: var Status = Class.create( ); /** * This object, Status, is a rough skeleton for a status bar on a page. */ Status.prototype = { /** * This member, _element, holds the id of this status bar. * @private */ _element: null, /** * This member, _percent, holds the current percent the status bar shows. * @private */ _percent: 0, /** * This method, initialize, is the constructor for the class. Any members * that need to be initialized should be here. * * @member Status * @constructor * @param {String} p_elementId The element id that represents the status bar. * @see Element#show */ initialize: function(p_elementId) { this._element = p_elementId; Element.show(this._element); }, 242 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info /** * This member, increment, increments the status bar by a set increment and * changes the display of the status bar. * * @member Status * @see Element#setStyle */ increment: function( ) { this._percent += 25; $(this._element).setStyle( { width: this._percent + '%' }); } }; This is just a rough example, and it could be much more complex and creative, but it should give you an idea of how to create a simple status bar. A more complex (and more accurate) way to create a status bar requires a lot more processing on the client, and you should use this method only if a lot of data is coming back. With this method, you would have to set the Ajax.Request( ) to a variable such as xhr and then poll xhr.transport.responseText at a timed interval. The server would have to give you a full content length, and then you would have to calculate the size of what had been set at every interval and use that to calculate the percentage. As I said, it is not generally worth the trouble. Also, you must use responseText, as any XML sent would not be well formed until it was completely loaded. The key is to make sure that any indicator displayed to the user is unobtrusive—not in the way of other navigation or functionality that the application provides. This will help with user impatience, and it will increase the user’s overall satisfaction with your application. Problems with Ajax Navigation As we have seen, using Ajax to enhance your application’s navigation has the advantages of speeding up page loads, potentially stopping page reloads, and giving the application a sleeker feel (more like Web 2.0). However, using Ajax can present some rather big issues as well. These have to do with how the client uses its functionality while interacting with the pages—namely using bookmarks and the browser’s back button. Correcting problems caused by Ajax solutions is important for web accessibility, and not only so that all browser functionality remains unbroken. Go back through the code in this chapter, and you’ll notice how all links that have JavaScript events tied to them also have a hard link that can be followed when JavaScript does not work. I never really mentioned this fact, but it keeps the application accessibility-compliant, though it requires more work for the developer to code more pages. Another point I’m sure you noticed is that almost all our navigation techniques used XHTML lists—again, this enables browsers that cannot use CSS and JavaScript to still use the page. Problems with Ajax Navigation | www.it-ebooks.info 243 Bookmarks The first problem to highlight is how a typical Ajax session completely thwarts the use of the browser’s bookmarks. This happens because the browser uses the link in its address bar for the value it saves as a bookmark to the page. This link is the unique URL to the page that the browser should bookmark. The problem is that when Ajax enters into the mix, the URL in the address bar never changes as the page state changes. This “breaks” the functionality of bookmarks in the browser. If you do a Google search on this problem, you will see many different solutions. Take a look at the Unique URLs design pattern in Ajax Design Patterns by Michael Mahemoff (O’Reilly). This design pattern describes how to make a unique URL for every state of the page that the client interprets for requests to the server. It uses the idea of fragment identifiers creating a unique URL for the page. I think this is a very good solution. A more straightforward solution to using bookmarks still relies on fragment identifiers to create a unique URL for the page. (That part is a must, it seems.) I also want to suggest an alternative that could still comply with XHTML 1.1 or even XHTML 1.0 Strict. The Unique URLs design pattern, along with the articles and spin-offs based on Mike Stenhouse’s article, “Fixing the Back Button and Enabling Bookmarking for Ajax Apps” (http://www.contentwithstyle.co.uk/Articles/38/fixing-the-back-buttonand-enabling-bookmarking-for-ajax-apps), unfortunately use iframe elements, which are not part of the two standards just mentioned. So, here is something different. You still need to gather the page state from the URL on the page load event. The page must then parse the hash part of the URL and determine what state needs to be set. (The assumption here is that every bit of main content comes from an Ajax request, as well as other content changes on the page.) Consider the following example: function bodyOnload( ) { initializeStateFromURL( ); } function initializeStateFromURL( ) { var pageState = window.location.hash; var queryString = parseStateToQueryString(pageState); configureApp(queryString); } function parseStateToQueryString(state) { /* * Parse the state according to how the fragment identifier is set up and * return a formatted querystring to send to a server page via an * XMLHttpRequest using POST. */ 244 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info ... return queryString; } function configureApp(queryString) { new Ajax.Request('somURL', { parameters: queryString, onSuccess: function(xhrResponse) { ... window.location.hash = xhrResponse.responseXML. getElementsByTagName('hash')[0].childNodes[1].nodeValue; ... } I know this is still a bit roughed out, mainly because the complexity of the parseStateToQueryString( ) function could be extremely taxing depending on the application; therefore, one can only speculate what needs to happen in all subsequent functions. I can easily envision the parseStateToQueryString( ) function returning an array of individual query strings that need many XMLHttpRequest calls to the server to set up the client the necessary way for a given page state. Whatever the solution you choose to implement, remember that the bookmark functionality in the client requires a unique URL to reference for any given page. The Browser’s Back Button The browser’s back button needs the same basic thing we just discussed with the browser’s bookmarks: a unique URL. This time, it is a URL stored in the browser’s history. The solution remains the same as far as navigating the application using fragment identifiers. For all XMLHttpRequest calls, you need to remember to push the old URL into history and change the window.location.hash to the new hash value coming from the server. The problem is that a developer will have to rely on a timeout function that checks whether the hash changes, because setting the window.location.hash affects the history for the browser but changes nothing in terms of events on the page for the back and forward buttons. It is not a big deal, except that it will hurt performance. Whenever we notice a change with the fragment identifier, we should call our initializePageStateFromURL( ) function to set the application. The biggest problem with these “hacks” is the processing hit the client will take in figuring out what should be displayed to the user. There is no truly clean way around these issues. As a developer, I can only hope browser makers will eventually recognize that the browser needs to be accountable for XMLHttpRequest requests as well as the requests that they currently keep track of. Anyone want to volunteer to code this into the browsers? Anyone? Problems with Ajax Navigation | www.it-ebooks.info 245 General Layout The main idea to take away from this chapter concerns an application’s general layout. It is best to provide navigation for the site’s main areas in several places throughout a single application page. For this reason, keeping the layout consistent is important so that the user does not become confused or impatient when he does not find what he expected. Break longer pieces of content into smaller chunks; these smaller chunks of data are easier to browse on the Web. Try to be as conscious of accessibility issues as possible, as most issues are easy to correct without any effort. Finally (and this is a very important point), if something in your application breaks the client’s normal functionality and there is no good workaround, let the user know. In larger projects, site layout starts with the designer. The developer’s job is to remain as faithful to that initial design as possible without causing a major muck-up of the client. I hope this chapter will spur new ideas that can be put in another edition of the book! Seriously, though, you can develop pretty much any type of navigational aid with CSS and JavaScript at your disposal. This fact will not change as we move forward, but rather will continue to be reinforced. This chapter discussed topics pertaining to navigation aids. In providing these aids, the developer satisfies the following WAI-WCAG 1.0 guideline: • Priority 3 checkpoint 13.5: Provide navigation bars to highlight and give access to the navigation mechanism. Remember to design your layout for the modern browser. It is just as important to keep accessibility and client functionality stashed somewhere in the back of your mind while you do. With this plan of attack, your code should degrade cleanly in browsers that cannot handle the CSS, the JavaScript, or both. When this happens, layout is not all that important anyway, but is responsible for only the basic navigation. 246 | Chapter 7: Laying Out Site Navigation www.it-ebooks.info Chapter 8 CHAPTER 8 Fun with Tables and Lists 8 When (X)HTML tables were first introduced, they evoked a variety of emotions in different developers: fear, confusion, satisfaction, excitement, and even loathing. They were confusing, yes, but they gave developers layout control they never had before. As time went on, tables began to handle the bulk of the work when it came to providing structures to display data on the client to the user. (X)HTML lists, although not inducing the love-hate relationship that tables sometimes did, also provided the developer with a means of structuring data. Until the idea of dynamic content came around, these tables and lists were workhorses for this static display. But then came dynamic tables. Using CSS, rows and items could be highlighted when clicked on or moused over, and on-the-fly sorting became popular. The table and list became integral parts of the web site or application, with fancier and more sophisticated looks thanks to the CSS rules that can be applied to them. Now we have Ajax, and many developers can see ways to utilize these structures to create functionality in web applications that, until now, were limited to desktop and Windows applications. Lists provide ways to display hierarchical details and data. Tables can not only be sorted, but also added to, updated, and deleted without refreshing the browser. Tables and lists are not the mysterious entities they once were; now they are useful everyday tools put to work in web applications. With Ajax being applied to these elements, tables and lists can not only be exciting, useful objects, but they can also now be fun. Layout Without Tables Tables in an application enable the developer to display tabular data to the user in an organized fashion. This is what tables should be used for, but this has not always been the case. Even after CSS rules made it easier for the developer to lay out a site, tables were still used prevalently in web design for the purposes of page layout. 247 www.it-ebooks.info This not only breaks the practice of tables for data/CSS for layout, but it also hurts accessibility. Besides the issue of accessibility on a site that uses tables for layout, consider the following problems associated with using tables: • Tables do not always function the way they should in all browsers, meaning pages might look different than expected. • Table layouts require many more text characters to produce a table, increasing page sizes and download times. All of the major browsers had a number of issues at one time or another when it came to rendering a table. Columns did not align correctly, gaps were placed between rows, and the thickness of rows and columns would fluctuate. This put the developer in the same position she would be in if she had chosen to use CSS instead. No matter which way the page was laid out, the developer had to test the layout in all browsers for compatibility issues. You need many more characters to lay out a site with a table than with CSS rules. Not only does the text size increase, but the complexity of the Document Object Model (DOM) document increases as well. This leads to slower rendering on slower machines, and slower processing of the DOM document by any JavaScript that may need to process it. Old Layouts In the old days of the Web, design tables were used in page layout because there was no alternative. Tables could align text and images in the desired ways, but more important, tables could produce layouts that had two-, three-, and even four-column designs. To make web design even more complicated, tables were nested within tables that were sometimes three or four levels deep. For a simple example, examine the following:
        Section One
        248 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info
        Section Two.One Section Two.Two
        Section Two.Three
        Section Three

        Main page content.

        Tables gave the designers columned layouts, complicated picture links (as an alternative to an image map), simple form alignments, and other uses as well. Take the simple layout of a login page that provides inputs for a username and password, along with a Submit button, such as the one shown in Figure 8-1. Figure 8-1. A simple username/password login page example You could easily lay out this page using the following table design:
        Email:
        Password:
        Layout Without Tables | www.it-ebooks.info 249 These examples are simple in nature, yet they illustrate the complexity and bloat associated with using tables for page layout. Using CSS It is the wise developer who uses CSS for all of the presentation and layout of an Ajax web application instead of relying on tables. Besides the reasons I gave in the preceding section, CSS allows the developer to separate the presentation layer from the structure or data layer. I cannot emphasize this enough. The first layout example using tables is one of many problems you can solve with some CSS and a little forethought. For example:

        Main page content.

        Section One

        Section Two.OneSection Two.Two

        Section Two.Three

        Section Three

        This structure needs just a few CSS rules to make the layout like that of the table: body { margin: 0; padding: 0; } #mainContent { margin-left: 230px; width: 530px; } #leftColumn { left: 0; overflow: hidden; position: absolute; top: 0; width: 220px; } #sectionTwo span { padding-right: 2em; white-space: nowrap; } 250 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info The structure is easier to read when using CSS, and it is more accessible to screen readers and text-only browsers because the main content comes first in these browsers. This makes browsing a page faster and less frustrating. Separating presentation from structure properly satisfies the following Web Accessibility Initiative-Web Content Accessibility Guidelines (WAI-WCAG) 1.0 guidelines: • Priority 2 checkpoint 3.3: Use stylesheets to control layout and presentation. • Priority 2 checkpoint 5.3: Do not use tables for layout unless the table makes sense when linearized. The CSS rules lay out a design that works for all browsers with a screen size of 800 × 600 or better. Absolute positioning aligns the left column where it needs to go (before the main content) even though in the structure itself it comes last. In this example, the overflow from the column is set to hidden. This becomes an issue of preference as to how you want your site to render. The other big use for tables in layout design is in aligning form controls, as the second example in the preceding section showed. The following example shows how you can accomplish this same layout without tables:
        Email:
        Password:
        This structure then uses the following CSS rules: #login { width: 280px; } #login div span { float: left; text-align: right; width: 100px; } div.center { text-align: center; } Layout Without Tables | www.it-ebooks.info 251 This accomplishes the same layout and adds flexibility to page implementation. Here, an absolute width is set for the form inputs, as are the elements that are used to hold the input labels. The labels are aligned to the right, but the width of the span will be ignored for elements because they are displayed inline. By making them float to the left, we force them to be displayed as block, and the width is then recognized. CSS was designed for layout and presentation, and you should use it whenever possible. The WAI-WCAG guidelines specifically state that you should avoid tables for layout whenever possible, and that making Ajax applications that are still somewhat accessible is a high priority. The two examples shown here may be simple, but I hope they illustrate how easy it is to use CSS for all of your layout needs. Accessible Tables So far, I have only talked about what you should not use tables for, not really what you should use them for. Before exploring the tricks a developer can use to manipulate tables dynamically with Ajax, I want to take a brief look at the proper way to build a table in XHTML to make it accessible. Most of the time, a user can look at a table with data and determine the table’s purpose without much difficulty. However, people who are blind and use a page reader, for instance, do not have this luxury. In these and other cases, giving the user a caption for the table (much like every table title in this book) allows the user to quickly identify the table’s purpose without having to look at the table. You use the element to give a table a caption, like this: ...
        Current Ajax Books from O'Reilly Media
        Ajax and Web Services Mark Pruett August 2006
        Ajax Design Patterns Michael Mahemoff June 2006
        Your Life in Web Apps Giles Turnbull June 2006
        Giving a table a caption aids normal browser users, but to go further down the path to accessible tables, the developer should also provide a summary for the table. 252 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info The summary attribute in the element is used for this purpose, as this example shows:
        ...
        Current Ajax Books from O'Reilly Media
        Ajax and Web Services Mark Pruett August 2006
        Ajax Design Patterns Michael Mahemoff June 2006
        Your Life in Web Apps Giles Turnbull June 2006
        Adding a summary to the table properly satisfies the following WAIWCAG 1.0 guideline: • Priority 3 checkpoint 5.5: Provide summaries for tables. A table with tabular data should have a header for every column of data. This header should be defined as such, and not as another element with a style attached to it to make it stand out. Use of the element is recommended for all tables that are not to be used for layout. For example: Accessible Tables | www.it-ebooks.info 253 ...
        Current Ajax Books from O'Reilly Media
        Book Title Author Release Date
        Ajax and Web Services Mark Pruett August 2006
        Ajax Design Patterns Michael Mahemoff June 2006
        Your Life in Web Apps Giles Turnbull June 2006
        Remember that if you use a table for layout, do not use the element to add something in bold and to center it. This would break the WAI-WCAG 1.0 Guideline Priority 2 checkpoint 5.4: if a table is used for layout, do not use any structural markup for the purpose of visual formatting. Notice that the element is given a unique id attribute, and that attribute value is used to associate headers with data by means of the headers attribute on a element. Now, defining a table header is great, as it aids screen readers in parsing through a table. But with the header defined, this is how the screen reader would output the earlier example: "This table provides a list of current Ajax books from O'Reilly Media, broken down by title, author, and release date" "Current Ajax Books from O'Reilly Media" "Book Title: Ajax and Web Services 2006" "Book Title: Ajax Design Patterns 2006" ... "Book Title: Your Life in Web Apps 2006" Author: Mark Pruett Release Date: August Author: Michael Mahemoff Release Date: June Author: Giles Turnbull Release Date: June Repeating the headers—especially if they are long and there is a lot of data and plenty of rows—can get wearisome. To remedy these situations, you can abbreviate the headers so that the screen reader will not have to output as much. You use the abbr attribute to do this. For example: Book Title Author Release Date Now when the screen reader outputs the table, it will look like this: "title: Ajax and Web Services "title: Ajax Design Patterns ... "title: Your Life in Web Apps 254 | Author: Mark Pruett Author: Michael Mahemoff date: August 2006" date: June 2006" Author: Giles Turnbull date: June 2006" Chapter 8: Fun with Tables and Lists www.it-ebooks.info This little change can make a big difference to someone with an assistive technology that accesses the table. For big tables, cutting down on the number of words a screen reader or something similar must output can be a great benefit for the person using it. Adding the element and header attributes to identify the header columns and row elements, and creating abbreviations for the header, satisfies the following WAI-WCAG 1.0 guidelines: • Priority 1 checkpoint 5.1: For data tables, identify row and column headers. • Priority 3 checkpoint 5.6: Provide abbreviations for header labels. To clarify the structure and relationship of the rows contained in a table, you can group the rows according to their use. The elements , , and provide this functionality. To use elements, you must precede them with a element. (The element is optional, but if you add it, it also must go before the elements.) As you may have noticed from the pluralizations just used, there can be only one and element, but there may be multiple elements. Example 8-1 shows a WAI-WCAG 1.0 table that is fully compliant. Example 8-1. A table showing off all WAI-WCAG 1.0 checkpoints that are compliant Accessible Tables | www.it-ebooks.info 255 Example 8-1. A table showing off all WAI-WCAG 1.0 checkpoints that are compliant (continued)
        Current Ajax Books from O'Reilly Media
        Book Title Author Release Date
        Ajax and Web Services Mark Pruett August 2006
        Dynamic Apache with Ajax and JSON Tracy Brown September 2006
        Your Life in Web Apps Giles Turnbull June 2006
        Ajax Design Patterns Michael Mahemoff June 2006
        Ajax Hacks Bruce W. Perry March 2006
        Head Rush Ajax Brett McLaughlin March 2006
        Learning JavaScript Shelley Powers October 2006
        Programming Atlas Christian Wenz September 2006
        Figure 8-2 shows this table. A nice feature that browser makers could add to all of the existing clients on the Internet is to allow the table bodies ( elements) to scroll independent of the table’s header and possible footer. This currently does not happen, and the developer has to go through many hoops to implement this sort of functionality, but this is a hack, at best. What most modern browsers have already implemented in regard to table display is the ability to print both the header and the footer on every page. Adding the , , and elements to a table to separate the differences in content satisfies the following WAI-WCAG 1.0 guideline: • Priority 1 checkpoint 5.2: For data tables that have two or more logical levels of row or column headers, use markup to associate data cells and header cells. We have only one thing left to talk about with regard to WAI-WCAG compliance, and that is what to do about tables that may not read properly with a screen reader 256 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Figure 8-2. The accessible table from Example 8-1 (with some style rules applied) because they are not linearized properly. For example, if a table were output to the client like this: Come back to Saint Louis University with your family and friends for a whole weekend of True Blue Biliken Spirit at Homecoming 2006, Sept. 29 - Oct. 1. There will be a variety of events for everyone, including a golf cart parade, campus tours and live entertainment. a screen reader may interpret it like this: Come back to Saint Louis University with There will be a variety of events for your family and friends for a whole everyone, including a golf cart parade, weekend of True Blue Biliken Spirit at campus tours and live entertainment. Homecoming 2006, Sept. 29 - Oct. 1. Though it is not very difficult to linearize a table, it does require a little bit of forethought. Usually you will need to translate the table data into such a scheme. One way to do this is to use the dir attribute to specify the column layout order for the browser. As browsers advance, this will become less of an issue, but until then it should at least be in the back of your mind to attempt a serialized version of any table that could be misinterpreted. Attempting to have a serialized version of the table satisfies the following WAI-WCAG 1.0 guideline: • Priority 3 checkpoint 10.3: Until user agents (including assistive technologies) render side-by-side text correctly, provide a linear text alternative (on the current page or some other) for all tables that lay out text in parallel, word-wrapped columns. Accessible Tables | www.it-ebooks.info 257 The point of this introduction to accessible tables is to remind you how easy it is to make a data table accessible to people who do not use one of the common browsers. Now that you should have a thorough understanding of the structure of an XHTML table, we need to tackle how to interact with it dynamically. Interacting with Tables When I say “interacting with tables,” I really mean dynamically creating, deleting, and updating tables using JavaScript. Danny Goodman wrote a terrific article on this subject, which you can find in O’Reilly’s Web DevCenter. Located at http://www. oreillynet.com/pub/a/javascript/2003/05/06/dannygoodman.html, “Dynamic HTML Tables: Improving Performance” examines different methods of creating dynamic content. In his article, Danny discusses the following ways to dynamically make tables, using DOM methods in some and proprietary methods in others: Method one Use the methods insertRow( ) and insertCell( ) with the innerHTML property. Method two Use the methods insertRow( ) and insertCell( ) with DOM text nodes. Method three Use the createElement( ) method with a DOM DocumentFragment element and the innerHTML property. Method four Use the createElement( ) method with a DOM DocumentFragment element and DOM text nodes. Method five Assemble all of the content that will be contained within the element as a string, and then assign this to the innerHTML property of the element. Method six Assemble the entire table as a string, and then assign this to the innerHTML of an outer
        element. The first method uses the methods insertRow( ) and insertCell( ) to give the developer access to the DOM document when items are added. The result of calling these methods is a reference to a newly created DOM element. All that’s left is to put data into the table, and this first method uses the innerHTML property for this task. For example: /* This is the element that will have data added to it */ var tbodyElement = $('tbodyOne'); /* These are the new elements that will be created */ var newTrElement = null, newTdElement = null; 258 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info /* * This technique loops through the rows of data in the array, creating a new * row for the table for each row of data, and then looping through the columns * of data in the array, creating a new column that corresponds to each column * of data. The column of data is then inserted into the table with the * /innerHTML/ property. */ for (var i = 0, il = dataArray.length; i < il; i++) { newTrElement = tbodyElement.insertRow(tbodyElement.rows.length); newTrElement.setAttribute('id', 'row_' + tbodyElement.rows.length); for (var j = 0, jl = dataArray[i].length; j < jl;) { newTdElement = newTrElement.incertCell(newTrElement.cells.length); newTdElement.setAttribute('id', 'r_' + tbodyElement.rows.length + 'col_' + newTrElement.cells.length); newTdElement.innerHTML = dataArray[i][j++]; } } The second method also uses the insertRow( ) and insertCell( ) methods to create the rows and columns. The difference is in how the data is populated into the table. This method sticks with DOM methods for the task, using createTextNode( ) and appendChild( ), as this example shows: /* This is the element that will have data added to it */ var tbodyElement = $('tbodyOne'); /* These are the new elements that will be created */ var newTrElement = null, newTdElement = null, newTxtNode = null; /* * This technique loops through the rows of data in the array, creating a * new row for the table for each row of data, just as the last example had. * The looping through the columns of data in the array, creating a new * column that corresponds to each column of data, is also the same as the * last example. The column of data is first created as a textNode and * then appended to the existing table. */ for (var i = 0, il = dataArray.length; i < il; i++) { newTrElement = tbodyElement.insertRow(tbodyElement.rows.length); newTrElement.setAttribute('id', 'row_' + tbodyElement.rows.length); for (var j = 0, jl = dataArray[i].length; j < jl;) { newTdElement = newTrElement.incertCell(newTrElement.cells.length); newTdElement.setAttribute('id', 'r_' + tbodyElement.rows.length + 'col_' + newTrElement.cells.length); newTxtNode = document.createTextNode(dataArray[i][j++]); newTdElement.appendChild(newTxtNode); } } When comparing innerHTML to the DOM methods, you will find that different browsers perform better or worse with the different methods. Internet Explorer does better with the innerHTML property, which is not a surprise since Microsoft invented it. Mozillabased browsers, however, perform better using DOM methods with this technique. Accessible Tables | www.it-ebooks.info 259 The third and fourth methods look to improve upon the speed and efficiency of the first two methods. They do so by building up data outside the DOM tree using a documentFragment and the createElement( ) method. There are definite speed advantages to not bothering the existing DOM document tree unless only one or two changes are made to it. In these cases, the documentFragment comes in handy. This example uses innerHTML to apply the data to the DOM document tree after everything is created: /* This is the table that will have the data added to it */ var tableElement = $('theTable'); /* These are the new elements that will be created */ var newTrElement = null, newTdElement = null; /* This is the element that will be constructed in memory */ var newTbodyElement = document.createElement('tbody'); /* This is the documentFragment that will be used to construct the new element */ var fragment = document.createDocumentFragment( ); /* * This technique loops through the rows of data in the array, creating a * new row for the table for each row of data, and then looping through the * columns of data in the array, creating a new column that corresponds * to each column of data. The column of data is then inserted into the * table with the innerHTML method and appended to the new row. When the * inner loop completes, the new row is appended to the fragment. Once * the outer loop completes, the fragment is appended to the new tbody * element, which is in turn appended to the existing table. */ for (var i = 0, il = dataArray.length; i < il;) { newTrElement = document.createElement('tr'); newTrElement.setAttribute('id', 'row_' + i); for (var j = 0, jl = dataArray[i].length; j < jl;) { newTdElement = document.createElement('td'); newTdElement.setAttribute('id', 'r_' + i + 'col_' + j); newTdElement.innerHTML = dataArray[i++][j++]; newTrElement.appendChild(newTdElement); } fragment.appendChild(newTrElement); } newTbodyElement.appendChild(fragment); tableElement.appendChild(newTbodyElement); This example, on the other hand, uses the DOM methods createTextNode( ) and appendChild( ): /* This is the table that will have the data added to it */ var tableElement = $('theTable'); /* These are the new elements that will be created */ var newTrElement = null, newTdElement = null, newTxtNode = null; /* This is the element that will be constructed in memory */ var newTbodyElement = document.createElement('tbody'); /* This is the documentFragment that will be used to construct the new element */ var fragment = document.createDocumentFragment( ); 260 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info /* * This technique loops through the rows of data in the array, creating a * new row for the table for each row of data, and then looping through the * columns of data in the array, creating a new column that corresponds to * each column of data. The column of data is first created as a /textNode/ * and then appended to the existing table data element. When the inner * loop completes, the new row is appended to the fragment. Once the outer * loop completes, the fragment is appended to the new element, which * is in turn appended to the existing table. */ for (var i = 0, il = dataArray.length; i < il;) { newTrElement = document.createElement('tr'); newTrElement.setAttribute('id', 'row_' + i); for (var j = 0, jl = dataArray[i].length; j < jl;) { newTdElement = document.createElement('td'); newTdElement.setAttribute('id', 'r_' + i + 'col_' + j); newTxtNode = document.createTextNode(dataArray[i++][j++]); newTdElement.appendChild(newTxtNode); newTrElement.appendChild(newTdElement); } fragment.appendChild(newTrElement); } newTbodyElement.appendChild(fragment); tableElement.appendChild(newTbodyElement); Again, there are differences in how fast the various browsers process each example. One thing that is common among all browsers is that methods three and four are significantly faster than methods one and two. In all of the examples so far, I have been adding data to a element within a table. Method five demonstrates perhaps the easiest solution: to simply replace the innerHTML of the . But note that although this is an easy solution, the element does not support the innerHTML element in Internet Explorer. If you are not concerned about cross-browser support, the following example is for you: /* * This is the variable that will hold our string of data that we * dynamically build. */ var data = ''; /* * Loop through the /dataArray/ much like the other methods, but build the * table using a string and then move the string into the /innerHTML/ of * the element. */ for (var i = 0, il = dataArray.length; i < il;) { data += ''; for (var j = 0, jl = dataArray[i].length; j < jl;) data += '' + dataArray[i++][j++] + ''; data += ''; } $('tbodyOne').innerHTML = data; Accessible Tables | www.it-ebooks.info 261 Method five is much faster than the other methods introduced so far. However, no DOM-compliant method is comparable. It is also unfortunate that Internet Explorer does not support it. The sixth method gets around the innerHTML/ issue with Internet Explorer. This method is the best cross-browser approach, as long as you are not concerned about World Wide Web Consortium (W3C) DOM compliance. This method simply creates the entire table in a string, and then adds these string contents to the innerHTML of a
        element acting as a table wrapper, as shown in this example: /* * This is the variable that will hold our string of data that we dynamically * build, only this time start by building the table element. */ var data = ''; /* * Loop through the /dataArray/ much like the other methods, but build the table * using a string and then move the string into the /innerHTML/ of the table * wrapper element. */ for (var i = 0, il = dataArray.length; i < il;) { data += ''; for (var j = 0, jl = dataArray[i].length; j < jl;) data += ''; data += ''; } data += '
        ' + dataArray[i++][j++] + '
        '; $('tableWrapper').innerHTML = data; This method, as it turns out, is the fastest way to build a table dynamically. Performance is important when it comes to Ajax applications, so we will come back to this method for our examples. Updating content in a row shares a common problem with deleting a row of data. You must first locate the row in question. Then, updating is simple enough, as this example shows: /* * We will assume with this code that we searched through the rows of the table * to find a particular row to update the data in. The code will then return * the row element as the variable /oldTrElement/, and the number for that row * as the variable /rowNumber/. */ ... /* These are the new elements that will be created */ var newTrElement = null, newTdElement = null; /* Create the new row that will contain the updated data */ newTrElement = document.createElement('tr'); newTrElement.setAttribute('id', 'row_' + rowNumber); for (var i = 0, il = updateData.length; i < il;) { 262 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info newTdElement = document.createElement('td'); newTdElement.setAttribute('id', 'r_' + rowNumber + 'col_' + i); newTdElement.innerHTML = updateData[i++]; newTrElement.appendChild(newTdElement); } /* Update the record */ $('theTable').replaceChild(newTrElement, oldTrElement); The options for updating are to walk the row and change the individual cells, or replace a whole row of data. Replacing the whole row is the better solution. All that is left is to show the simplest method for deleting data. This example shows the method in action for deleting a section of the table: /* This is the element holding the data to delete */ var tbodyElement = $('tbodyOne'); var il = tbodyElement.childNodes.length; /* Loop until there are no more childNodes left */ while (il--) tbodyElement.removeChild(tbodyElement.firstChild); As you can see, the simplest solution is to remove the firstChild from the section repeatedly until there are no firstChild nodes left. It would not make sense to try a different approach for this, as any tree traversal techniques are much slower than this method. All of this discussion on dynamic table manipulation is important. It is hard to apply Ajax techniques to a table if you are unfamiliar with good ways to access tables and manipulate them. Different problems will require different solutions. Hopefully, the methods discussed here provide enough different solutions to get you on the right path, if nothing else. Ajax and Tables So the question is, how should Ajax and tables be combined? The answer, of course, depends on what you are doing with the table. If the Ajax request is to update one row, getting a fully formatted table is not the answer. But if a whole chunk of a table needs to be replaced, the formatted chunk of table does not sound so bad. You also need to take into account whether the Ajax application needs to be browser-compliant. An application that must run on Internet Explorer cannot have data loaded into a element’s innerHTML property, so you must take a different approach in this case. Another thing to consider is how the data will be transported to the client. For loading tables with data, JavaScript Object Notation (JSON) can be an excellent solution for the updated data in a single row, whereas an XHTML document would be better for a whole table. Thus far, we have examined how to pull Ajax data and how to manipulate a table. Now we will discuss the practical application of these techniques. Accessible Tables | www.it-ebooks.info 263 Sorting Tables As applications become more commonplace on the Web, their response time and speed will have to improve, or they will be doomed to failure. A common object to find in an application is some sort of table filled with data that may or may not need to be manipulated. One thing that is expected is that the table should be self-sorting; that is, clicking on a column heading will sort the data in either ascending or descending order based on that column’s data. You can accomplish this kind of functionality in two ways. The traditional method was to send back for the server on every column click, let the server do the sorting, and refresh the whole page with the newly sorted table. As developers became more sophisticated, they turned their sights toward letting the client do all the work. This method lets JavaScript do all the heavy lifting, thereby keeping the browser from flickering on the new page load and, in many cases, speeding up the action. JavaScript Sorting JavaScript sorting is aimed at keeping the load on the client and not on the server. Why would we want to do this? As more people hit a site or application, server speed and response suffer. To keep this to a minimum, developers try to keep most of the work on the client instead of burdening the server with more requests. This same line of thinking applies to any databases used to serve up the data in the tables. The first part of sorting anything—whether it is an array, collection, table, or something similar—is to be able to compare two values and check for three different states: greater than, less than, and equal to. Before we can make these comparisons, however, we need a way to compare text within a table data element. Comparing text is essentially the same as comparing numbers, in a roundabout sort of way. Complicating matters are child nodes contained within the element. For example: Ideally we would hope to get all of this text in our search To avoid any errors or complications that could occur, first we need a way to normalize any data contained in the elements we need to compare. Example 8-2 shows a simple normalize function. Example 8-2. A function to normalize data in an element and its childNodes /** * This function, normalizeElement, takes the passed /p_element/ and strips out * all element tags, etc. from the node and any /childNodes/ the element may * contain, returning a string with only the text data that was held in the * element. * 264 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-2. A function to normalize data in an element and its childNodes (continued) * @param {Node} p_element The element that is to be normalized. * @return Returns the normalized string without element tags or extra whitespace. * @type String */ function normalizeElement(p_element) { /* The variable that will hold the normalized string. */ var normalized = ''; /* Loop through the passed element's /childNodes/ to normalize them as well */ for (var i = 0, il = p_element.childNodes.length; i < il; i++) { /* The child element to check */ var el = p_element.childNodes[i]; /* Is this node a text node or a cdata section node? */ if (el.nodeType == document.TEXT_NODE || el.nodeType == document.CDATA_SECTION_NODE || el.nodeType == document.COMMENT_NODE) normalized += el.NodeValue; /* Is this node an element node and a
        element? */ else if (el.nodeType = document.ELEMENT_NODE && el.tagName == 'BR') normalized += ' '; /* This is something to normalize */ else normalized += normalizeElement(el); } return (stripSpaces(normalized)); } /* * These regular expressions are used to strip unnecessary white space from * the string. */ var endWhiteSpace = new RegExp("^\\s*|\\s*$", "g"); var multWhiteSpace = new RegExp("\\s\\s+", "g"); /** * This function, stripSpaces, takes the passed /p_string/ variable and using * regular expressions, strips all unnecessary white space from the string * before returning it. * * @param {String} p_string The string to be stripped of white space. * @return Returns the passed string stripped of unnecessary white space. * @type String */ function stripSpaces(p_string) { p_string = p_string.replace(multWhiteSpace, ' '); p_string = p_string.replace(endWhiteSpace, ''); return (p_string); } Sorting Tables | www.it-ebooks.info 265 The normalize code uses DOM standard constants that correspond to an element’s nodeType property. These constants, however, are not defined in some browsers (such as Internet Explorer), so the developer must define them. For example: /* This code is necessary for browsers that do not define DOM constants */ if (document.ELEMENT_NODE == null) { document.ELEMENT_NODE = 1; document.ATTRIBUTE_NODE = 2; document.TEXT_NODE = 3; document.CDATA_SECTION_NODE = 4; document.ENTITY_REFERENCE_NODE = 5; document.ENTITY_NODE = 6; document.PROCESSING_INSTRUCTION_NODE = 7; document.COMMENT_NODE = 8; document.DOCUMENT_NODE = 9; document.DOCUMENT_TYPE_NODE = 10; document.DOCUMENT_FRAGMENT_NODE = 11; document.NOTATION_NODE = 12; } Now, we need to decide what kind of sorting algorithm we should use on the table. I’ll just skip most of the computer science involved in determining a good algorithm, and instead will make two simple comments. One, the quick sort is the fastest search algorithm that is relatively straightforward to implement. Two, for the Web, the insertion sort is the best choice of algorithms. Why should we not use a quick sort for our sort algorithm if it is the fastest? The easiest answer for me to give is that generally, the data displayed in an Ajax application is not going to have hundreds or thousands of records that will need to be sorted. The quick sort is an excellent algorithm to implement on a desktop application, or wherever you have a large number of records to display. Using the quick sort for a table with 30 records, however, would be like attempting to crush a bug by backing over it with a car. The insertion sort works using a basic algorithm, but it works quickly on smaller data sets and does not require any recursion. The algorithm would generally use two lists (a source list and a final list); however, to save memory, an in-place sort is used most of the time. This works by moving the current item being sorted past the items already sorted and repeatedly swapping it with the preceding item until it is in place. Figure 8-3 shows the algorithm in action for a small list of numbers. Example 8-3 shows how you should implement this in code. 266 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Sorted Initial Unsorted 9 5 3 Sorted 1st Iteration 5 7 Unsorted 9 3 Sorted 2nd Iteration 3 7 7 5 3 3 Unsorted 9 7 Sorted 3rd Iteration 3 3 Unsorted 5 9 3 5 9 Sorted 4th Iteration 3 7 3 Figure 8-3. The insertion sort in action on a small list of numbers Example 8-3. A simple implementation of an insertion sort /** * This function, insertionSort, takes an array of data (/p_dataArray/) and sorts * it using the /Insertion/ sort algorithm. It then returns the sorted array. * * @param {Array} p_dataArray The array of values to sort. * @return Returns the sorted array of values. * @type Array */ function insertionSort(dataArray) { var j, index; /* Loop through the array to sort each value */ for (var i = 0, il = dataArray.length; i < il; i++) { index = dataArray[i]; j = i; /* Move the /dataArray/ index to the place of insertion */ while ((j > 0) && (dataArray[j - 1] > index)) { dataArray[j] = dataArray[j - 1]; j -= 1; } /* Move the current /dataArray/ index to the insertion location */ dataArray[j] = index; } return (dataArray); } Sorting Tables | www.it-ebooks.info 267 Sorting Algorithms The most common sorting algorithms can be separated into two groups based on their complexity. These two groups are represented by the Big O notations O(n2) and O(n log n). The computer science of Big O notation is interesting, but well beyond the scope of this book. You can find more information on the subject at http://en.wikipedia.org/wiki/ Big_O_notation. The O(n2) group of algorithms is best used for smaller data sets, whereas the O(n log n) algorithms perform best with large data sets. The common O(n2) algorithms are bubble, selection, insertion, and shell. The bubble sort is the oldest, but also the slowest and most inefficient. At the other end of the spectrum is the shell sort, the fastest of the O(n2) sorting algorithms. Also known as a comb sort, it makes multiple passes through the table and sorts a number of equally sized sets of data with each pass using the insertion sort. The sets get larger with each pass until they comprise the entire table, which is sorted. Here is a code example in JavaScript of the shell sort: function shellSort(dataArray) { var j, increment = 3, temp; while (increment > 0) { for (var i = 0, il = dataArray.length; i < il; i++) { j = i; temp = dataArray[i]; while ((j >= increment) && (dataArray[j - increment] > temp)) { dataArray[j] = dataArray[j - increment]; j -= increment; } dataArray[j] = temp; } if ((increment / 2) != 0) increment /= 2; else if (increment == 1) increment = 0; else increment = 1; } return (dataArray); } This algorithm is straightforward, but if you need to sort something more than a simple array of numbers, it gets a little trickier to implement. The shell sort is fast, and the insertion sort is not far behind, but none of the O(n2) algorithms even comes close to the speed of the O(n log n) algorithms. The most common algorithms are heap, merge, and quick. The heap sort is the slowest, but it has one good point to make up for the speed: it does not require multiple arrays and massive recursion to work. The fastest of all of these algorithms, both O(n2) and O(n log n), is the quick sort. The quick sort is basically a divide-and-conquer, massively recursive sorting algorithm. —continued— 268 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Here is a version of the quick sort in JavaScript: function quickSort(dataArray) { qSort(dataArray, 0, dataArray.length - 1); return (dataArray); } function qSort(dataArray, left, right) { var pivot = dataArray[left], lHold = left, rHold = right; while (left < right) { while ((dataArray[right] >= pivot) && (left < right)) right--; if (left != right) { dataArray[left] = dataArray[right]; left++; } while ((dataArray[left] <= pivot) && (left < right)) left++; if (left != right) { dataArray[right] = dataArray[left]; right--; } dataArray[left] = pivot; pivot = left; left = lHold; right = rHold; if (left < pivot) qSort(dataArray, left, pivot - 1); if (right > pivot) qSort(dataArray, pivot + 1, right); } return (dataArray); } This algorithm has four steps: 1. If there is only one or no data elements left in the array to be sorted, return immediately. 2. Pick a data element in the array to serve as a pivot point. 3. Divide the array into two sections—one section with data elements larger than the pivot point and the other section with data elements smaller than the pivot point. 4. Recursively repeat the steps of the algorithm for both sections of the original array. The trouble with O(n log n) algorithms is that they work well for large data sets, but become much slower with small data sets. Because of this, small data sets should be attacked with an O(n2) algorithm while large data sets should be attacked by an O(n log n) algorithm. What algorithm to choose depends on the data being sorted and what the developer is comfortable implementing. Sorting Tables | www.it-ebooks.info 269 Keep in mind that this is a basic insertion sort algorithm that sorts only an array of values. Our code for sorting a table will be slightly more complicated due to the nature of moving table elements around dynamically with the DOM. This is also why an insertion sort is easier to implement than a shell sort, but complexity aside, the shell sort would be optimal for smaller data sets. We now have the basics for putting together a table sort. Before we do this, though, we need to discuss how we should build a table when it will contain the functionality for dynamic sorting. The parts of the table that we want sorted are the elements contained in the table. Only one block should be sorted at a time, though; the line of thinking here is that elements separate blocks of table data that are similar in nature, and it does not make sense to sort data that is not related to other data. We also never want to sort the and blocks of the table, as they should be constant in nature. Now we need to decide what functionality our sort should provide. The most common approach is that the sort will activate on a user’s click of a element in the table. The first click will sort the corresponding rows in ascending order, and a second click will sort the rows in descending order. All subsequent clicks will reverse the sort for that column of data. Example 8-4 takes the pieces we discussed and implements a basic table sort using the insertion sort. Example 8-4. Sorting tables using the insertion sort /* This code is necessary for browsers that do not define DOM constants */ if (document.ELEMENT_NODE == null) { document.ELEMENT_NODE = 1; document.ATTRIBUTE_NODE = 2; document.TEXT_NODE = 3; document.CDATA_SECTION_NODE = 4; document.ENTITY_REFERENCE_NODE = 5; document.ENTITY_NODE = 6; document.PROCESSING_INSTRUCTION_NODE = 7; document.COMMENT_NODE = 8; document.DOCUMENT_NODE = 9; document.DOCUMENT_TYPE_NODE = 10; document.DOCUMENT_FRAGMENT_NODE = 11; document.NOTATION_NODE = 12; } /** * This class, tableSort, is created as a class and not just an object so that the * page may have more than one table with the ability to sort, without having to * go to a lot of trouble in code to keep them separate within the logic itself. * * @requires Class#create * @see Class#create */ var tableSort = Class.create( ); tableSort.prototype = { 270 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-4. Sorting tables using the insertion sort (continued) /** * /multipleWS/ will hold the regular expression to eliminate multiple whitespace * in a string. * @private */ multipleWS: '', /** * /endWS/ will hold the regular expresssion to eliminate whitespace at the end * of the string. * @private */ endWS: '', /** * /tblElement/ will hold the table's element that is to be sorted. * @private */ tblElement: null, /** * /lastColumn/ will hold the last column sorted by the user. * @private */ lastColumn: null, /** * /reverseSort/ will contain an array of columns of the table and will keep * track of the sort direction the table should take. * @private */ reverseSort: new Array( ), /** * This method, initialize, is the constructor for the /tableSort/ class. It * seeds values into the private members of the class based on the passed * element /p_id/ variable and constant regular expressions. * * @param {String} p_id The id attribute of the table to sort. * @constructor */ initialize: function(p_id) { this.tblElement = $(p_id).getElementsByTagName('tbody')[0]; this.endWS = new RegExp("^\\s*|\\s*$", "g"); this.multipleWS = new RegExp("\\s\\s+", "g"); }, /* Normalize the data contained in the element into a single string */ /** * This method, normalizeElement, takes the passed /p_element/ and strips out * all element tags, etc. from the node and any /childNodes/ the element may * contain, returning a string with only the text data that was held in the * element. * * @param {Node} p_element The element that is to be normalized. * @return Returns the normalized string without element tags or extra * whitespace. * @type String Sorting Tables | www.it-ebooks.info 271 Example 8-4. Sorting tables using the insertion sort (continued) * @private * @see #sortColumn */ normalizeElement: function(p_element) { /* The variable to hold the normalized string */ var normalize = ''; /* * Loop through the passed element's /childNodes/ to normalize them * as well. */ for (var i = 0, il = p_element.childNodes.length; i < il;) { /* The child element to check */ var el = p_element.childNodes[i++]; /* Is this node a text node, CDATA node, or comment node? */ if (el.nodeType == document.TEXT_NODE || el.nodeType == document.CDATA_SECTION_NODE || el.nodeType == document.COMMENT_NODE) normalize += el.nodeValue; /* Is this node an element node and a
        element? */ else if (el.nodeType = document.ELEMENT_NODE && el.tagName == 'BR') normalize += ' '; /* This is something to normalize */ else normalize += this.normalizeElement(el); } return (this.stripSpaces(normalize)); }, /* Strip any unnecessary whitespace from the string */ /** * This method, stripSpaces, takes the passed /p_string/ variable and using * regular expressions, strips all unnecessary whitespace from the string * before returning it. * * @param {String} p_string The string to be stripped of whitespace. * @return Returns the passed string stripped of unnecessary whitespace. * @type String * @private * @see #normalizeElement */ stripSpaces: function(p_string) { p_string = p_string.replace(this.multipleWS, ' '); p_string = p_string.replace(this.endWS, ''); return (p_string); }, /** * This method, compareNodes, takes two node values as parameters (/p1/ and * /p2/) and compares them to each other. It sends back a value based on * the following: * * 1 - /p1/ is greater than /p2/ * 0 - /p1/ is equal to /p2/ 272 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-4. Sorting tables using the insertion sort (continued) * -1 - /p1/ is less than /p2/ * * @param {String} p1 The first node value in the test. * @param {String} p2 The second node value in the test. * @return Returns a value based on the rules in the description of this method. * @type Integer * @private * @see #sortColumn */ compareNodes: function(p1, p2) { /* Convert the values, if possible, to Floats */ var f1 = parseFloat(p1), f2 = parseFloat(p2); if (!isNaN(f1) && !isNaN(f2)) { /* Are both values numbers? (they are faster to sort) */ p1 = f1; p2 = f2; } /* Are the two values the same? */ if (p1 == p2) return 0; /* Is the first value larger than the second value? */ if (p1 > p2) return 1; return -1; }, /** * This method, sortColumn, sorts the passed /p_column/ in a direction based on * the passed /p_defaultDirection/, moving data in all sibling columns when it * sorts. * * @param {Integer} p_column The column index that is to be sorted. * @param {Integer} p_defaultDirection The direction the sort should take (1 for * !default). * @see #normalizeElement * @see #compareNodes */ sortColumn: function(p_column, p_defaultDirection) { var tempDisplay = this.tblElement.style.display; var j, index; /* Set a default direction if one is not passed in */ if (defaultDirection == null) defaultDirection = 0; /* * Has the passed column been sorted yet? - if not, set its initial sorting * direction. */ if (this.reverseSort[p_column] == null) this.reverseSort[p_column] = p_defaultDirection; /* * Was the lastColumn sorted the passed column? - if it is, reverse the sort * direction. Sorting Tables | www.it-ebooks.info 273 Example 8-4. Sorting tables using the insertion sort (continued) */ if (this.lastColumn == p_column) this.reverseSort[p_column] = !this.reverseSort[p_column]; this.lastColumn = p_column; /* * Hide the table during the sort to avoid flickering in the table and * misrendering issues found in Netscape 6 */ this.tblElement.style.display = 'none'; /* Loop through each row of the table and compare it to other row values */ for (var i = 0, il = this.tblElement.rows.length; i < il; i++) { index = this.normalizeElement(this.tblElement.rows[i].cells[p_column]); j = i; /* * Sort through all of the previous records and insert any of these rows * in front of the current row if such action is warranted. */ while (j > 0) { var doSort = this.compareNodes(this.normalizeElement( this.tblElement.rows[j - 1].cells[p_column]), index); /* * Doing the opposite sort direction for alternating clicks is as * simple as negating the current value for the sort order, * turning Ascending to Descending and back again. */ if (this.reverseSort[p_column]) doSort = -doSort; if (doSort > 0) this.tblElement.insertBefore(this.tblElement.rows[j], this.tblElement.rows[j - 1]); j -= 1; } } /* Set the table's display to what it was before the sort */ this.tblElement.style.display = tempDisplay; /* Do not let the onclick event know that it worked */ return (false); } }; The tableSort object requires that it be instantiated after the browser has created the table, such as: var myTableSort = new tableSort('myTable'); Each table header element must also contain an onclick event that calls the tableSort object’s sortColumn( ) method and passes it the index of the column. For example, to sort the first column the code would look like this: 274 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info It is as simple as that. The client takes over the burden of sorting the table. The sort is fast, and best of all, the client does not have to refresh all of its content on every user request for a new sort. The drawback to implementing the insertion sort is that speed will degrade as the data set gets larger, which is something the developer should always keep in mind. Sorting with Ajax Sorting a table using an Ajax technique is similar to the old way of sorting tables, which, of course, was to refresh the whole table each time there was a sort request. The difference between the old technique and Ajax is that the whole page does not have to be refreshed with every request; only the table does. Why would a developer want to do this instead of using the JavaScript approach we just discussed? One word: performance. The client’s performance cannot match the server’s performance, especially when the client is using JavaScript while the server can use much more powerful scripting. Using what we already discussed, we can send the table to the client with whatever sort order is requested. The client should send to the server the id of the table, the name of the column that should be sorted, and the direction of that sort (ascending or descending). The server can, in most cases, spit out a table (especially a larger table) much quicker than the client could have one sorted. Assume that we are to sort the following table:
        Column One
        Sorting Tables | www.it-ebooks.info 275
        2005-06 Premier League
        Team Points Won Drew Lost GS GA
        Chelsea 91 29 4 5 72 22
        ...
        headers="team">Manchester United headers="points">83 headers="won">25 headers="drew">8 headers="lost">5 headers="gs">72 headers="ga">34 headers="team">Sunderland headers="points">15 headers="won">3 headers="drew">6 headers="lost">29 headers="gs">26 headers="ga">69 Notice that the table has an id attribute that uniquely identifies it, and that the header elements have identified the column names. All we need to add to the header elements now is an onclick event that will call our Ajax request. Something like this should do: onclick="sortTable('premLeague', this.id);" Now we do a little housekeeping before the Ajax request can be sent: $('premLeague').lastColumn = ''; Like the JavaScript sort, the lastColumn property is added to the table when the page loads, and then is checked and set with every onclick event. Once the parameters are set, the Ajax call can be performed, and our functions for the completion of the request should be built. Example 8-5 shows how the Ajax would be called. Example 8-5. The sortTable( ) method modified for Ajax /** * This function, sortTable, takes the passed /p_tableId/ and /p_columnId/ * variables and sends this information to the server so it can do the * appropriate sort that is returned to the client. The data from the server * is the whole table, because it is faster to build the whole table on the * server. * * @param {String} p_tableId The id of the table to sort. * @param {String} p_columnId The id of the column that is to be sorted. */ function sortTable(p_tableId, p_columnId) { /* Get the direction the sort should go in */ var sortDirection = (($(p_tableId).lastColumn == p_columnId) ? 1 : 0); /* Create the queryString to send to the server */ var queryString = 'tableId=' + p_tableId + '&columnId=' + p_columnId + 276 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-5. The sortTable( ) method modified for Ajax (continued) '&sort=' + sortDirection; /* Record the column that is sorted */ $(p_tableId).lastColumn = p_columnId; /* * Make the XMLHttpRequest to the server, and place the /responseText/ into * the /innerHTML/ of the table wrapper (/parentNode/). */ new Ajax.Request('sortTable.php', { parameters: queryString, method: 'post', onSuccess: function(xhrResponse) { $(tableId).parentNode.innerHTML = xhrResponse.responseText; }, onFailure: function(xhrResponse) { $(tableId).parentNode.innerHTML = xhrResponse.statusText; } } } The onFailure property is taken care of inline during the Ajax request, as is the onSuccess property. The onSuccess property needs the tableId that is passed to sortTable( ), and the rest is straightforward—it takes the responseText from the Ajax response and sets it to the innerHTML of the
        element that is the parent of the table itself. All other building (the onclick events and the column names) is taken care of on the server side when the table is rebuilt. The server would have to accept the parameters and then build a SQL request based on what was passed. The table would be rebuilt as a string and then sent back to the client as a regular text response. The server code would look something like Example 8-6. Example 8-6. The PHP code that could be used to create a table for a response to a client $host, 'username' => $username, 'password' => $password, 'dbname' => $db); try { /* Create a connection to the database */ $conn = Zend_Db::factory('PDO_MYSQL', $params); /* Build the SQL string based on the parameters that were passed */ $sql = sprintf('SELECT team, points, won, drew, lost, gs, ga FROM ' .'premLeague WHERE year = 2005%s', (($_REQUEST['columnId'] != '') ? ' ORDER BY ' .$_REQUEST['columnId'].(($_REQUEST['sort'] == 1) ? ' DESC' : ' ASC') : '')); /* Get the results from the database query */ $result = $conn->query($sql); /* Are there results with which to build a table? */ if ($rows = $result->fetchAll( )) { $id = $_REQUEST['tableId']; /* Build the beginning and header of the table */ $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; /* * Loop through the rows of the result set and build the table data * in the tbody element. */ 278 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-6. The PHP code that could be used to create a table for a response to a client (continued) foreach($rows in $row) { $xml .=''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .= ''; $xml .=''; } /* Finish up the table to be sent back */ $xml .= ''; $xml .= '
        1995-96 Premier League
        ' .'Team' .'Points' .'Won' .'Drew' .'LostGSGA
        '.$row['team'].''.$row['points'].''.$row['won'].''.$row['drew'].''.$row['lost'].''.$row['gs'].''.$row['ga'].'
        '; } } catch (Exception $e) { $xml .= '
        There was an error retrieving the table data.
        '; } } /* Send the XHTML table to the client */ print($xml); ?> And the Winner Is… There is no way to definitively say which method is better or faster. Sometimes it will be better to use the Ajax method—maybe the server is old and could not handle all of the Ajax requests, or maybe the data sets are so small that it does not make sense to do an Ajax request. If the data sets are larger, the developer is faced with two choices. He can either rewrite the sort code and use a better algorithm, or use Ajax and let the server (which is much better equipped to handle large data sets) do all of the sorting and table building. JavaScript sorting relies on heavy manipulation of the DOM document that holds the table. This can be expensive, and it could lock up the client for several seconds in the process. The alternative does have its benefits. Using Ajax to do the table sort means the call can be asynchronous to the server, and the user is free to do other things on the client until the sort is complete. Clearly, it is in the developer’s best interests to at least indicate to the user that the client is doing something. Besides that, however, there is really nothing else adverse about using Ajax over straight JavaScript sorting. In the next several sections, it will become clearer why Ajax is the better choice for table sorting. Even after you read these sections, some of you will cling to your JavaScript sorting code, saying, “Look how beautiful this code is, and how slick it works on the client.” There is a coolness factor in doing the sort using JavaScript. Ajax is not about coolness in this case; it is about practicality, efficiency, and the speed of the application. Sorting Tables | www.it-ebooks.info 279 Tables with Style Left to their own devices, tables are boring objects. We have all seen the old tables that were prevalent on the Web years ago, an example of which appears in Figure 8-4. Using CSS to change the shape, size, and color of the table borders helped to change the table’s general appearance. Going further with CSS, alternating colors for alternating rows and making the header and footer more distinct helped to make tables more readable on the client. Figure 8-4. The default table from the browser is boring It became trickier to keep all of this style straight as the user dynamically altered the table in the client. Keeping alternating rows straight in an Ajax sorted table is simple. The whole table is regenerated so that any CSS that was placed on the rows before the sort is put on the new row order as though nothing has changed. Keeping the style of the table with a JavaScript sort is another story. 280 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Keeping Style with Sorts CSS has brought us a long way from tables such as the one in Example 8-3; consider the following CSS rules and imagine how they would style that table: table { border-collapse: collapse; border: 3px solid #000; width: 500px; } caption { font-weight: bold; } tr { background-color: #fff; color: #000; margin: 0; } tr.alternate { background-color: #dde; color: #000; } th { background-color: #007; border: 1px solid #000; border-bottom: 3px solid #000; color: #fff; padding: 2px; } td { border: 1px solid #000; padding: 2px; } This CSS creates a 3-pixel-thick black border around the outside of the table and under the table header. All of the other rows and columns are divided by a 1-pixel-thick black border. The table header has its background set to a deep blue and the text to white. Finally, the default row is set to a white background with black text, and a row with the alternative class attribute set would have a gray-blue background with black text, as shown in Figure 8-5. Alternating the color for every other column keeps the rows easier to read. But there is a problem, right? Tables with Style | www.it-ebooks.info 281 Figure 8-5. The Premier League table with CSS rules attached When we sort by any of the columns, the rows do not keep their alternating pattern. When the row is inserted in a new spot in the table body, everything about that row goes with it. So, how do we solve this problem? We need another method in our sortTable object to style the table, like this: /** * This method, styleTable, loops through all of the table rows in the table and * removes the /alternate/ class from the row before checking to see whether the * row is one of the alternating rows that should have the class. * * @private */ styleTable: function( ) { /* Loop through all of the table rows */ for (var i = 0, il = this.tblElement.rows.length; i < il; i++) { /* This is the current row */ var tblRow = this.tblElement.rows[i]; Element.removeClassName(tblRow, 'alternate'); /* Is this an alternate row? */ if ((i % 2) != 0) Element.addClassName(tblRow, 'alternate'); } } 282 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info The styleTable( ) method relies on two methods from the Prototype library Element object: removeClassName( ) and addClassName( ). I could have written code to add and remove classNames from the rows, but since Prototype already had these, I did not see the point. The styleTable( ) method needs to be called just before the display on the table is changed, like this: /* Add the needed style to the table */ this.styleTable( ); /* Set the table's display to what it was before the sort */ this.tblElement.style.display = tempDisplay; Now, our table keeps the CSS rules that were originally applied. The developer could get pretty fancy with the styleTable( ) method if she so desired. It would not take much to change the column that was selected so that it stood out more than the others. This would make it more accessible to a person trying to read the table. As far as Ajax goes, the classes would just have to be added to the table as it was being built. The addition of only a couple of lines does the trick, like this: $i = 0; foreach ($rows in $row) { $xml = sprintf('%s', $xml, ((($i++ % 2) != 0) ? 'class="alternate"': '')); This is yet another reason Ajax sorting may be a better choice—it requires even less processing on the client side. This, in particular, is nothing for the server to do, because it already is creating a bunch of strings to concatenate together. Table Pagination Table pagination follows the same principle we saw in Chapter 7 in the “Paged Navigation” section. A table that is longer than one page on a user’s screen is large by web standards, and you should probably break it up. The technique is the same as before: display only a certain number of rows to the user and provide a navigation list at the bottom of the table. Once again, the question is, should the table be broken up on the client or on the server? In either case, we will utilize the XHTML elements to make our job a little easier. The server will do more of the work, as it must keep track of how many records are in what grouping. The groupings are what’s important, and to make them, we will be using multiple elements—one for every page, actually. For example:
        Table Pagination | www.it-ebooks.info 283 ... ...
        2005-06 Premier League
        Team Points WonDrew Lost GS GA
        Chelsea 91 29 4 5 72 22
        Manchester United 83 25 8 5 72 34
        Everton 50 14 8 16 34 49
        The id attribute identifies the page that the represents. JavaScript or Ajax must do the rest. 284 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Making Pages with JavaScript The same principle we applied to the paged navigation will apply here. The server will send the entire table to the client, and when the page has loaded, the first “page” of the table will be displayed through code. All of the elements will start off hidden using CSS. For example: table#premLeague tbody { display: none; } The JavaScript to make the first page appear is minor: Note that I am setting the display to the value table-row-group and not the common block value. This is because if I set the to block, it will no longer align to any or element that is present. The part we need to add is the list of pages that will serve as navigation for the table. The server will have also taken care of this list’s initial state. The client’s job is to change its appearance, as well as the that should show when it is clicked. Assuming that our table contains five pages of data, the navigation list would look like this:
        • 1
        • 2
        • 3
        • 4
        • 5
        The onclick event fires off to the turnDataPage( ) function, shown in Example 8-7. Example 8-7. A function to simulate pages of table data /** * This function, turnDataPage, acts on whatever table (/p_tableId/) is passed, * which is the beginning of making this "table independent". It turns off * the display of all of the elements associated with the table, and * then displays the required one based on the passed /p_pageNumber/ variable. * Finally, it changes the page number list item that is to be highlighted in * association with the table page. * Table Pagination | www.it-ebooks.info 285 Example 8-7. A function to simulate pages of table data (continued) * @param {String} p_tableId The id of the table to paginate. * @param {Integer} p_pageNumber The number of the page that should be displayed. * @see Element#setStyle * @see Element#removeClassName * @see Element#addClassName */ function turnDataPage(p_tableId, p_pageNumber) { /* Get a list of the elements in the table */ var tbodies = $(p_tableId).getElementsByTagName('tbody'); /* Loop through the list of elements, and hide each one */ for (var i = 0, il = tbodies.length; i < il; i++) tbodies[i].style.display = 'none'; /* Display the element with the correct page number */ $('p' + p_pageNumber).style.display = 'table-row-group'; /* Get a list of the
      • elements in the page navigation menu */ var tableListElements = $('l' + p_pageNumber).parentNode.getElementsByTagName('li'); /* Loop through the list of
      • elements, and make each one inactive */ for (var i = 0, il = tableListElements.length; i < il; i++) Element.removeClassName(tableListElements[i], 'active'); /* Activate the
      • element with the correct page number */ Element.addClassName($('l' + p_pageNumber), 'active'); } With this function, we should change the onload event to the following so that the first page number is highlighted: Of course, the turnDataPage( ) function expects another CSS rule to be defined as well: li.active { font-weight: bold; } I will discuss the advantages and disadvantages of a straight JavaScript approach to table pagination after we discuss the Ajax method. Internet Explorer version 6 and earlier do not support the CSS display value table-row-group that is used in Example 8-7. For these browsers, you should use the value of block instead. The only problem with pagination such as this is that it is not accessible to browsers that do not support JavaScript. To make it accessible, we simply need to add links to our list of pages in the navigation list, like this:
          286 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info
        • 1
        • 2
        • 3
        • 4
        • 5
        Figure 8-6 shows this table with the paged navigation. Now when a client does not have JavaScript, the link will fire off and the data can still be accessed, provided that the server-side scripting is programmed to handle this scenario. Figure 8-6. The Premier League table with paged navigation Ajax Table Pagination Ajax table pagination also follows the same principles as Ajax paged navigation: namely, give the client only as much data as it needs to create one page of the table. As the pages are requested, the table builds up more elements until an Ajax request is no longer needed and the straight JavaScript method can take over. Example 8-8 shows the turnDataPage( ) function revised for Ajax. Table Pagination | www.it-ebooks.info 287 Example 8-8. The turnDataPage( ) function modified for Ajax /** * This function, turnDataPage, acts on whatever table (/p_tableId/) is passed, * which is the beginning of making this "table independent". It turns off the * display of all of the elements associated with the table, and then * makes an /XMLHttpRequest/ to pull the necessary if it does not already * have /childNodes/ under it. The success of the call adds the new content and * then displays the required one. Finally, it changes the page number list item * that is to be highlighted in association with the table page. * * @param {String} p_tableId The id of the table to paginate. * @param {Integer} p_pageNumber The number of the page that should be displayed. * @see Element#setStyle * @see Element#removeClassName * @see Element#addClassName */ function turnDataPage(p_tableId, p_pageNumber) { /* Get a list of the elements in the table */ var tbodies = $(p_tableId).getElementsByTagName('tbody'); /* Loop through the list of elements, and hide each one */ for (var i = 0, il = tbodies.length; i < il; i++) $(tbodies[i]).setStyle({ display: 'none' }); /* Has this page been grabbed by an XMLHttpRequest already? */ if ($('p' + p_pageNumber).childNodes.length == 0) { /* Get the data from the server */ new Ajax.Request('getTable.php', { method: 'post', parameters: 'dataPage=' + p_pageNumber, onSuccess: function(xhrResponse) { var newNode, response = xhrResponse.responseXML; /* Is this browser not IE? */ if (!window.ActiveXObject) newNode = document.importNode( response.getElementsByTagName('tbody')[0], true); else newNode = importNode( response.getElementsByTagName('tbody')[0], true); $(tableId).replaceChild(newNode, $('p' + p_pageNumber)); }, onFailure: function(xhrResponse) { alert('Error: ' + xhrResponse.statusText); } }); } /* Display the element with the correct page number */ $('p' + p_pageNumber).setStyle({ display: 'table-row-group' }); /* Get a list of the
      • elements in the page navigation menu */ var tableListElements = $('l' + p_pageNumber).parentNode.getElementsByTagName('li'); /* Loop through the list of
      • elements, and make each one inactive */ 288 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-8. The turnDataPage( ) function modified for Ajax (continued) for (var i = 0, il = tableListElements.length; i < il; i++) Element.removeClassName(tableListElements[i], 'active'); /* Activate the
      • element with the correct page number */ Element.addClassName($('l' + p_pageNumber), 'active'); } This is similar to what we already saw with our other Ajax calls using Prototype. Here, the main difference is how we check whether content is already in the . Because this should be a cross-browser function, we cannot check the innerHTML property like we did in Chapter 7; Internet Explorer does not support the property. Instead, we check to see whether the has any childNodes. Instead of even fiddling with elements and the innerHTML property, the client will expect the server to send the contents of the (all of the rows and corresponding columns, including the itself) and then import those elements into a new element. Once again, for Internet Explorer, we must rely on both the DOM importNode( ) method and the importNode( ) function I introduced in Example 7-8 in Chapter 7 for browsers that do not support importNode( ) natively. After the import, we just replace the empty that is attached to the table with our new element. Of course, we could have avoided having to check for childNodes and import and replace elements. If the server were to send a new table with each request—a table that contains only the data for the page that is requested—we could use the innerHTML property with the responseText from the server’s response on a wrapper
        element. In the long run, the user may request a lot of data if a lot of browsing is done on the table. The developer must weigh whether simple (and potentially faster) code is better than fewer calls to the server requesting data. There is one argument for constantly calling on the server to give the user data. Well, two arguments, really. I will get to the second one in the next section. The first argument is that if the user is working with an Ajax application that gets data from different users potentially at the same time, getting constant data refreshes from the server on every request is a better solution. This way, the user knows that the data being viewed is constantly up-to-date versus data that is loaded once and might become stale. Sorting Paginated Tables Now for the second argument: if you want the table to be broken up into pages as well as dynamically sortable, you may have a dilemma. Either you must modify the methods for sorting to cycle through every element in the table, which requires digging deeper into the DOM tree, or you can simply have the server handle the sort and send the table data page that was requested. Letting the server handle things is certainly cleaner and easier, if not faster. Table Pagination | www.it-ebooks.info 289 To accomplish this, we must add an extra parameter to the sortTable( ) method in Example 8-5 for the current page, and we must pass it to the server. Everything else is the same for the Ajax sort. The server will create the sorted table, sending the small part of the table that the client needs, which is then put into the table wrapper’s innerHTML property as the responseText. Example 8-9 shows the changes needed. Example 8-9. The sortTable( ) method modified for pagination sorting with Ajax /** * This function, sortTable, takes the passed /p_tableId/ and /p_columnId/ * variables and sends this information to the server so it can do the * appropriate sort that is returned to the client. The data from the server * is the whole table, because it is faster to build the whole table on the * server. * * @param {String} p_tableId The id of the table to sort. * @param {String} p_columnId The id of the column that is to be sorted. * @param {Integer} p_pageNumber The number of the page to display. */ function sortTable(p_tableId, p_columnId, p_pageNumber) { /* Get the direction the sort should go in */ var sortDirection = (($(p_tableId).lastColumn == p_columnId) ? 1 : 0); /* Create the queryString to send to the server */ var queryString = 'tableId=' + p_tableId + '&columnId=' + p_columnId + '&sort=' + sortDirection + '&page=' + p_pageNumber; /* Record the column that is sorted */ $(p_tableId).lastColumn = p_columnId; /* * Make the XMLHttpRequest to the server, and place the /responseText/ into the * /innerHTML/ of the table wrapper (/parentNode/). */ new Ajax.Request('sortTable.php', { parameters: queryString, method: 'post', onSuccess: function(xhrResponse) { $(tableId).parentNode.innerHTML = xhrResponse.responseText; }, onFailure: function(xhrResponse) { $(tableId).parentNode.innerHTML = xhrResponse.statusText; } } } I think that is the easiest solution. As I said, if you choose to go the JavaScript route, there will be a loop through all of the elements in the table in addition to the loops through the rows in a single element. To simplify things, I recommend looping through everything while building an array of values, and sorting that array with a simpler implementation of the insertion sort (or maybe a shell sort now). 290 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Once everything is sorted, you are left with the difficult task of getting the table rows in the correct order—all while maintaining the correct number of rows in each element. This will be complicated and messy. In the end, the developer wants something that works, and works fast. Using the Ajax solution for sortable paginated tables does just that. Lists 2.0 A great deal of Chapter 7 dealt with utilizing XHTML lists for different purposes. Most of these purposes really take lists to that new level—no longer relegating the list to the boring purpose of displaying information in a vertical manner using a circle or square to delineate the items. When you use CSS and XHTML smartly, lists can become a very powerful structure in an Ajax application. When using lists, the main thing to watch for is the purpose of the list. Once the list is used, in any way, for presentation instead of structure, it breaks the following WAI-WCAG 1.0 guideline: • Priority 2 checkpoint 3.6: Mark up lists and list items properly. As we saw in Chapter 7, lists are useful for a variety of widgets and objects in web applications. However, this barely scratches the surface of what they can do. What We’ve Already Seen We already saw that we can use lists for navigation functionality in an Ajax application—we did not use the lists as lists at all (for the most part). So, what have we covered? • Lists used for navigation bars • Lists used for buttons in navigation • Lists used for drop-down menus • Lists used for a file menu • Lists used for tabs • Lists used for breadcrumbs • Lists used for links at the bottom of a page • Lists used for the navigation in paged content • Lists used for navigation trees • Lists used for vertical list navigation This is already a lot of uses for a simple structure, but lists have still more uses for dynamic content, if you can believe that. Lists 2.0 | www.it-ebooks.info 291 Lists for All Seasons What we’ll discuss next ranges from fairly common to rarely seen uses of XHTML lists. I’ll put a Web 2.0 spin on the common list applications, and will hopefully spawn new development for the rarer scenarios. In any case, I will throw some Ajax into the examples where it fits, and the other examples are intended for Ajax applications where Ajax has more to do with dynamic user interaction. Table of Contents Anyone who has had to create any kind of online documentation knows the hassle of updating the document. The hassle mainly involves changes that must be made to the table of contents whenever a section is moved, deleted, or added. When a section is moved or deleted, numbering must shift up for any headers below the point in question. When a section is added, numbering must shift down for any headers below the point in question. In almost all cases, lists are already used for the structure of the table of contents, and why not? A table of contents consists of only ordered or unordered lists and sublists. Nothing needs to change with this—yet. First we must concentrate on the document itself, or rather how we need to structure the document so that we can easily and dynamically generate a table of contents. The often misused header elements (

        ) are where we need to focus. The misuse usually occurs with the headers not following in immediate descending order. In other words, the headers that should follow an

        element are

        elements. No

        elements should be direct descendants of the

        element. This paradigm should continue throughout the document. Also true of header elements is that they are used to convey the document’s structure; they are not used for presentation within the document. If you need to create a section of code with larger text, use appropriate markup and do not simply throw in header elements to accomplish the task. By following proper header order, not skipping any header levels, and using the headers for structure, the developer satisfies the following WAI-WCAG 1.0 guideline: • Priority 2 checkpoint 3.5: Use header elements to convey document structure and use them according to specification. The following shows how this chapter would be structured with XHTML markup:
        Fun with Tables and Lists

        Layout Without Tables

        292 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info

        Old Layouts

        Using CSS

        Accessible Tables

        Interacting with Tables

        Ajax and Tables

        ...

        Lists for All Seasons

        Table of Contents

        The document requires that all the headers have unique id attributes so that the table of contents can identify them properly. Once we know our document is properly structured, we must think about how to create a dynamic list that reflects that structure. Example 8-10 shows the JavaScript function that will do this job. Example 8-10. A function to dynamically create a list to be used as a table of contents /** * This function, createTOC, parses through an XHTML document, taking all header * elements within the /content/ of the document, and creating a clickable table * of contents to the individual headers that it finds. */ function createTOC( ) { /* * The table of contents is only concerned with elements contained within * this element. */ var content = $('content'); /* Get a node list of all

        elements */ Lists for All Seasons | www.it-ebooks.info 293 Example 8-10. A function to dynamically create a list to be used as a table of contents (continued) var head1 = content.getElementsByTagName('h1'); var toc = '
          '; /* Loop through the list of

          elements */ for (var i = 0, il = head1.length; i < il; i++) { /* Try to get to the
          element following the header element */ var h1 = head1[i].nextSibling; /* * Is the node text (whitespace in this case)? If so, try the next element * after this...it should be the
          element */ if (h1.nodeType == 3) h1 = h1.nextSibling; /* Get a node list of all

          elements under the
          element */ var head2 = h1.getElementsByTagName('h2'); toc += '
        • ' + head1[i].childNodes[0].nodeValue + ''; /* Are there any

          elements? */ if (head2.length > 0) { toc += '
            '; /* Loop through the list of

            elements */ for (var j = 0, jl = head2.length; j < jl; j++) { /* * Try to get to the
            element following the header * element. */ var h2 = head2[j].nextSibling; /* * Is the node text (whitespace in this case)? If so, try the * next element after this...it should be the
            * element. */ if (h2.nodeType == 3) h2 = h2.nextSibling; /* * Get a node list of all

            elements under the
            * element. */ var head3 = h2.getElementsByTagName('h3'); toc += '
          • ' + head2[j].childNodes[0].nodeValue + ''; /* Are there any

            elements? */ if (head3.length > 0) { toc += '
              '; /* Loop through the list of

              elements */ for (var k = 0, kl = head3.length; k < kl; k++) { /* 294 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-10. A function to dynamically create a list to be used as a table of contents (continued) * Try to get to the
              element following the * header element. */ var h3 = head3[k].nextSibling; /* * Is the node text (whitespace in this case)? If so, * try the next element after this...it should be the *
              element */ if (h3.nodeType == 3) h3 = h3.nextSibling; /* * Get a node list of all

              elements under the *
              element. */ var head4 = h3.getElementsByTagName('h4'); toc += '
            • ' + head3[k].childNodes[0].nodeValue + ''; /* Are there any

              elements? */ if (head4.length > 0) { toc += ''; } toc += '

            • '; } toc += '

            '; } toc += '

          • '; } toc += '

          '; } toc += '

        • '; } toc += '

        '; $('toc').innerHTML = toc; } As you can see from this example, the function dynamically creates an XHTML list as a string as it walks the DOM’s document tree. It walks it through a series of loops that go deeper and deeper into levels, stopping after going four levels deep. I stopped here mainly because O’Reilly frowns on the use of even a fourth level of header—you can feel free to go to all six header levels if you want. Lists for All Seasons | www.it-ebooks.info 295 Once the list is created, it is passed to the innerHTML property of our wrapper
        element, along with some header text for the table of contents. Put simply, you can create a dynamic table of contents that always follows whatever content is in the document, all modifications included. Say that I am creating a document and I want to use numbers to identify each section. As such, the sections would have an order of 1, 1.1, 1.2, 1.2.1, and so on. We need to change nothing—I repeat, nothing—in what we have already done to accomplish this. All we need are some CSS rules regarding how to style the table of contents and headers. Example 8-11 shows these rules. Example 8-11. The CSS rules needed to create a numbering system for the document and table of contents #content { counter-reset: h1; } #content h1:before { content: counter(h1) "\0020\2014\0020"; counter-increment: h1; } #content h1 { counter-reset: h2; font-size: 1.6em; } #content h2:before { content: counter(h1) "." counter(h2) "\0020\2014\0020"; counter-increment: h2; } #content h2 { counter-reset: h3; font-size: 1.4em; } #content h3:before { content: counter(h1) "." counter(h2) "." counter(h3) "\0020\2014\0020"; counter-increment: h3; } #content h3 { counter-reset: h4; font-size: 1.2em; } #content h4:before { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) "\0020\2014\0020"; counter-increment: h4; } 296 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-11. The CSS rules needed to create a numbering system for the document and table of contents (continued) #toc ul { counter-reset: item; font-weight: bold; } #toc li { display: block; } #toc li:before { content: counters(item, ".") "\0020\2014\0020"; counter-increment: item; } Adding a table of contents to an application or site satisfies the following WAI-WCAG 1.0 guideline: • Priority 2 checkpoint 13.3: Provide information about the general layout of a site (i.e., a site map or table of contents). The only drawback to our table of contents is that it requires JavaScript and CSS to create, which may not work and ultimately may defeat this accessibility requirement. Figure 8-7 shows how the table of contents and the headers look with the style rules from Example 8-11 applied. The CSS rules in Example 8-11 liberally use CSS2 rules to achieve the desired look. Specifically, the :before pseudoclass does all the heavy lifting. Certain browsers do not yet implement this pseudoclass—or much CSS2 in general. In these browsers, the table of contents will function normally, but none of the numbering will be displayed. Sortable Lists You can sort list items in a variety of ways. One of the easiest for the user to understand (at least visually) is to place checkboxes to the left of each item in the list. Then provide buttons that indicate movement up and down and that, when pressed, shift the checked items into a new position in the list. This is not the most elegant solution, but it gets the job done. For an Ajax web application, however, we are striving for more than just a working solution. We want to provide users with interfaces that remind them of the interfaces in desktop applications. To do this, and to provide more of a Web 2.0 feel to the functionality, dragging and dropping list items to reposition them is the way to go. Most of the JavaScript libraries we covered in Chapter 4 provide methods for sorting lists via drag-and-drop solutions. Lists for All Seasons | www.it-ebooks.info 297 Figure 8-7. The document with style rules applied, as viewed with Firefox For the examples in this section, I have chosen to use script.aculo.us for my drag-anddrop solution, mainly because it is built on top of the Prototype Framework (which we already have had some experience with). For thoroughness, I also will provide the methods the Dojo Toolkit uses, after we explore the script.aculo.us interface. It is a good idea to become familiar with several of these JavaScript libraries and toolkits because none of them have all the functionality needed for a complete Ajax solution. Like Prototype, script.aculo.us is divided into different areas based on functionality. For dragging and dropping list items on the screen, it provides the Sortable object. The Sortable object allows you to sort items in a list by dragging them to the desired position in the list and dropping them into place. To instantiate an XHTML list, do the following: Sortable.create('id_of_list', [options]); Creating a sortable list is as simple as that. Table 8-1 provides the options available to pass in the object parameter of the Sortable.create( ) method. 298 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Table 8-1. Options available to pass in the object parameter of Sortable.create( ) Option Default value Description tag li Sets the type of element for the child elements of the container that will be made sortable. For
          and
            element containers, the default works, but for any other kind of element, this must be specified. only None Restricts the child elements for sorting further so that only those elements with the specified CSS class will be used. An array of CSS classes (as strings) can also be passed so that the restriction is on any of the classes. overlap vertical Specifies in which direction the overlapping of elements occurs. The possible values are vertical and horizontal; vertical lists use vertical and horizontal lists use horizontal. constraint vertical Specifies whether a constraint is put on the dragging to vertical or horizontal directions. Possible values are vertical, horizontal, and false. containment (only within container) Enables dragging and dropping between Sortables. This takes an array of elements or element ids for the containers. handle None Sets a handle for the draggable object so that dragging only occurs using the handle. Is an element reference, an element id, or a CSS class. With the CSS class, the first child element found with the passed value will be used as the handle. hoverclass None Gives an additional CSS class to the list items when an item being dragged is hovered over the other items. ghosting false When set to true, the dragged element will be cloned such that the list does not shift until the dragged element is dropped into a new place. Possible values are true and false. dropOnEmpty false When set to true, the Sortable container will be made into a Droppable that can receive elements as soon as it no longer has any child elements. Possible values are true and false. scroll None If the Sortable is in a container and that element has overflow: scroll set, this value can be set to the container’s id. Once this is set, the position of the container will follow along with the position of the Sortable element. scrollSensitivity 20 The scrollSensitivity value tells the Sortable how sensitive it should be to the scrolling boundaries of the container. The higher the number, the more sensitive the Sortable will be. scrollSpeed 15 The speed of the scrolling of the surrounding container is controlled with this value. The higher the number, the faster the scroll as it follows the Sortable. tree false When this value is set to true, sortable functionality to elements listed in treeTag is set. Values are true and false. treeTag ul This identifies the element type in which the tree nodes are contained. The Sortable object will function correctly in your application only if you include the following line of code before the code to create the Sortable: Position.includeScrollOffsets = true; Lists for All Seasons | www.it-ebooks.info 299 Let’s pretend we are building an administrative widget for an online contact application, and the functionality required is to change the order in which contact fields are presented. Assume the following list is used for the possible fields to sort:
            • Full Name
            • Company
            • Business Phone
            • Business Fax
            • Home Phone
            • Mobile Phone
            • Job Title
            • Business Address
            • Email address
            • Icon
            • Flag Status
            Next there is the small matter of styling the list so that it looks a little more presentable than its default value. These CSS rules should do the trick: #sortList { margin: 0; padding: 0; } #sortList li { color: #0c0; cursor: move; margin: 0; margin-left: 20px; padding-left: 20px; padding: 4px; } #sortList li span { color: #070; } Finally, to make this list sortable, we would use the following JavaScript to initialize it and make it functional: With that, we have created a sortable list of items that requires simple drag-and-drop functionality to work, as shown in Figure 8-8. 300 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Figure 8-8. A sortable list in action using script.aculo.us Now we will see how Dojo would accomplish this task. The Dojo Toolkit functions by loading packages into the application page when they are needed. In fact, the method looks very similar to Java and comparable languages and the way they include libraries in a program. For example: This loads the io, event, and widget packages so that you can use them throughout the page. Like script.aculo.us, Dojo has built-in functionality for sortable lists using drag and drop. Using the same list we used for the script.aculo.us example, we will again make it sortable. First, in the head of the XHTML page, we must load the necessary Dojo modules: dojo.require('dojo.dnd.*'); dojo.require('dojo.event.*'); Once these modules have been loaded, we need a way to initiate the JavaScript on our list. We will take advantage of Dojo’s event handling for this so that we execute a function once the page has loaded. This is an initialization function with the event JavaScript underneath it: Lists for All Seasons | www.it-ebooks.info 301 /** * This function, onBodyLoad, initializes the drag-and-drop sorting capabilities of * the Dojo Toolkit by setting the list container (
              element) as the target for * dropping and the list items ( element) are set as the elements that are * draggable. */ function onBodyLoad( ) { /* Get the container element */ var dndList = document.getElementById('sortList'); /* * Create the drop target out of the container element and set a grouping that * has the right to drop within it. */ new dojo.dnd.HtmlDropTarget(dndList, ['sortListItems']); /* Get a node list of all of the container's
            • elements */ var dndItems = dndList.getElementsByTagName('li'); /* * Loop through the list of
            • elements and make each of them draggable * and set them as members of the drop target's group. */ for (var i = 0, il = dndItems.length; i < il; i++) new dojo.dnd.HtmlDragSource(dndItems[i], 'sortListItems'); } /* Set an event to call the onBodyLoad( ) function when the page is loaded */ dojo.event.connect(dojo, 'loaded', 'onBodyLoad'); There aren’t many options for setting up the sortable list, as there are for script.aculo.us. Besides the id of the list to sort, you can only pass in an array of strings that represent the groups of pages to which you want the list to drag and drop. In our initialization function, onBodyLoad( ), the list will be used only for items in the sortListItems group. This allows some control over where items can be dragged and dropped in the application. Creating drag-and-drop sort functionality is simple when you use one of the many JavaScript libraries, toolkits, or frameworks found on the Web. However, we need to integrate this functionality with Ajax to make it more useful. Ajax and the Draggable List The practical application of a sortable list is in the client sending its sort order information to the server. The server can then process the information and send a response back to the client. Returning to our contact application, we want to send the server the updated sort order so that it knows how to format, say, a table that it can then send back to the client. For now, we will concentrate on sending the information. We will return to the server part in a couple of chapters. 302 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Using script.aculo.us, integrating Ajax is not difficult. As you probably noted in Table 8-1, the Sortable object has an onUpdate callback as part of its Sortable.create( ) options. We simply need to write a function that parses the new list, creates a string list with the new order, and sends that to the server. Before we do that, we need to modify our list slightly:
              • Full Name
              • Company
              • Business Phone
              • Business Fax
              • Home Phone
              • Mobile Phone
              • Job Title
              • Business Address
              • Email address
              • Icon
              • Flag Status
              This new list has an id attribute that we will use to identify which item is in which position for the order string. Now our function can parse this attribute to obtain the order of the list. For example: Sortable.create('sortList', { ghosting: true, constraint: 'vertical', onUpdate: function(container) { /* Get the list of child nodes */ var listItems = container.getElementsByTagName('li'); /* Start the parameter string */ var params = 'order='; /* * Loop through the items and parse the id attribute, creating an array * with the
            • element portion in index 0, and the order number in * index 1. Then add that to the string. */ for (var i = 0, il = listItems.length; i < il; i++) { var temp = listItems[i].id.split('_'); if (i > 0) params += ','; params += temp[1]; } /* Make the call to the server with the new order */ new Ajax.Request('updateOrder.php', { method: 'post', parameters: params, onSuccess: function(xhrResponse) { /* you can do anything you want here...I say nothing */ }, Lists for All Seasons | www.it-ebooks.info 303 onFailure: function(xhrResponse) { /* * Maybe you would want to let the user know there was a * problem and whom to contact about it. */ } }); } }); The only difficult part is deciding whether we need to do anything when the call to the server completes. We will talk about errors in Chapter 12, and then decide what we should do in this case. The server will get a comma-delimited string with the new sort order, such as 1,4,6,3,2,5,7,10,8,9. It will parse this string and do something with it. For now, that is all we want it to do—the server will parse the string and create a sorting string out of the data. This will be stored in a session variable for later use. Here is an example: Much like we split the string for the ids on the underscore (_) character, in this case we will be making an array based on the commas in the string. This array is actually what we need to store, as it is a good construct for future coding. In Chapter 10, we will see how to let the server know it needs to generate a new contact list that is to be sent to the client with the proper sort order. The script returns a 0 or a 1 depending on what happened in the script, which the client can use for a true or false. The JavaScript libraries and toolkits are—and I know many of you will scoff at this—very useful and good for taking care of the code you shouldn’t have to think about. Instead, you can concentrate on the application’s actual functionality. I ask all of you naysayers, do desktop application developers code everything from scratch, or do they use third-party libraries when they are available? An Ajax Slide Show Building a slide show requires the client to load a lot of pictures. The pictures are hidden as they are loaded, only to be viewed when the show cycles them. If it is a large slide show, it could take a very long time to load the images—too long, in fact, 304 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info for a lot of users to wait. Ajax will allow us to load images after the client has loaded, and the asynchronous part of Ajax allows the show to start while pictures are still being loaded. The first thing to think about is how the data will be presented. Because this is a chapter on tables and lists, I think we should hold the images in an unordered list. We will want to keep track of three things for each picture: the image, a title for the image, and a description of the image. Before we begin to worry about the slide show, we need to lay out the structure of the XHTML. To get a little bit fancy, let’s lay out the page something like this: As you can see, the list (slideshowList), along with an element to hold the title (imageTitle) and an element to hold the navigation (navigationContainer), sits inside a wrapper called, appropriately enough, slideshowWrapper. That wrapper is contained in another element, the slideshowContainer, which sits at the same level as the element holding the image’s description (imageDescription). Both are wrapped in the bodyContainer. All of these layers are here so that you can lay out the page in just about any manner you want. Once we get to the image loading part, all of the images will be the child node of an
            • element that gets appended to the slideshowList. For now, this will remain an unordered list without any children. Our next order of business is to define the CSS rules to make the slide show look more presentable. Example 8-12 takes care of styling our XHTML. Example 8-12. slideshow.css: The CSS rules to lay out the slide show a { background-color: transparent; color: #fff; } body { background-color: #fff; color: #000; font-family: serif; font-size: 16px; } Lists for All Seasons | www.it-ebooks.info 305 Example 8-12. slideshow.css: The CSS rules to lay out the slide show (continued) /* Put the title in the center of the box */ #imageTitle { text-align: center; } /* * Since this is the main (outside) container, give it a big red border and * make the inside background black. Shift it right with an extra margin on * the left to make room for the image description. */ #slideshowContainer { background-color: #000; border: 3px solid #f00; color: #fff; display: table; height: 540px; margin: 0 0 0 280px; overflow: hidden; width: 500px; } /* Make the wrapper act like it is a table cell so that it will obey the * vertical alignment to the middle. */ #slideshowWrapper { display: table-cell; vertical-align: middle; } /* Get rid of the list marker and center the list */ #slideshowList { list-style-type: none; margin: 0; padding: 0; text-align: center; } #slideshowList li { display: inline; margin: 0; padding: 0; } /* Put the navigation tools in the center of the box */ #navigationContainer { text-align: center; } /* * Move the image description into the space that the outside container made * by shifting over. Let this container scroll if the contents are too large * (but that shouldn't happen). */ 306 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-12. slideshow.css: The CSS rules to lay out the slide show (continued) #imageDescription { border: 1px solid #000; height: 495px; margin: 23px 0 0 5px; overflow: auto; position: absolute; padding: 10px 0 10px 10px; top: 0; width: 250px; } Now that the application looks the way we want it to, we can concentrate on the JavaScript portion. The navigation links will need to have an onclick event associated with them that will call a function to move between the pictures in the list. Because the images are stored in a list as the firstChild element of the
            • element, the other image information that will be available must be stored elsewhere. Yes, all of the information could be stored in the list item, but then the slide show would require additional style rules and the JavaScript would have to look at a more complicated DOM tree. Instead, a multidimensional array can store the other information, where index 0 will hold the image title and index 1 will hold the description. The following code provides an easy solution for changing back and forth between images: /** * This function, changeSlide, moves the display of individual
            • elements one * element at a time while hiding all other elements to give the illusion of * moving back and forth through a slide show of
            • elements. * * @param {Integer} p_slideDirection The direction of the slide change (-1 * back and 1 forward). * @return Returns false so that the element that had the event click stops * any default events. * @type Boolean * @see Element#hide * @see Effect#Appear */ function changeSlide(p_slideDirection) { /* Is the index going to be too small or too large? */ if (!((index + p_slideDirection) < 0 || (index + p_slideDirection) > $('slideshowList').childNodes.length - 1)) { index += p_slideDirection; /* Loop through the unordered list and hide all images */ var items = $('slideshowList').getElementsByTagName('li'); for (var i = 0, il = items.length; i < il; i++) Element.hide(items[i]); /* * Now make the image that is to be changed appear, and change the * title and description. */ Lists for All Seasons | www.it-ebooks.info 307 Effect.Appear($('slideshowList').childNodes[index]); $('imageTitle').innerHTML = imageData[index][0]; $('imageDescription').innerHTML = imageData[index][1]; } /* Return false so that the links do not try to actually go somewhere */ return (false); } The parameter p that is passed to the function is simply –1 to go to the previous image and 1 to go to the next image. This function requires the Prototype Framework and the script.aculo.us library to function. We use script.aculo.us to make the images look more spectacular than if we had just used display: block and display: none. For the images to be served up with Ajax, the developer must rely on the data URL format. The data URL format allows the src of an image to be encoded inline as Base64 content. It looks something like this: A Base64-encoded image. where the [...] is replaced with the Base64-encoded image data. Now for the bad news: Internet Explorer 7 and earlier do not recognize the data URL format for an src image. Have no fear, though: I will address this before I finish this section. Assuming that we can get our image as a Base64-encoded string (we will address this when we discuss the server end of this application) our next task is the format that the Ajax request will receive. The following XML gives an example of a possible format: The Image's Title This is the description for the Image. The most important thing to note about this format (other than that it is really simple) is that the Base64-encoded string is inside a CDATA section. Without this, the browser does not recognize the string as true text, and no image will render. Our Ajax call will end up pseudorecursively calling itself until there are no more images to get, as Example 8-13 shows. Example 8-13 combines all of our JavaScript into a single file for the XHTML page to include. 308 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-13. slideshow.js: Code used to load our images with Ajax and move through them /** * @fileoverview This file, slideshow.js, builds up a list of images dynamically * through continuous /XMLHttpRequest/ calls to the server for data until all * pictures have been placed in the list. This allows the application to load * faster, as it does not have to load all of the images before the page is * functional. The application then allows users to view the list of images as * a slide show, viewing one image (and all of its associated data) at a time. * */ /** * This variable holds the current image number to be loaded. */ var imageNumber; /** * This variable holds the extra image information (title, description). */ var imageData = []; /** * This variable holds the index of which picture is being viewed. */ var index = 0; /** * This function, setupApp, sets the initial image number to start pulling from * the server, then calls the /fetchNextImage/ function which starts the Ajax * calls. * * @see fetchNextImage */ function setupApp( ) { imageNumber = 0; fetchNextImage( ); } /** * This function, fetchNextImage, checks to make sure it should make an Ajax call * based on the image number, and then calls the Prototype Framework's * /Ajax.Request/ method and creates a function to handle the results. * * @see Ajax#Request */ function fetchNextImage( ) { /* Is the image number bigger than 0? */ if (++imageNumber > 0) { /* Call sendPhoto.php with the number of the photo */ new Ajax.Request('sendPhoto.php', { method: 'post', parameters: 'number=' + imageNumber, /* Lists for All Seasons | www.it-ebooks.info 309 Example 8-13. slideshow.js: Code used to load our images with Ajax and move through them (continued) * The onSuccess method checks to see if the number of elements sent * via XML is greater than one (one means there was an error). If * it is, then it creates a new list item and image, placing the * latter inside the former before adding the Base64–encoded string * into the image's src. */ onSuccess: function(xhrResponse) { /* Did we get an XML response we want? */ if (xhrResponse .responseXML.documentElement.childNodes.length > 1) { /* Create new elements within the DOM document */ var newItem = document.createElement('li'); var newImage = document.createElement('img'); /* * Add id attributes to both and put the image in the list * item */ newImage.setAttribute('id', 'i' + imageNumber); newItem.appendChild(newImage); newItem.setAttribute('id', 'l' + imageNumber); /* Add the new image to the list, then hide it */ $('slideshowList').appendChild(newItem); Element.hide($('l' + imageNumber)); /* Add the Base64-encoded string */ $('i' + imageNumber).src = 'data:image/jpg;base64,' + xhrResponse.responseXML.documentElement.getElementsByTagName( 'image')[0].firstChild.nodeValue; /* Create the next index in the array to hold the image data */ imageData[(imageNumber - 1)] = []; imageData[(imageNumber - 1)][0] = xhrResponse.responseXML.documentElement.getElementsByTagName( 'title')[0].firstChild.nodeValue; imageData[(imageNumber - 1)][1] = xhrResponse.responseXML.documentElement.getElementsByTagName( 'description')[0].firstChild.nodeValue; /* Is this the first image? */ if (imageNumber <= 1) { /* Set the initial image and show it */ index = 0; Effect.Appear($('l' + imageNumber)); $('imageTitle').innerHTML = imageData[0][0]; $('imageDescription').innerHTML = imageData[0][1]; } /* Recursive call! */ fetchNextImage( ); } else { /* We are done */ imageNumber = -1; } } }); 310 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info Example 8-13. slideshow.js: Code used to load our images with Ajax and move through them (continued) } } /** * This function, changeSlide, moves the display of individual
            • elements one * element at a time while hiding all other elements to give the illusion of moving * back and forth through a slide show of
            • elements. * * @param {Integer} p_slideDirection The direction of the slide change (-1 back * and 1 forward). * @return Returns false so that the element that had the event click stops any * default events. * @type Boolean * @see Element#hide * @see Effect#Appear */ function changeSlide(p_slideDirection) { /* Is the index going to be too small or too large? */ if (!((index + p_slideDirection) < 0 || (index + p_slideDirection) > $('slideshowList').childNodes.length - 1)) { index += p_slideDirection; /* Loop through the unordered list and hide all images */ var items = $('slideshowList').getElementsByTagName('li'); for (var i = 0, il = items.length; i < il; i++) Element.hide(items[i]); /* * Now make the image to be changed appear, and change the title and * description. */ Effect.Appear($('slideshowList').childNodes[index]); $('imageTitle').innerHTML = imageData[index][0]; $('imageDescription').innerHTML = imageData[index][1]; } /* Return false so that the links do not try to actually go somewhere */ return (false); } try { /* Call /setupApp( )/ when the page is loaded */ Event.observe(window, 'load', setupApp, false); } catch (ex) {} Now that we have some working script, we can finish the XHTML file to pull this together, as shown in Example 8-14. Example 8-14. ajax_slideshow.html: A working slide show utilizing Ajax An Ajax Slide Show Lists for All Seasons | www.it-ebooks.info 311 Example 8-14. ajax_slideshow.html: A working slide show utilizing Ajax (continued) What about the server, you ask. Don’t worry, I didn’t forget about that. If the scripting language being used on the server side of things is PHP, this is a simple task. PHP has a function, base64_encode( ), that does just what it says (and just what we need it to do). Other server-side scripting languages may have the same functionality, but I chose to be consistent in my use of PHP for server-side examples. On the server, our script is going to require an image number be passed to it so that it knows what picture to send back. Assuming that all of our data, including the image as a BLOB, is sitting in a MySQL database, Example 8-15 shows how we can write the server script to send data back to the client. Example 8-15. sendPhoto.php: PHP script that gets an image out of a database and sends the results to the client $host, 'username' => $username, 'password' => $password, 'dbname' => $db); try { /* Connect to the database */ $conn = Zend_Db::factory('PDO_MYSQL', $params); /* Query the database with the passed number */ $sql = 'SELECT encoded_string, title, description FROM pictures WHERE ' .'pic_id = \'pic_'.$_REQUEST['number'].'\';'; /* Get the results of the query */ $result = $conn->query($sql); /* Are there results? */ if ($rows = $result->fetchAll( )) { /* Build the XML to be sent to the client */ $xml .= ''; foreach($rows in $row) $xml .= ''; $xml .= "{$row['title']}"; $xml .= "{$row['description']}"; $xml .= ''; } else /* No records...return error */ $xml .= '-1'; } catch (Exception $e) { /* Uh oh, something happened, so we need to return an error */ $xml .= '-1'; } } else /* A number was not passed in, so return an error */ $xml .= '-1'; /* * Change the header to text/xml so that the client can use the return string * as XML. Lists for All Seasons | www.it-ebooks.info 313 Example 8-15. sendPhoto.php: PHP script that gets an image out of a database and sends the results to the client (continued) */ header('Content-Type: text/xml'); /* Give the client the XML */ print($xml); Not only does this method allow the user to interact with the slide show application while images are being loaded, but also errors can be trapped when requesting images. This is something other image loading techniques failed to handle adequately. Figure 8-9 shows what this application would look like in action. Figure 8-9. An Ajax-enabled slide show application As for Internet Explorer, things are grim but not completely bleak yet. There is a workaround to the data URL problem that, to my knowledge, Dean Edwards created (see his blog, at http://dean.edwards.name/weblog/2005/06/base64-ie/). His solution is to send the Base64-encoded string back to the server, let PHP decode the string, and send back the result as an image. For this to work dynamically, he relies on Internet Explorer’s support for nonstandard dynamic CSS expressions—specifically, the behavior property. First, you need a JavaScript function to ready the encoded string: 314 | Chapter 8: Fun with Tables and Lists www.it-ebooks.info /* The regular expression to test for Base64 data */ var BASE64_DATA = /^data:.*;base64/i; /* This is the path to the PHP that will decode the string */ var base64Path = 'base64.php'; /* The fixBase64 function will handle getting the new image by calling the PHP */ function fixBase64(img) { /* Stop the CSS expression from being endlessly evaluated */ img.runtimeStyle.behavior = 'none'; /* Should we apply the fix? */ if (BASE64_DATA.test(img.src)) /* * Setting the src to an external source makes it do the call (Ajax, * sort of). */ img.src = base64Path + '?' + img.src.slice(5); } Then you need to have the dynamic CSS expression call this function: img {behavior: expression(fixBase64(this));} For a more elegant and completely CSS version, you can wrap all the JavaScript code into the CSS expression, like this: img { behavior: expression((this.runtimeStyle.behavior = "none") && (/^data:.*;base64/i.test(this.src)) && (this.src="/my/base64.php?" + this.src.slice(5))) } That was easy to follow, wasn’t it? Now, all we have left is to handle this on the server. This is a simple solution in PHP: Just like that, we now have a cross-browser solution for our slide show application. The Ajax slide show application shows just one more way in which lists can be useful for the structure of a dynamic widget. With proper styling and minor changes to the JavaScript, a developer could use a definition list instead of an unordered list. Then all of the image data could be stored together. The CSS would probably be more complex, and it may or may not simplify the JavaScript. However, this sort of solution would make the slide show more accessible, so it deserves some serious thought. Lists for All Seasons | www.it-ebooks.info 315 Chapter 9 9 CHAPTER Page Layout with Frames That Aren’t 9 Many of us don’t realize how much site layout decisions affect end users. These kinds of decisions are a little outside the scope of this book (they are truly design issues). However, there are some important questions regarding how the site is laid out from a coding standpoint, not from the designer’s point of view. By coding, I mean the design of elements that are used to define the application’s structure. These elements are the controls and widgets that go into an application built with XHTML, CSS, and JavaScript (and that you can enhance with Ajax). Sites used to be structured with frames in the old days of web building, especially when the sites were doing more than just showing one page at a time. That changed out of necessity, as DHTML took hold and the limitations of frames became more evident. Using Frames Frames allow a developer to divide an application page into named sections that can still interact, but never overflow into one another. This has its advantages and disadvantages, as you can well imagine. On the one hand, it allows for easy layout from a development point of view. On the other hand, it is hard to create dynamic content that can interact anywhere on the page, because anything dynamic is constrained to its own frame. If you decide to use frames, the XHTML 1.0 Frameset document type definition (DTD) is available, as is the HTML 4.01 Frameset DTD. Use whichever you like, but remember, the Web deals with XML a great deal, and that trend will not stop anytime soon. It would be better to not have to change so much of a site by at least following XML standards and using the XHTML 1.0 Frameset DTD. 316 www.it-ebooks.info The declaration tag for HTML 4.01 Framesets is: The declaration tag for XHTML 1.0 Framesets is: It is important to use proper declarations in your application so that your browser stays in Standards mode when rendering pages. The Frameset and Frame You use the element to define the page’s frameset—what a surprise! You use it to organize multiple rows and columns that may be nested within the page with a element. Within each frame is a separate document that will be loaded. You specify rows and columns for the page through the element’s two attributes: rows and cols. Table 9-1 shows the attributes available for a element. Table 9-1. The available attributes for the element Attribute Value Description cols Pixels This attribute defines the number of columns in a frameset as well as their sizes. % * rows Pixels This attribute defines the number of rows in a frameset as well as their sizes. % * Whereas the element defines the basic structure of the page, the element defines the details of each subwindow in the page. You specify these pages in the element with the src attribute. Within the element, most style attributes are defined, a list of which appears in Table 9-2. Table 9-2. The available attributes for the element Attribute Value Description frameborder 0 1 This attribute defines whether a border is displayed around the frame. longdesc URL This attribute is a URL to the long description of the frame that is used for browsers that do not support frames. marginheight Pixels This attribute defines the top and bottom margins for the frame. Using Frames | www.it-ebooks.info 317 Table 9-2. The available attributes for the element (continued) Attribute Value Description marginwidth Pixels This attribute defines the right and left margins for the frame. name frame_name This attribute defines a unique name for the frame so that the Document Object Model (DOM) may identify it. noresize noresize This attribute, when set, prevents the user from being able to resize the frame. scrolling yes no auto This attribute defines the actions the scroll bars can take. src URL This attribute defines the URL of the page to show in the frame. Some browsers still do not support frames. To allow for this case, there is an optional element: . Within this element, you can place normal body content (including the body element) to inform the user of the circumstances, or to provide her with alternative pages to view the content. An example of a complete frameset appears in Example 9-1. Example 9-1. A simple frameset layout that was and is popular with many web designers <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Simple Frameset Layout</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta name="author" content="Anthony T. Holdener III (ath3)" /> <meta http-equiv="imagetoolbar" content="no" /> </head> <frameset rows="15%, *, 5%"> <frame id="topFrame" name="topFrame" noresize="noresize" scrolling="no" src="" /> <frameset cols="25%, *"> <frame id="navigationFrame" name="topFrame" noresize="noresize" scrolling="no" src="" /> <frame id="contentFrame" name="topFrame" noresize="noresize" scrolling="no" src="" /> </frameset> <frame id="bottomFrame" name="topFrame" noresize="noresize" scrolling="no" src="" /> <noframes> <body> This application requires frames for complete use. Please go to the <a href="index2.html">Text Version</a> of this application for a better experience. Sorry for the inconvenience. </body> Figure 9-1 gives you an idea of how this frameset would look. 318 | Chapter 9: Page Layout with Frames That Aren’t www.it-ebooks.info Figure 9-1. The simple frameset layout, shown in Firefox With the frames of the page constructed, the developer must now develop the four XHTML pages that will make up these individual frames in the design. What is nice about this sort of layout is that the user can navigate without having to refresh all the frames; only the one active frame is refreshed. For a properly validated application, only the and elements can be used when the DOCTYPE has been declared in the page prolog. That DOCTYPE must be one of the Frameset DTDs. The iframe Craze Frames are a good start to a well-structured site, but as I said, they have some limitations. One of the biggest of these limitations, at least as far as DHTML is concerned, is that content cannot overlap from one frame to the next. Think of the setup of frames from Figure 9-1, with navigation being the left-side frame. If an application needs, say, a pull-out vertical menu, the menu has to fit inside the width of the frame. If it doesn’t, the frame will scroll to accommodate the objects inside it, at least if the proper attributes are set on the frame to allow it to handle objects larger than its width. This scenario also occurs on a site that has a drop-down menu in its top frame. I am not slighting frames. They are useful for any site with content (e.g., a logo at the top, a menu or some other navigation widget, a footer, etc.) that does not change as the page content changes. This is a very good approach for keeping the site faster by cutting down on the amount of data the client must retrieve. As the site or application becomes more dynamic, however, different solutions must be found. Using Frames | www.it-ebooks.info 319 The 324 | Chapter 9: Page Layout with Frames That Aren’t www.it-ebooks.info However, more options with using a
              element can make the application look more modern. For example, the developer can change the size of the margin or padding within the