Progressive Enhancement with Spry

Progressive enhancement is a technique used by web developers to ensure that their pages are accessible to as wide an audience as possible while at the same time trying to take advantage of the latest browser enhancements to provide the best user experience for their users. The basic idea behind progressive enhancement is to start off by creating a basic version of your page that contains your content and semantic markup that is supported by the lowest common denominator browser you intend to target. Once you are done, you can then alter the display and behavior of elements on the page with the use of technologies such as CSS, JavaScript, and Flash.

By designing your pages in this manner, the user and any assistive technologies they use, or search engine spiders, should be able to access your page content, even in the absence of CSS, JavaScript and Flash.

This document will give examples of how to use the various components within Spry in a progressive enhancement scenario and introduce some techniques and utilities that will hopefully make it easy:

Here is a very basic example that illustrates progressive enhancement with JavaScript. Given the following basic markup:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Basic Markup</title>
</head>
<body>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

We can progressively enhance it so that each time the user's cursor is over the content, it turns a different color:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Basic Markup</title>
</head>
<body>
<div id="event" onmouseover="this.style.backgroundColor = 'yellow';" onmouseout="this.style.backgroundColor = 'white'">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

In the example above, onmouseover and onmouseout attributes were added to the <div> with the id of "event". The value of each of those attributes is some JavaScript that the browser will execute anytime the user's cursor enters or exits the "event" <div>. While this is a very trivial example, it illustrates the addition of a behavior to the basic page. With JavaScript on, users will see this new behavior as they move the cursor around on the page, but, with JavaScript off, all of the content on the page is still accessible.

Spry Widgets and Effects

Because Spry Effects and Widgets both work on elements that *must* be present within the page, they are well suited for use with progressive enhancement. For example, given this basic semantic markup:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Basic Markup</title>
</head>
<body>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to see a live version of the example above.)

We can progressively enhance this markup with CSS and some JavaScript behaviors that turn it into a collapsible panel:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Progressively Enhanced Markup</title>
<script type="text/javascript" src="../../../widgets/collapsiblepanel/SpryCollapsiblePanel.js"></script>
<link href="css/sample.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
<script type="text/javascript">
<!--
var cp = new Spry.Widget.CollapsiblePanel("event", { contentIsOpen: false });
-->
</script>
</body>
</html>

(Click here to see a live version of the example above.)

In the example above, SpryCollapsiblePanel.js was included to enable support for a collapsible panel. We also includes a stylesheet that has some rules that make the "event" div look like a collapsible panel. At the very bottom of the document is a script block which attaches the collapsible panel behaviors to the "event" element and some of its descendants.

If you load the example above into your browser, you'll notice that the page displays a collapsible panel tab. If you click on this tab, it animates to reveal additional content. If you turn off CSS and JavaScript support in your browser and re-load this same example, you will notice that the content is entirely accessible, just as it is in the basic example.

Here's an example of progressively enhancing the same basic source with an effect that highlights the content as you mouse over its header:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Progressively Enhanced Markup</title>
<script type="text/javascript" src="../../../includes/SpryEffects.js"></script>
</head>

<body>
<div class="event">
	<h2 class="name" onmouseover="Spry.Effect.DoHighlight('AquoThonContent', {from: '#FFF', to:'#FFCC33', toggle: true });" onmouseout="Spry.Effect.DoHighlight('AquoThonContent', {from: '#FFF', to:'#FFCC33', toggle: true });">Aquo-Thon</h2>
	<div id="AquoThonContent" class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to see the sample above in a browser.)

As with the previous example, if you turn off CSS and JavaScript in your browser, the content is still accessible.

Spry Data Sets and Regions

Before integrating support for Spry data sets and regions into your existing pages, take a moment to think about how and where within your site this support will be surfaced, and about the potential impact this may have on the accessibility and search engine indexing of your site. For some folks, after playing with some of the Spry demos and samples, their first inclination is to externalize all of the content from their existing pages so they can use data sets and regions to swap out the page contents without full page refreshes. For example, they start using pages that look like this throughout their site:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Page With No Content Sample</title>
<script src="../../../includes/xpath.js" type="text/javascript"></script>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script type="text/javascript">
var dsEvents = new Spry.Data.XMLDataSet("events.xml", "/events/event");
</script>
</head>
<body>
<div id="content" spry:region="dsEvents">
	<div spry:repeat="dsEvents" class="event">
		<h2 class="name">{name}</h2>
		<div class="info">		
			<div class="location">{location}</div>
			<div class="date">{date}</div>
			<p class="description">{description}</p>
			<a href="{location}">directions...</a>
		</div>
	</div>
</div>
</body>
</html>

(Click here to see the sample above in a browser.)

Although it looks fine and renders with content in a browser when JavaScript is turned on, it renders without content when JavaScript is turned off:

{name}

{location}
{date}

{description}

directions...

(Click here to see the example above in a browser.)

This means the content is not accessible to users and screen readers when running in an environment where JavaScript must be turned off. This is also what most search engine web crawlers will see, which means your content will not be found and indexed.

The only way to solve this problem is to follow the progressive enhancement methodology, and start with a basic page that actually contains static content.

Over the last few releases, the Spry team has been steadily introducing new features within the framework that will allow the developer to progressively enhance their pages to load content dynamically. These features include the use of the spry:content attribute within regions, the HTML Data Set, Spry.Utils.updateContent() utility method, and the HTML Panel Widget.

Progressive Enhancement and Dynamic Loading of Content With Spry

