- XML Reference Guide
- Overview
- What Is XML?
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Table of Contents
- The Document Object Model
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- DOM and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- Implementations
- DOM and JavaScript
- Using a Repeater
- Repeaters and XML
- Repeater Resources
- DOM and .NET
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Downloads
- DOM and C++
- DOM and C++ Resources
- DOM and Perl
- DOM and Perl Resources
- DOM and PHP
- DOM and PHP Resources
- DOM Level 3
- DOM Level 3 Core
- DOM Level 3 Load and Save
- DOM Level 3 XPath
- DOM Level 3 Validation
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Implementations
- The Simple API for XML (SAX)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- SAX and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- SAX and .NET
- Informit Articles and Sample Chapters
- SAX and Perl
- SAX and Perl Resources
- SAX and PHP
- SAX and PHP Resources
- Validation
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Document Type Definitions (DTDs)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XML Schemas
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- RELAX NG
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Schematron
- Official Documentation and Implementations
- Validation in Applications
- Informit Articles and Sample Chapters
- Books and e-Books
- XSL Transformations (XSLT)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XSLT in Java
- Java in XSLT Resources
- XSLT and RSS in .NET
- XSLT and RSS in .NET Resources
- XSL-FO
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XPath
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XML Base
- Informit Articles and Sample Chapters
- Official Documentation
- XHTML
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XHTML 2.0
- Documentation
- Cascading Style Sheets
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XUL
- XUL References
- XML Events
- XML Events Resources
- XML Data Binding
- Informit Articles and Sample Chapters
- Books and e-Books
- Specifications
- Implementations
- XML and Databases
- Informit Articles and Sample Chapters
- Books and e-Books
- Online Resources
- Official Documentation
- SQL Server and FOR XML
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Implementations
- Service Oriented Architecture
- Web Services
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Creating a Perl Web Service Client
- SOAP::Lite
- Amazon Web Services
- Creating the Movable Type Plug-in
- Perl, Amazon, and Movable Type Resources
- Apache Axis2
- REST
- REST Resources
- SOAP
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- SOAP and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- WSDL
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- UDDI
- UDDI Resources
- XML-RPC
- XML-RPC in PHP
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Ajax
- Asynchronous Javascript
- Client-side XSLT
- SAJAX and PHP
- Ajax Resources
- JSON
- Ruby on Rails
- Creating Objects
- Ruby Basics: Arrays and Other Sundry Bits
- Ruby Basics: Iterators and Persistence
- Starting on the Rails
- Rails and Databases
- Rails: Ajax and Partials
- Rails Resources
- Web Services Security
- Web Services Security Resources
- SAML
- Informit Articles and Sample Chapters
- Books and e-Books
- Specification and Implementation
- XML Digital Signatures
- XML Digital Signatures Resources
- XML Key Management Services
- Resources for XML Key Management Services
- Internationalization
- Resources
- Grid Computing
- Grid Resources
- Web Services Resource Framework
- Web Services Resource Framework Resources
- WS-Addressing
- WS-Addressing Resources
- WS-Notifications
- New Languages: XML in Use
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Google Web Toolkit
- GWT Basic Interactivity
- Google Sitemaps
- Google Sitemaps Resources
- Accessibility
- Web Accessibility
- XML Accessibility
- Accessibility Resources
- The Semantic Web
- Defining a New Ontology
- OWL: Web Ontology Language
- Semantic Web Resources
- Google Base
- Microformats
- StructuredBlogging
- Live Clipboard
- WML
- XHTML-MP
- WML Resources
- Google Web Services
- Google Web Services API
- Google Web Services Resources
- The Yahoo! Web Services Interface
- Yahoo! Web Services and PHP
- Yahoo! Web Services Resources
- eBay REST API
- WordML
- WordML Part 2: Lists
- WordML Part 3: Tables
- WordML Resources
- DocBook
- Articles
- Books and e-Books
- Official Documentation and Implementations
- XML Query
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XForms
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Resource Description Framework (RDF)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Topic Maps
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation, Implementations, and Other Resources
- Rich Site Summary (RSS)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Simple Sharing Extensions (SSE)
- Atom
- Podcasting
- Podcasting Resources
- Scalable Vector Graphics (SVG)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- OPML
- OPML Resources
- Summary
- Projects
- JavaScript TimeTracker: JSON and PHP
- The Javascript Timetracker
- Refactoring to Javascript Objects
- Creating the Yahoo! Widget
- Web Mashup
- Google Maps
- Indeed Mashup
- Mashup Part 3: Putting It All Together
- Additional Resources
- Frequently Asked Questions About XML
- What's XML, and why should I use it?
- What's a well-formed document?
- What's the difference between XML and HTML?
- What's the difference between HTML and XHTML?
- Can I use XML in a browser?
- Should I use elements or attributes for my document?
- What's a namespace?
- Where can I get an XML parser?
- What's the difference between a well-formed document and a valid document?
- What's a validating parser?
- Should I use DOM or SAX for my application?
- How can I stop a SAX parser before it has parsed the entire document?
- 2005 Predictions
- 2006 Predictions
- Nick's Book Picks
While all of the principles that make Ajax work have been around for some time, the method of putting them all together is fairly new, so some growing pains are bound to erupt. In fact, if you followed our series on building an Ajax feed reader, the word "pain" may have been foremost in your mind.
I mean, sure, it's powerful, but making it happen is a lot of work. Maintaining asynchronous connections, callback functions and of course the dreaded "browser differences" issue... it's enough to make you wonder why somebody hasn't simplified this and created a library already.
Well, of course someone has. The Simple Ajax (SAJAX) library enables you to call server-side functions directly from the browser.
Well, almost directly.
For example, we can take our feed reader page and use SAJAX to simplify the process of requesting information. We have three "actions" to perform:
- When the user clicks a top-level directory, display the subcategories in the "sub" div.
- When the user clicks a subcategory, display the list of feeds in the "feedlist" div.
- When the user clicks a feed, retrieve the feed and the XSLT stylesheet and display the transform data in a "feed" div.
Let's start by putting together the framework. First, we have the page itself:
<html> <head> <style type="text/css"> span {font-weight: bold; color: blue; text-decoration: underline} #feed {font-size: smaller} </style> <script type="text/javascript"> function getSubcategory(catId){ alert(catId); } </script> </head> <body> <table width="100%" border="0"> <tr> <td id="main" width="200" valign="top"><h3>Main categories</h3> <span onclick="getSubcategory(1)">Arts</span> (608)</br /> <span onclick="getSubcategory(2)">Business</span> (152)</br /> <span onclick="getSubcategory(3)">Comics</span> (21)</br /> <span onclick="getSubcategory(4)">Computers</span> (202)</br /> <span onclick="getSubcategory(5)">Games</span> (29)</br /> <span onclick="getSubcategory(6)">Health</span> (35)</br /> <span onclick="getSubcategory(7)">Home</span> (3)</br /> <span onclick="getSubcategory(8)">News</span> (66)</br /> <span onclick="getSubcategory(9)">Recreation</span> (21) </td> <td width="200" valign="top"><h3>Sub categories</h3> <div id="sub"></div> </td> <td rowspan="2" valign="top"><div id="status"></div> <div id="feed"></div></td> </tr> <tr> <td colspan="2"><h3>Feeds</h3> <div id="feedlist"></div> </td> </tr> </table> </body> </html>
Here we see the HTML page with the divs defined, and with the getSubcategory()
script in place.
That script needs to retrieve a list of categories, which come from a PHP function:
<? function subcategories($cat_id){ return "Subcat for ".$cat_id; } ?> <html> <head> <style type="text/css"> span {font-weight: bold; color: blue; text-decoration: underline} #feed {font-size: smaller} </style> <script type="text/javascript"> function getSubcategory(catId){ alert(catId); } </script> </head> <body> <table width="100%" border="0"> <tr> <td id="main" width="200" valign="top"><h3>Main categories</h3> <span onclick="getSubcategory(1)">Arts</span> (608)</br /> ...
In a moment will add some actual code, but the goal here is to show the calling of the function, not the function itself.
Okay, now we have the script in the page, so we have to hook the two together. Download the
SAJAX package and place the Sajax.php
file in the same directory as your page.
The way we create a "hook" to the PHP function is to "export" the functions want to make available. In this case, that means:
<? function subcategories($cat_id){ return "Subcat for ".$cat_id; } require("Sajax.php"); sajax_init(); sajax_export("subcategories"); sajax_handle_client_request(); ?> <html> <head> ...
Now we can call the function from the JavaScript. To do that, we actually need two JavaScript functions for each PHP function. The first calls the exported function, and the second is the callback function to which the exported function sends its results. For example:
<? function subcategories($cat_id){ return "Subcat for ".$cat_id; } require("Sajax.php"); sajax_init(); sajax_export("subcategories"); sajax_handle_client_request(); ?> <html> <head> <style type="text/css"> span {font-weight: bold; color: blue; text-decoration: underline} #feed {font-size: smaller} </style> <script type="text/javascript"> <? sajax_show_javascript(); ?> function getSubcategory_cb(list) { document.getElementById('sub').innerHTML = list; } function getSubcategory(catId){ x_subcategories(catId, getSubcategory_cb); } </script> </head> <body> <table width="100%" border="0"> <tr> <td id="main" width="200" valign="top"><h3>Main categories</h3> <span onclick="getSubcategory(1)">Arts</span> (608)</br /> <span onclick="getSubcategory(2)">Business</span> (152)</br /> <span onclick="getSubcategory(3)">Comics</span> (21)</br /> ... </td> <td width="200" valign="top"><h3>Sub categories</h3> <div id="sub"></div> </td> <td rowspan="2" valign="top"><div id="status"></div> <div id="feed"></div></td> </tr> ...
When the user clicks a category link, the JavaScript getSubcategory
function calls
x_subcategories
, the exported version of the PHP subcategories
function.
The x_subcategories
function takes the category ID and the function to which it should send
the results, getSubcategories_cb
. That function takes the results and displays them, as
you can see in figure 1.
We can do the same thing for the other functions:
<? function subcategories($cat_id){ return "Subcat for ".$cat_id; } function feeds($cat_id){ return "Feeds for ".$cat_id; } function proxy($feed_url){ return "Proxy for ".$feed_url; } require("Sajax.php"); sajax_init(); sajax_export("subcategories"); sajax_export("feeds"); sajax_export("proxy"); sajax_handle_client_request(); ?> <html> <head> <style type="text/css"> span {font-weight: bold; color: blue; text-decoration: underline} #feed {font-size: smaller} </style> <script type="text/javascript"> <? sajax_show_javascript(); ?> function getSubcategory_cb(list) { document.getElementById('sub').innerHTML = list; } function getSubcategory(catId){ x_subcategories(catId, getSubcategory_cb); } function getFeeds_cb(list) { document.getElementById('feedlist').innerHTML = list; } function getFeeds(catId){ x_feeds(catId, getFeeds_cb); } function loadFeed_cb(feedStream){ document.getElementById('feed').innerHTML = feedStream; } function loadFeed(feedURL){ x_proxy(feedURL, loadFeed_cb); } </script> </head> <body> ...
Now we can start adding the actual code. Again, the important thing is not so much what the functions are doing, but that they're being called, so we can just have them output the results:
<? function subcategories($cat_id){ if ($cat_id == 1){ return "<span onclick='getFeeds(100)'>Animation</span> (1)<br />". "<span onclick='getFeeds(101)'>Architecture</span> (1)<br />". "<span onclick='getFeeds(102)'>Comics</span> (3)<br />". "<span onclick='getFeeds(103)'>Digital</span> (1)<br />". "<span onclick='getFeeds(104)'>Entertainment</span> (0)<br />". "<span onclick='getFeeds(105)'>Science Fiction</span> (2)<br />". "<span onclick='getFeeds(106)'>Literature</span> (2)<br />". "<span onclick='getFeeds(107)'>Movies</span> (9)<br />". "<span onclick='getFeeds(108)'>Music</span> (292)<br />". "<span onclick='getFeeds(109)'>Online_Writing</span> (3)<br />". "<span onclick='getFeeds(110)'>People</span> (255)<br />". "<span onclick='getFeeds(111)'>Radio</span> (1)<br />". "<span onclick='getFeeds(112)'>Television</span> (37)<br />". "<span onclick='getFeeds(113)'>Writers_Resources</span> (1)<br />"; } else if ($cat_id == 2) { return "Cat 2, Subcat 1<br />Cat 2, Subcat 2<br />Cat 2, Subcat 3<br />Cat 2, Subcat 4<br />Cat 2, Subcat 5<br />Cat 2, Subcat 6<br />Cat 2, Subcat 7<br />Cat 2, Subcat 8<br />Cat 2, Subcat 9<br />Cat 2, Subcat 10<br />"; } else { return "Cat 3, Subcat 1<br />Cat 3, Subcat 2<br />Cat 3, Subcat 3<br />Cat 3, Subcat 4<br />Cat 3, Subcat 5<br />Cat 3, Subcat 6<br />Cat 3, Subcat 7<br />at 3, Subcat 8<br />Cat 3, Subcat 9<br />Cat 3, Subcat 10<br />"; } ... } function feeds($cat_id){ return '<br /> <span onclick="loadFeed(\'http://www.vanguardreport.com/phpnuke/backend.php\')">The Vanguard Science Fiction Report</span><br /> <span onclick="loadFeed(\'http://www.nicholaschase.com/blog/index.rdf\')">Chaos Magnet</span><br />'; } function proxy($feed_url){ return file_get_contents($feed_url); } require("Sajax.php"); sajax_init(); $sajax_debug_mode = 1; sajax_export("subcategories"); sajax_export("feeds"); sajax_export("proxy"); ...
At this point, you should be able to request a list of categories and feeds, and display a raw feed. But what about transforming the feed?
Well, you can do that on the server-side, or on the client side. To do it on the server-side, you'd simply modify the
proxy
function to perform the transformation before returning the results.
That, however, assumes that your server can perform transformations, and in this case -- rented space on a shared server -- that's not true. So instead, were going to handle it as we did before, loading both the stylesheet and the raw feed and transforming it in the browser:
<? function subcategories($cat_id){ if ($cat_id == 1){ return "<span onclick='getFeeds(100)'>Animation</span> (1)<br />". "<span onclick='getFeeds(101)'>Architecture</span> (1)<br />". "<span onclick='getFeeds(102)'>Comics</span> (3)<br />". "<span onclick='getFeeds(103)'>Digital</span> (1)<br />". "<span onclick='getFeeds(104)'>Entertainment</span> (0)<br />". "<span onclick='getFeeds(105)'>Science Fiction</span> (2)<br />". "<span onclick='getFeeds(106)'>Literature</span> (2)<br />". "<span onclick='getFeeds(107)'>Movies</span> (9)<br />". "<span onclick='getFeeds(108)'>Music</span> (292)<br />". "<span onclick='getFeeds(109)'>Online_Writing</span> (3)<br />". "<span onclick='getFeeds(110)'>People</span> (255)<br />". "<span onclick='getFeeds(111)'>Radio</span> (1)<br />". "<span onclick='getFeeds(112)'>Television</span> (37)<br />". "<span onclick='getFeeds(113)'>Writers_Resources</span> (1)<br />"; } else if ($cat_id == 2) { return "Cat 2, Subcat 1<br />Cat 2, Subcat 2<br />Cat 2, Subcat 3<br />Cat 2, Subcat 4<br />Cat 2, Subcat 5<br />Cat 2, Subcat 6<br />Cat 2, Subcat 7<br />Cat 2, Subcat 8<br />Cat 2, Subcat 9<br />Cat 2, Subcat 10<br />"; } else { return "Cat 3, Subcat 1<br />Cat 3, Subcat 2<br />Cat 3, Subcat 3<br />Cat 3, Subcat 4<br />Cat 3, Subcat 5<br />Cat 3, Subcat 6<br />Cat 3, Subcat 7<br />at 3, Subcat 8<br />Cat 3, Subcat 9<br />Cat 3, Subcat 10<br />"; } } function feeds($cat_id){ return '<br /> <span onclick="loadFeed(\'http://www.vanguardreport.com/phpnuke/backend.php\')">The Vanguard Science Fiction Report</span><br /> <span onclick="loadFeed(\'http://www.nicholaschase.com/blog/index.rdf\')">Chaos Magnet</span><br />'; } function proxy($feed_url){ return file_get_contents($feed_url); } require("Sajax.php"); sajax_init(); sajax_export("subcategories"); sajax_export("feeds"); sajax_export("proxy"); sajax_handle_client_request(); ?> <html> <head> <style type="text/css"> span {font-weight: bold; color: blue; text-decoration: underline} #feed {font-size: smaller} </style> <script type="text/javascript"> <? sajax_show_javascript(); ?> function getSubcategory_cb(list) { document.getElementById('sub').innerHTML = list; } function getSubcategory(catId){ x_subcategories(catId, getSubcategory_cb); } function getFeeds_cb(list) { document.getElementById('feedlist').innerHTML = list; } function getFeeds(catId){ x_feeds(catId, getFeeds_cb); } var styleLoaded = false; var feedLoaded = false; var rawFeed = null; var styleSheet = null; function loadFeed_cb(feedStream){ rawFeed = feedStream; feedLoaded = true; if (styleLoaded){ transformFeed(); } } function loadStyle_cb(styleStream){ styleSheet = styleStream; styleLoaded = true; if (feedLoaded){ transformFeed(); } } function loadFeed(feedURL){ x_proxy(feedURL, loadFeed_cb); x_proxy("http://www.nicholaschase.com/ajaxdemo/rss1.xsl", loadStyle_cb); } function transformFeed(){ if (window.XMLHttpRequest){ var dp = new DOMParser(); theDocument = dp.parseFromString(rawFeed, "text/xml"); var dp2 = new DOMParser(); stylesheetDoc = dp2.parseFromString(styleSheet, "text/xml"); var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(stylesheetDoc); response = xsltProcessor.transformToFragment(theDocument, document); destinationDiv = document.getElementById("feed"); destinationDiv.innerHTML = ""; destinationDiv.appendChild(response); feedLoaded = false; rawFeed = null; } else if (window.ActiveXObject) { stylesheetDoc = new ActiveXObject("Microsoft.XMLDOM"); stylesheetDoc.async = false; stylesheetDoc.loadXML(styleSheet); theDocument = new ActiveXObject("Microsoft.XMLDOM"); theDocument.async = false; theDocument.loadXML(rawFeed); destinationDiv = document.getElementById("feed"); destinationDiv.innerHTML = theDocument.transformNode(stylesheetDoc); feedLoaded = false; rawFeed = null; } } </script> </head> <body> <table width="100%" border="0"> <tr> <td id="main" width="200" valign="top"><h3>Main categories</h3> <span onclick="getSubcategory(1)">Arts</span> (608)</br /> <span onclick="getSubcategory(2)">Business</span> (152)</br /> <span onclick="getSubcategory(3)">Comics</span> (21)</br /> <span onclick="getSubcategory(4)">Computers</span> (202)</br /> <span onclick="getSubcategory(5)">Games</span> (29)</br /> <span onclick="getSubcategory(6)">Health</span> (35)</br /> <span onclick="getSubcategory(7)">Home</span> (3)</br /> <span onclick="getSubcategory(8)">News</span> (66)</br /> <span onclick="getSubcategory(9)">Recreation</span> (21) </td> <td width="200" valign="top"><h3>Sub categories</h3> <div id="sub"></div> </td> <td rowspan="2" valign="top"><div id="status"></div> <div id="feed"></div></td> </tr> <tr> <td colspan="2"><h3>Feeds</h3> <div id="feedlist"></div> </td> </tr> </table> </body> </html>
In this case, we can use the same exported function, x_proxy
, but send the results to
two different callback functions.
Each one checks to see if the other has finished yet -- remember, were working asynchronously here -- and if so,
performs the transformation and displays the results.
And we didn't have to touch the XMLHttpRequest
object once.