Below are some samples of how to use various features within Spry to progressively enhance your static pages so they can dynamically load new content.

Each feature comes with a set of requirements, so which feature you decide to use depends on what it is you are trying to accomplish. For example, if you are trying to make a page dynamically load and display *existing* HTML content without resorting to modifying or creating any server-side scripts to serve up fragments of your existing pages, you may want to use an HTML Data Set or the HTML Panel since they both have the ability to extract HTML fragments out of existing pages. Or, if you want to take a server side approach and serve up HTML fragments embedded within XML/JSON or some other format, along with instructions on where to insert them on the page, perhaps using a data set, suited to the particular format being served (XML, JSON, etc), with or without regions is a better approach.

The samples below all start with a page that contains static content, and links that take you to other pages with static content. They are then progressively enhanced with JavaScript to intercept clicks on links to prevent the browser from following the link and instead trigger some code we added to dynamically load content and replace portions of the page without a full page refresh. If this concept sounds familiar to some folks, its because it is the same approach described by the Hijax methodology.

Using Spry.Utils.updateContent()

Click to showhide the content for this section.

The Spry.Utils.updateContent() utility function allows developers to easily load HTML fragments into a specific element on the page. You simply call it with the node or id of the element who's content you want to replace, and the URL to the HTML fragment and it does the rest. Unlike some of the other methods for dynamically loading content mentioned in this document, Spry.Utils.updateContent() is a no-frills implementation that uses the XMLHttpRequest object under the hood to load the HTML fragment off the network, and the innerHTML property of the element it was given to insert the fragment into the document.

The Spry.Utils.updateContent() function takes an optional callback function as an argument. If supplied, this callback will be called *after* the HTMLFragment is inserted into the document. It does *not* provide a mechanism for examining the content received by the server before it is inserted into the document. Also, unlike the HTMLPanel widget, Spry.Utils.updateContent() does *not* have the ability to extract a fragment out of a larger HTML document. It will always insert *everything* it receives into the document.

Spry.Utils.updateContent() is well suited for pages that need a quick way to interact with a server application that can deliver raw HTML fragments to minimize page refreshes.

Here's an example of a basic page that contains links that take you to other pages:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
</head>

<body>
<p>
<a href="data/AquoThon.html">Aquo-Thon</a> |
<a href="data/AquoSwing.html">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html">Aquo Extreme Ski</a>
</p>

<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the example above.)

Here is that same page progressively enhanced to intercept a click on a link so that an HTML fragment can be loaded directly into an element on the page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
</head>

<body>
<p>
<a href="data/AquoThon.html" onclick="Spry.Utils.updateContent('event', 'data/AquoThonFrag.html'); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="Spry.Utils.updateContent('event', 'data/AquoSwingFrag.html'); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="Spry.Utils.updateContent('event', 'data/AquoUltraMarathonFrag.html'); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="Spry.Utils.updateContent('event', 'data/AquoExtremeSkiFrag.html'); return false;">Aquo Extreme Ski</a>
</p>

<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the example above.)

In the example above, onclick attributes were added to override the default link behavior. When JavaScript is enabled, and the user clicks on a link, the onclick code will call Spry.Utils.updateContent() with the id of the element on the page to update, and the URL to the HTML fragment to load. As you click on the links in this example, pay attention to the URL displayed by the browser. When JavaScript is on, the URL never changes because fragments of HTML are being loaded dynamically and replacing content on the page. When JavaScript is off, clicking on the links actually take you to a different page so the URL displayed by the browser changes.

Here's a version of the previous example that has been enhanced to use a fade transition whenever the content changes:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/SpryEffects.js" type="text/javascript"></script>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script type="text/javascript">
<!--

function FadeAndUpdateContent(ele, url)
{
try {
	Spry.Effect.DoFade(ele,{ duration: 500, from: 100, to: 0, finish: function() {
		Spry.Utils.updateContent(ele, url, function() {
				Spry.Effect.DoFade(ele,{ duration: 500, from: 0, to: 100 });
		});
	}});
}catch(e){ alert(e); }
}

-->
</script>
<style type="text/css">
/* IE HACK to prevent bad rendering when fading. */
#event { background-color: white; }
</style>
</head>

<body>
<p>
<a href="data/AquoThon.html" onclick="FadeAndUpdateContent('event', 'data/AquoThonFrag.html'); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="FadeAndUpdateContent('event', 'data/AquoSwingFrag.html'); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="FadeAndUpdateContent('event', 'data/AquoUltraMarathonFrag.html'); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="FadeAndUpdateContent('event', 'data/AquoExtremeSkiFrag.html'); return false;">Aquo Extreme Ski</a>
</p>

<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the example above.)

As with the previous example, the example above uses the same onclick attribute on links technique to override the default link behavior. In this particular example instead of calling Spry.Utils.updateContent() directly, an intermediate function is called which fires of an effect to fade out the current content within the "event" element. Once that effect completes, it fires off a call to Spry.Utils.updateContent(), but notice that in this example a 3rd argument is passed into the updateContent() call. This is a callback which will fade the content back in after updateContent() has finished loading and updating the "event" element's content.

Using the HTML Panel Widget

Click to showhide the content for this section.

The HTML Panel widget is a JavaScript object that is bound to a specific element container on the page, and has the ability to load a remote HTML fragment, or a piece of an existing HTML page into that element container. Developers can register observers on the HTML Panel Widget to hook into the pre/post load of the HTML fragments, and pre/post update of the element container processes. The HTML Panel widget also introduces the concept of states and provides automatic class name addition/removal for styling different states, and the ability to automatically swap in content provided by the developer during specific panel states.

The HTML Panel is well suited for pages that have well defined areas of the page that can be replaced dynamically. Although it can be used in scenarios that interact with a server application that can deliver raw HTML fragments, the HTML Panel also has the ability to extract out a portion of a full HTML page, based on an element id you give it. This makes it ideal for use in sites where there are lots of pages that differ slightly in well defined areas, for example the main content area, or the header of the page.

Here's an example of progressively enhancing a basic page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
</head>

<body>
<p>
<a href="data/AquoThon.html">Aquo-Thon</a> |
<a href="data/AquoSwing.html">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html">Aquo Extreme Ski</a>
</p>

<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the example above.)

to use an HTML Panel widget to load event data directly into the element with the id of "event":

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../widgets/htmlpanel/SpryHTMLPanel.js" type="text/javascript"></script>
</head>
<body>
<p>
<a href="data/AquoThon.html" onclick="hp.loadContent(this.href); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="hp.loadContent(this.href); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="hp.loadContent(this.href); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="hp.loadContent(this.href); return false;">Aquo Extreme Ski</a>
</p>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
<script type="text/javascript">
var hp = new Spry.Widget.HTMLPanel("event");

hp.addObserver(function(notificationType, notifier, data)
{
	// We are about to load some content for the panel, make sure we modify
	// the URL so that the server knows we want a fragment.

	if (notificationType == "onPreLoad")
		data.url = data.url.replace(/\.html/, "Frag.html");
});
</script>
</body>
</html>

(Click here to view the example above.)

In the example above, an HTML Panel widget is created and bound to the element that has an id of "event". An observer function is then added to the HTML Panel so that it gets called anytime the HTML Panel is told to load a URL. This observer function is used to automatically modify the URL in some way to let the server know that we want a fragment instead of a full HTML page.

hp.addObserver(function(notificationType, notifier, data)
{
	// We are about to load some content for the panel, make sure we modify
	// the URL so that the server knows we want a fragment.

	if (notificationType == "onPreLoad")
		data.url = data.url.replace(/\.html/, "Frag.html");
});

In this particular case, we are modifying the URL so that it loads a file that is named the same as the one in the URL, except it ends with Frag.html, but you can imagine that in a real-world scenario we might do something like add an extra query param to the url:

hp.addObserver(function(notificationType, notifier, data)
{
	// We are about to load some content for the panel, make sure we modify
	// the URL so that the server knows we want a fragment.

	if (notificationType == "onPreLoad")
		data.url = data.url + "?frag=true";
});

Next, onclick attributes were added to the links on the page to override their default action. When JavaScript is enabled, and the user clicks on a link, the onclick code will call the loadContent() function on the HTML Panel we created, passing it the href for the link that was just clicked. This results in our observer function being invoked, which then transforms the URL into a request for an HTML fragment instead of an entire page. After the URL is transformed, the HTML Panel then loads the transformed URL. When the HTML Panel receives the fragment from the server, it automatically replaces the content underneath the "event" element, with the content in the fragment.

When running the example above, as you click the links, pay attention to the URL displayed by the browser. When JavaScript is on, the URL never changes because fragments of HTML are being loaded dynamically and replacing content on the page. When JavaScript is off, clicking on the links actually take you to a different page so the URL displayed by the browser changes.

The following example builds on the previous one to show the use of state content and observers:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<link href="../../../widgets/htmlpanel/SpryHTMLPanel.css" rel="stylesheet" type="text/css" />
<script src="../../../widgets/htmlpanel/SpryHTMLPanel.js" type="text/javascript"></script>
<script src="../../../includes/SpryEffects.js" type="text/javascript"></script>
</head>
<body>
<p>
<a href="data/AquoThon.html" onclick="hp.loadContent(this.href); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="hp.loadContent(this.href); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="hp.loadContent(this.href); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="hp.loadContent(this.href); return false;">Aquo Extreme Ski</a> |
<a href="data/non-existent-file.html" onclick="hp.loadContent(this.href); return false;">Non-Existent File</a>
</p>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
	<div class="HTMLPanelLoadingContent">Loading ...</div>
	<div class="HTMLPanelErrorContent">Failed to load content!</div>
</div>
<script type="text/javascript">
var hp = new Spry.Widget.HTMLPanel("event");

hp.addObserver(function(notificationType, notifier, data)
{
	if (notificationType == "onPreLoad")
	{
		// We are about to load some content for the panel, make sure we modify
		// the URL so that the server knows we want a fragment.

		data.url = data.url.replace(/\.html/, "Frag.html");
	}
	else if (notificationType == "onPreUpdate")
	{
		// We are about to replace the content of the panel with the
		// content we just received from the server. Set the background
		// of the panel to yellow to draw the user's attention to the panel.

		document.getElementById("event").style.backgroundColor = "#FF0";
	}
	else if (notificationType == "onPostUpdate")
	{
		// We are done replacing the content within the panel. Fire off
		// a Highlight effect to slowly change the panel's background
		// from yellow, back to white.

		Spry.Effect.DoHighlight("event", { to: "#FFF", duration: 1000 });
	}
});
</script>
</body>
</html>

(Click here to view the example above.)

With the example above, anytime the user clicks on a link, the HTML Panel will clear its content area and display the string "Loading ...". Once the fragment is received from the server, the HTML Panel will insert the fragment into the panel and then trigger a Yellow Fade Technique (YFT) effect to draw the user's attention to the panel. Should an error occur while loading the HTML fragment, the HTML Panel will automatically show the string "Failed to load content!". An extra link to a non-existent file was added to the example to demonstrate the failure to load content.

Note that because the actual fragments being loaded by the panel are small, some browsers may actually load the fragment and update the panel contents so fast that you don't actually see the "Loading ..." string appear.

To get the HTML Panel to automatically show the "Loading ..." and "Failed to load content!" content at the appropriate times, all that was necessary was to add the content and tag each one with either the "HTMLPanelLoadingContent" or "HTMLPanelErrorContent" class and include the SpryHTMLPanel.css file.

To implement the YFT effect, the observer we added in the previous example was modified to listen for onPreUpdate and onPostUpdate notifications. On the onPreUpdate notification, it set the background color of the HTML Panel to yellow, and on the onPostUpdate notification, it simply triggers a highlight effect which gradually sets the background-color of the HTML Panel back to white.

As with the previous example, if JavaScript is turned off, clicking on any of the links simply take you to another page to show you the appropriate content.

This last example demonstrates the HTML Panel's ability to extract a fragment out of an existing HTML page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<link href="../../../widgets/htmlpanel/SpryHTMLPanel.css" rel="stylesheet" type="text/css" />
<script src="../../../widgets/htmlpanel/SpryHTMLPanel.js" type="text/javascript"></script>
<script src="../../../includes/SpryEffects.js" type="text/javascript"></script>
</head>
<body>
<p>
<a href="data/AquoThon.html" onclick="hp.loadContent(this.href, { id: 'event' }); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="hp.loadContent(this.href, { id: 'event' }); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="hp.loadContent(this.href, { id: 'event' }); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="hp.loadContent(this.href, { id: 'event' }); return false;">Aquo Extreme Ski</a> |
<a href="data/non-existent-file.html" onclick="hp.loadContent(this.href, { id: 'event' }); return false;">Non-Existent File</a>
</p>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
	<div class="HTMLPanelLoadingContent">Loading ...</div>
	<div class="HTMLPanelErrorContent">Failed to load content!</div>
</div>
<script type="text/javascript">
var hp = new Spry.Widget.HTMLPanel("event");

hp.addObserver(function(notificationType, notifier, data)
{
	if (notificationType == "onPreUpdate")
	{
		// We are about to replace the content of the panel with the
		// content we just received from the server. Set the background
		// of the panel to yellow to draw the user's attention to the panel.

		document.getElementById("event").style.backgroundColor = "#FF0";
	}
	else if (notificationType == "onPostUpdate")
	{
		// We are done replacing the content within the panel. Fire off
		// a Highlight effect to slowly change the panel's background
		// from yellow, back to white.

		Spry.Effect.DoHighlight("event", { to: "#FFF", duration: 1000 });
	}
});
</script>
</body>
</html>

(Click here to view the example above.)

In the example above, we made 2 small tweaks to the previous example. First, for each of the links in our page, we modified the loadContent() function call so that we passed in an option object, that contained an id property, that told the panel that we want to extract a fragment out of the content it is about to load with the given URL. The last change was the removal of the code that was listening for onPreLoad messages within our HTML Panel observer. Since the HTML Panel is now loading full pages, there is no need to modify the URL, we'll let the HTML Panel load the href in the link and pull out the fragment we need from it.

With JavaScript on, clicking on a link causes the HTML Panel to load the page referred to by the links href. After it loads the page, it searches through the page for an element with the specified id. If it finds one, it then extracts the content inside that element, and then inserts it into the panel.

When JavaScript is turned off, clicking on a link causes the browser to follow the link.

Using Data Sets without Regions

Click to showhide the content for this section.

Some developers like the idea of using a data set as a mechanism for automatically loading and parsing data, but don't like the idea of using regions because it uses name spaced attributes and/or requires the embedding of the actual region template markup within the page.

It is possible to use data sets to load data and/or HTML fragments for selective page updates, without the use of regions, with a tiny bit of code that inserts the data/HTML fragments into the document manually.

Because data sets require the use of a transport format (XML, JSON, etc), in a progressive enhancement scenario this approach would require the duplication of your HTML content. That is the HTML content would appear in the original static page, and embedded within the XML, or other transport format used by the data set. This approach would make the most sense when you're taking a server side approach where you have a server-side application that is able to serve up the same content within full pages or as fragments.

What's particularly nice about using data sets, is it allows the developer to define what delivery format (XML, JSON, CSV, etc) to use, *and* the structure of the data within that format. For example, the developer could define the data format so that each time the URL on the data set is changed, the server sends back a single set of fragments that contain information on what parts of the page to update:

<?xml version="1.0" encoding="utf-8"?>
<update>
	<header>Aquo-Thon</header>
	<content><![CDATA[	
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	]]>
	</content>
</update>

In the sample XML above, the developer has designed things so that the names of the tags underneath the <update> node actually correspond to the id of an element within the HTML page to update. So in the sample above the child content of the element with the id of "header" would be replaced with the word "Aquo-Thon", likewise, the child content of the element with the id of "content" would be replaced with the HTML markup that gave information about the "Aquo-Thon" event. What's great about this particular scheme, is that it allows the server to decide what portions of the page need to be updated for a particular request.

Alternatively, the developer could define a data format where a group of page updates were sent with a single request (batched) to minimize the number of requests made to the server:

<?xml version="1.0" encoding="utf-8"?>
<updates>
	<update>
		<header>Aquo-Thon</header>
		<content><![CDATA[	
			<div class="location">Whistler, British Columbia</div>
			<div class="date">May 26th</div>
			<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
			<a href="#">directions...</a>
		]]>
		</content>
	</update>
	<update>
		<header>Aquo Swing</header>
		<content><![CDATA[	
			<div class="location">Sacramento, CA</div>
			<div class="date">June 10th</div>
			<p class="description">Watch these uber talented couples swing the night away at Sacramento's most famous dance hall.</p>
			<a href="#">directions...</a>
		]]>
		</content>
	</update>
	<update>
		<header>Aquo Ultra Marathon</header>
		<content><![CDATA[	
			<div class="location">Whistler, British Columbia</div>
			<div class="date">June 22nd</div>
			<p class="description">Get ready to admire the amateur runners pace themselves for the annual Aquo Ultra Marathon.</p>
			<a href="#">directions...</a>
		]]>
		</content>
	</update>
	<update>
		<header>Aquo Extreme Ski</header>
		<content><![CDATA[	
			<div class="location">Jackson Hole, WY</div>
			<div class="date">December 17th</div>
			<p class="description">Extreme skiing has never been so intense. Join thousands of spectators and watch the industry's most daring extreme skiers take on the mountain.</p>
			<a href="#">directions...</a>
		]]>
		</content>
	</update>
</updates>

The next 2 examples show how a developer can progressively enhance the following markup, to consume the 2 data formats above:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
</head>
<body>
<p>
<a href="data/AquoThon.html">Aquo-Thon</a> |
<a href="data/AquoSwing.html">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html">Aquo Extreme Ski</a>
</p>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the sample above.)

In the scenario where the server sends back a single set of page updates, we could progressively enhance the markup in the following manner:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/xpath.js" type="text/javascript"></script>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script type="text/javascript">
<!--

var dsUpdate = new Spry.Data.XMLDataSet(null, "//update", { entityEncodeStrings: false });

function dsUpdateObserver(notificationType, ds, data)
{
	if (notificationType == "onDataChanged" || notificationType == "onCurrentRowChanged")
	{
		var row = ds.getCurrentRow();
		if (row)
		{	
			for (var columnName in row)
			{
				if (columnName && !columnName.match(/^ds_/))
				{
					var element = document.getElementById(columnName);
					if (element)
						Spry.Utils.setInnerHTML(element, row[columnName]);
				}
			}
		}
	}
}

dsUpdate.addObserver(dsUpdateObserver);

function LoadURL(url)
{
	url = url.replace(".html", ".xml");
	dsUpdate.setURL(url);
	dsUpdate.loadData();
}

-->
</script>
</head>
<body>
<p>
<a href="data/AquoThon.html" onclick="LoadURL(this.href); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="LoadURL(this.href); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="LoadURL(this.href); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="LoadURL(this.href); return false;">Aquo Extreme Ski</a>
</p>
<div id="event">
	<h2 id="header">Aquo-Thon</h2>
	<div id="content">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the example above.)

In the example above, an observer function called dsUpdateObserver() is placed on the dsUpdate data set. Any time the data changes in the data set, the dsUpdateObserver() function will be triggered. Since the tag names of our XML data are the ids of elements on our page, the dsUpdateObserver function simply enumerates the column names (aka. xml tag names) of the data set, it then finds the corresponding element on the page that has an id equivalent to a given column name. If an element is found, it inserts the content in that column underneath the element.

The links on the page are enhanced with onclick attributes that override the default link behavior. Anytime the user clicks on a link, the LoadURL() function is called with the value of the link's href attribute. The LoadURL() function then has a chance to modify the URL in some way to tell the server that we want fragments. In this particular example we swap the .html extension of the URL with .xml, but you could easily imagine that if your pages were coming from a server side script that you could also append query params to the URL that told the server side script to serve up frags dynamically. After modifying the URL, the LoadURL() function simply tells the dsUpdate data set to load it. When the dsUpdate data set receives the data from the server, the dsUpdateObserver() function is automatically triggered, and the page gets updated.

To support the scenario where the server sends back a set of page updates, a slight tweak to the previous example needs to be made:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/xpath.js" type="text/javascript"></script>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script type="text/javascript">
<!--

var dsUpdate = new Spry.Data.XMLDataSet("data/event_updates.xml", "//update", { entityEncodeStrings: false });
dsUpdate.loadData();

function dsUpdateObserver(notificationType, ds, data)
{
	if (notificationType == "onDataChanged" || notificationType == "onCurrentRowChanged")
	{
		var row = ds.getCurrentRow();
		if (row)
		{	
			for (var columnName in row)
			{
				if (columnName && !columnName.match(/^ds_/))
				{
					var element = document.getElementById(columnName);
					if (element)
						Spry.Utils.setInnerHTML(element, row[columnName]);
				}
			}
		}
	}
}

dsUpdate.addObserver(dsUpdateObserver);

-->
</script>
</head>

<body>
<p>
<a href="data/AquoThon.html" onclick="dsUpdate.setCurrentRowNumber(0); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="dsUpdate.setCurrentRowNumber(1); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="dsUpdate.setCurrentRowNumber(2); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="dsUpdate.setCurrentRowNumber(3); return false;">Aquo Extreme Ski</a>
</p>

<div id="event">
	<h2 id="header">Aquo-Thon</h2>
	<div id="content">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to view the example above.)

In the example above, a URL is specified at the time the dsUpdate data set is created. This allows the page to pre-load multiple sets of updates ahead of time so when the user clicks on a link on the page, the updates can happen instantly without hitting the server again. As with the previous example, onclick attributes are added to the links to override the default behavior, but this time, the links simply set the current row of the data set which triggers dsUpdateObserver() to update the parts of the page that have data specified in the new current row of the data set.

Using the spry:content Attribute with Regions

Click to showhide the content for this section.

The spry:content attribute is a processing instruction for the Spry dynamic-region processing code, which tells it to replace the static data, currently contained underneath that element, with the data represented by the data reference in the spry:content attribute value, when there is data in the data set the region is bound to.

Using spry:content with regions is analogous to the method shown in the "Data Sets without Regions" section above, except that it leverages the existing region mechanism within Spry to automatically update elements with a spry:content attribute on them.

Because regions rely on data sets, and data sets require the use of a transport format (XML, JSON, etc), in a progressive enhancement scenario this approach would require the duplication of your HTML content. That is the HTML content would appear in the original static page, and embedded within the XML, or other transport format used by the data set. This approach would make the most sense when you're taking a server side approach where you have a server-side application that is able to serve up the same content within full pages or as fragments.

The following examples will progressively enhance this basic markup:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
</head>
<body>
<p>
<a href="data/AquoThon.html">Aquo-Thon</a> |
<a href="data/AquoSwing.html">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html">Aquo Extreme Ski</a>
</p>
<div id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

There are actually 2 ways to use the spry:content attribute with a Spry region. The first is to place the attribute on the same element that contains the spry:region or spry:detailregion attribute:

<div spry:detailregion="dsEvents" spry:content="{event}" id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>

When used in this manner, all of the static content underneath the region element will be replaced with dynamic content from the "event" column of the data set the region is bound to, whenever new data is loaded, or when the current row of the data set changes. In this particular example, the data in the "event" column can be fully formatted HTML content:

<?xml version="1.0" encoding="utf-8"?>
<events>
	<event><![CDATA[
		<h2 class="name">Aquo-Thon</h2>
		<div class="info">		
			<div class="location">Whistler, British Columbia</div>
			<div class="date">May 26th</div>
			<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
			<a href="#">directions...</a>
		</div>
	]]></event>

	<event><![CDATA[
		<h2 class="name">Aquo Swing</h2>
		<div class="info">		
			<div class="location">Sacramento, CA</div>
			<div class="date">June 10th</div>
			<p class="description">Watch these uber talented couples swing the night away at Sacramento's most famous dance hall.</p>
			<a href="#">directions...</a>
		</div>
	]]></event>

	<event><![CDATA[	
		<h2 class="name">Aquo Ultra Marathon</h2>
		<div class="info">		
			<div class="location">Whistler, British Columbia</div>
			<div class="date">June 22nd</div>
			<p class="description">Get ready to admire the amateur runners pace themselves for the annual Aquo Ultra Marathon.</p>
			<a href="#">directions...</a>
		</div>
		]]></event>

	<event><![CDATA[
		<h2 class="name">Aquo Extreme Ski</h2>
		<div class="info">		
			<div class="location">Jackson Hole, WY</div>
			<div class="date">December 17th</div>
			<p class="description">Extreme skiing has never been so intense. Join thousands of spectators and watch the industry's most daring extreme skiers take on the mountain.</p>
			<a href="#">directions...</a>
		</div>
	]]></event>

	<event><![CDATA[	
		<h2 class="name">Aquo Baja 400</h2>
		<div class="info">		
			<div class="location">San Filipe, Baja California</div>
			<div class="date">October 3rd</div>
			<p class="description">Come south of the border and watch the dirt fly. This Baja 400 promises to be the best yet, with all the top seeds being there.</p>
			<a href="#">directions...</a>
		</div>
	]]></event>

	<event><![CDATA[
		<h2 class="name">Big Wave Challenge</h2>
		<div class="info">		
			<div class="location">Mavericks, CA</div>
			<div class="date">November 8th</div>
			<p class="description">Watch the worlds best big wave surfers take on the legendary break at Mavericks.</p>
			<a href="#">directions...</a>
		</div>
		<h2 class="name">Aquo Classic</h2>
	]]></event>

	<event><![CDATA[
		<div class="info">		
			<div class="location">Carlsbad, CA</div>
			<div class="date">August 15th</div>
			<p class="description">Join us for the 10th annual Aquo Classic at Alvaro Pines in sunny Carlsbad California.</p>
			<a href="#">directions...</a>
		</div>
	]]></event>
</events>

Here's the full source for a progressively enhanced page that uses spry:content in this manner:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/xpath.js" type="text/javascript"></script>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script type="text/javascript">
var dsEvents = new Spry.Data.XMLDataSet("events_formatted.xml", "/events/event");
dsEvents.setColumnType("event", "html");
</script>
</head>
<body>
<p>
<a href="data/AquoThon.html" onclick="dsEvents.setCurrentRowNumber(0); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="dsEvents.setCurrentRowNumber(1); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="dsEvents.setCurrentRowNumber(2); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="dsEvents.setCurrentRowNumber(3); return false;">Aquo Extreme Ski</a>
</p>
<div spry:detailregion="dsEvents" spry:content="{event}" id="event">
	<h2 class="name">Aquo-Thon</h2>
	<div class="info">		
		<div class="location">Whistler, British Columbia</div>
		<div class="date">May 26th</div>
		<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<a href="#">directions...</a>
	</div>
</div>
</body>
</html>

(Click here to see a working example.)

In the example above, an XMLDataSet is used to preload several sets of updates. After the data set loads all regions that are bound to that data set are told to re-generate their content. Because our detail region has a spry:content attribute on the region container, all of the content of the region will be replaced with whatever value is in the "event" column of the data set, which in this particular example, is HTML markup.

The links on the page were modified with onclick attributes to override the default link behavior. When a user clicks on a link the setCurrentRowNumber() is called on the data set to change its current row. This triggers the detail region to update, and the contents of the "event" column for the current row to be inserted into the region container.

In this particular example, the order of the updates embedded in the XML is known so it was convenient to use of setCurrentRow() to trigger the correct update, but you can also set the current row based on the value of a particular column, or you could even trigger the data set to load new content.

Make sure you run the example with JavaScript on and off, to see how the behavior of the page changes, but the content is still accessible. With JavaScript on, clicking on the links changes only the region containing the event content, and you remain on the same page. With JavaScript off, clicking on the links take you to different pages that have the same content.

The 2nd way to use spry:content, is within a spry:region or spry:detailregion:

<div spry:detailregion="dsEvents" class="event">
	<h2 spry:content="{name}" class="name">Aquo-Thon</h2>
	<div class="info">
		<div spry:content="{location}" class="location">Whistler, British Columbia</div>
		<div spry:content="{date}" class="date">May 26th</div>
		<p spry:content="{description}" class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<span spry:content="{directions}"><a href="#">directions...</a></span>
	</div>
</div>

You can use spry:content in this manner if you want to let the markup for the static content you start out with, dictate how your data will be formatted. So for example, if you had XML data for each event that was delivered in this format:

<?xml version="1.0" encoding="utf-8"?>
<events>
		<event>
			<photo path="i/photo_events_aquothon.jpg" desc="Aquo-Thon" />
			<name>Aquo-Thon</name>
			<location>Whistler, British Columbia</location>
			<date>May 26th</date>
			<description>Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</description>
			<directions url="#"><![CDATA[<a href="#">directions...</a>]]></directions>
		</event>

		<event>
			<photo path="i/photo_events_swingsacramento.jpg" desc="Aquo Swing" />
			<name>Aquo Swing</name>
			<location>Sacramento, CA</location>
			<date>June 10th</date>
			<description>Watch these uber talented couples swing the night away at Sacramento's most famous dance hall.</description>
			<directions url="#"><![CDATA[<a href="#">directions...</a>]]></directions>
		</event>

		<event>
			<photo path="i/photo_events_ultramarathon.jpg" desc="Aquo Ultra Marathon" />
			<name>Aquo Ultra Marathon</name>
			<location>Whistler, British Columbia</location>
			<date>June 22nd</date>
			<description>Get ready to admire the amateur runners pace themselves for the annual Aquo Ultra Marathon.</description>
			<directions url="#"><![CDATA[<a href="#">directions...</a>]]></directions>
		</event>

		<event>
			<photo path="i/photo_events_extremeski.jpg" desc="Aquo Extreme Ski" />
			<name>Aquo Extreme Ski</name>
			<location>Jackson Hole, WY</location>
			<date>December 17th</date>
			<description>Extreme skiing has never been so intense. Join thousands of spectators and watch the industry's most daring extreme skiers take on the mountain.</description>
			<directions url="#"><![CDATA[<a href="#">directions...</a>]]></directions>
		</event>
</events>

It would automatically be "poured" into the markup used by the original static content. Here's the full source for a progressively enhanced page that uses spry:content in this manner:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Aquo Events</title>
<script src="../../../includes/xpath.js" type="text/javascript"></script>
<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script type="text/javascript">
var dsEvents = new Spry.Data.XMLDataSet("events.xml", "/events/event");
dsEvents.setColumnType("directions", "html");
</script>
</head>

<body>
<p>
<a href="data/AquoThon.html" onclick="dsEvents.setCurrentRowNumber(0); return false;">Aquo-Thon</a> |
<a href="data/AquoSwing.html" onclick="dsEvents.setCurrentRowNumber(1); return false;">Aquo Swing</a> |
<a href="data/AquoUltraMarathon.html" onclick="dsEvents.setCurrentRowNumber(2); return false;">Aquo Ultra Marathon</a> |
<a href="data/AquoExtremeSki.html" onclick="dsEvents.setCurrentRowNumber(3); return false;">Aquo Extreme Ski</a>
</p>

<div spry:detailregion="dsEvents" class="event">
	<h2 spry:content="{name}" class="name">Aquo-Thon</h2>
	<div class="info">	
		<div spry:content="{location}" class="location">Whistler, British Columbia</div>
		<div spry:content="{date}" class="date">May 26th</div>
		<p spry:content="{description}" class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
		<span spry:content="{directions}"><a href="#">directions...</a></span>
	</div>
</div>
</body>
</html>

(Click here to see a working example.)

In this example, clicking on a link causes the Spry region to replace the data references within the static markup with the appropriate data. When JavaScript is turned off the clicking on the links take you to pages with the appropriate content.

Using the HTML Data Set with Regions

Click to showhide the content for this section.

The HTML Data Set allows developers to use content within an HTML document or fragment, as a data source. This content can be embedded within your page, or retrieved from an external data source (file, web app, etc). To read more about the HTML Data Set, checkout the HTML Data Set Overview and samples.

Unlike other data sets, in a progressive enhancement scenario there is no duplication of HTML content because the HTML Data Set extracts its data directly from HTML documents or fragments. This means that the HTML Data Set can work with static HTML files, without the need for a server side application.

In a progressive enhancement scenario, the HTML Data Set can be used to present static content in a whole new way. For example, we can take a page with static content that looks something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Aquo Energy Drink - Events</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<div id="container">		
	<div id="header">	
	...
	</div><!-- close header -->	
	
	<div id="title"></div>	
	
	<div id="staticcontent">

		<div class="event">
			<div class="eventimage"><img src="i/photo_events_aquothon.jpg" alt="Aquo-Thon" /></div>
			<div class="eventinfo">		
				<h2 class="name">Aquo-Thon</h2>
				<div class="location">Whistler, British Columbia</div>
				<div class="date">May 26th</div>
				<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
				<a href="#">directions...</a>
			</div>
		</div>
		<div class="divider"></div>

		<div class="event">
			<div class="eventimage">
				<img src="i/photo_events_swingsacramento.jpg" alt="Aquo Swing" />
			</div>
			<div class="eventinfo">
				<h2 class="name">Aquo Swing</h2>
				<div class="location">Sacramento, CA</div>
				<div class="date">June 10th</div>
				<p class="description">Watch these uber talented couples swing the night away at Sacramento's most famous dance hall.</p>
				<a href="#">directions...</a>
			</div>
		</div>
		<div class="divider"></div>

		...

	</div>
	<!-- close staticcontent -->

	<div id="border_bottom"></div><!-- close border_bottom  -->
	
	<div id="footer">
	...
	</div><!-- close footer -->

</div><!-- close container -->
</body>
</html>

(Click here to view the example above and see the full source.)

then transform it into something completely different by progressively adding an HTMLDataSet and a couple of dynamic regions:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<title>Aquo Energy Drink - Events</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link type="text/css" rel="stylesheet" href="css/global.css" media="screen" />
<link type="text/css" rel="stylesheet" href="css/events.css" media="screen" />
<link type="text/css" rel="stylesheet" href="css/basic.css" media="screen" />

<script src="../../../includes/SpryData.js" type="text/javascript"></script>
<script src="../../../includes/SpryHTMLDataSet.js" type="text/javascript"></script>
<script type="text/javascript">
<!--
var dsEvents = new Spry.Data.HTMLDataSet(null, "staticcontent", {firstRowAsHeaders:false, rowSelector:".event", dataSelector:".eventimage, h2, .location, .date, .description", columnNames:["eventimage","name","location","displaydate","description"]});
//-->
</script>
</head>
<body>
<div id="container">		
	<div id="header">	
	...
	</div><!-- close header -->	
	
	<div id="title"></div>	
	
	<div id="staticcontent">

		<div class="event">
			<div class="eventimage"><img src="i/photo_events_aquothon.jpg" alt="Aquo-Thon" /></div>
			<div class="eventinfo">		
				<h2 class="name">Aquo-Thon</h2>
				<div class="location">Whistler, British Columbia</div>
				<div class="date">May 26th</div>
				<p class="description">Join us in Whistler as Extreme Mountain bikers take on the hills and curves of British Columbia.</p>
				<a href="#">directions...</a>
			</div>
		</div>
		<div class="divider"></div>

		<div class="event">
			<div class="eventimage">
				<img src="i/photo_events_swingsacramento.jpg" alt="Aquo Swing" />
			</div>
			<div class="eventinfo">
				<h2 class="name">Aquo Swing</h2>
				<div class="location">Sacramento, CA</div>
				<div class="date">June 10th</div>
				<p class="description">Watch these uber talented couples swing the night away at Sacramento's most famous dance hall.</p>
				<a href="#">directions...</a>
			</div>
		</div>
		<div class="divider"></div>

		...

	</div>
	<!-- close staticcontent -->
	
	<div id="content">
		<div id="full-column">
			<h1 id="events_selectanevent">Select an Event</h1>

			<div id="content1" spry:region="dsEvents">
				<table id="events">
					<tr>
						<th id="eventheader" spry:sort="date">Event</th>
						<th id="locationheader" spry:sort="location">Location</th>
					</tr>
					<tr class="eventrow" spry:repeat="dsEvents" spry:hover="rowHover" spry:select="rowSelected" spry:setrow="dsEvents">
						<td>
							<span class="eventname">{name}</span><br />
							<span class="date">{displaydate}</span>
						</td>
						<td>{location}</td>
					</tr>
				</table>
			</div>

			<div id="sidebar">
				<div id="sidebar_brace" spry:detailregion="dsEvents">
					<div id="eventbanner">{eventimage}</div>
					<div id="eventdesc">
						<h3>Event Information</h3>
						<div>{description}</div>
					</div>
				</div>
			</div>
		</div><!-- close full_column -->
	</div><!-- close content -->

	<div id="border_bottom"></div><!-- close border_bottom  -->
	
	<div id="footer">
	...
	</div><!-- close footer -->

</div><!-- close container -->

<script type="text/javascript">
<!--
Spry.$("content").style.display = "block";
//-->
</script>

</body>
</html>

(Click here to view the example above and see the full source.)

In the example above, an HTML Data Set is used to extract data from the <div> with the "staticContent" id within the page. Some markup containing regions was added at the bottom of the page. This markup starts off as display:none so that it doesn't show up if JavaScript is turned off, and is made visible by the <script> block that was added at the bottom of the file if JavaScript is turned on. By default, once the data set finishes loading its data, it automatically sets its HTML data source to display:none, in effect causing the original static content to not display. It then notifies all regions it is bound to that it should re-generate its content. As the regions are re-generated, content from the data set is poured into elements that have the spry:content attribute on them.


Copyright © 2007. Adobe Systems Incorporated. All rights reserved.