Spry's HTML Panel
Over the past few weeks, I've been taking a look at Spry's widgets, specifically those related to form items. I've been surprised by what I've found. I really wish I would have looked at them earlier. Today I looked at another widget, the HTML Panel. This one isn't form related, but is darn cool.
The HTML Panel is not a "panel" like what you get in ColdFusion 8. You can think of the panel as simply an item on your web page that can be loaded with other content. For example, imagine this div:
<div id="content">
Stuff will load here.
</div>
Now imagine I want to load content into this div. By creating an HTML Panel widget out of the div, I can easily change the content. Let's look at a real example.
First off - like the other widgets, you need to use a CSS and JavaScript file:
<html>
<head>
<script src="/spryjs/SpryHTMLPanel.js" language="javascript" type="text/javascript"></script>
<link href="/sprycssSpryHTMLPanel.css" rel="stylesheet" type="text/css">
</head>
Next I'm going to create a menu. This is what I'll use to load content:
<h2>Products</h2>
<p>
<b>
<a onClick="panel.loadContent('apple.html'); return false">Apples</a> /
<a onClick="panel.loadContent('banana.html'); return false">Bananas</a> /
<a onClick="panel.loadContent('cherry.html'); return false">Cherries</a>
</b>
</p>
Don't worry about the JavaScript just yet. Now I'll create the area where content will load:
<div id="product">
<p>
Please select a product.
</p>
</div>
The last thing I'll do is enable the HTML panel with a line of JavaScript. This is like every other widget I've covered so far:
<script type="text/javascript">
var panel = new Spry.Widget.HTMLPanel("product");
</script>
</body>
</html>
I simply create a new instance of the HTMLPanel, and point it to the ID of the item that will be replaceable. Ok, so now if you go back to the JavaScript you can see what I use to load content:
<a onClick="panel.loadContent('apple.html'); return false">Apples</a>
I just use the loadContent function of the panel object. I point it to the HTML to load, and that's it! You can see a live example of this here. View source to see the complete example.
So far so good - and easy as well. But wait - it gets a lot sexier. One of the things Spry tries to help out with is a progressive enhancement. That is a fancy way of saying "support non-JavaScript" browsers. One of the options you can use when loading widgets is to supply an ID. Spry will load the remote content, but only display the stuff within the specified ID. Why is that sexy? Consider this new example (note, I trimmed a bit of the HTML):
<h2>Fragment Test</h2>
<p>
<b>
<a href="f1.html" onClick="panel.loadContent(this.href,{id:'content'}); return false">Test One</a> /
<a href="f2.html" onClick="panel.loadContent(this.href,{id:'content'}); return false">Test Two</a>
</b>
</p>
<div id="panel">
<p>
Please select something!
</p>
</div>
<script type="text/javascript">
var panel = new Spry.Widget.HTMLPanel("panel");
</script>
As with the previous example, I've got a menu on top with a section in the middle that will be dynamic, but let's focus on one of the links:
<a href="f1.html" onClick="panel.loadContent(this.href,{id:'content'}); return false">Test One</a>
Note that I have a normal href. Then notice my onclick. First off - the URL for the onclick refers to the same URL defined in the tag itself. A little fancy self-referring which is nice if you ever change the URL. The second argument passed to the loadContent function is an object with one key/value pair. The ID attribute simply means, "Load the remote URL, but just show the stuff inside the content id." Let's look at f1.html:
<html>
<head>
<title>F1</title>
</head>
<body>
<h2>F1</h2>
<div id="content">
<p>
This is the content for fragement page 1.
</p>
</div>
</body>
</html>
As you can see - only one div has the ID of content. Now think about it. With one link you have 2 possible things going on:
- If the user doesn't have JavaScript, it loads up f1.html, and they get the complete page.
- If the user does have JavaScript, the remote page is loaded, but only the content area is displayed.
So in one simple link you have support for both JS enabled browser and browsers that have JS turned off (or search engines). As I said - darn sexy! You can see this in action here, and I recommend testing it with JS on and off to see it working.
Now one thing you can may not like about this is that even with the JS-enabled clicks, you are loading all the HTML, but it should still be faster for the end user as the browser won't have to load layout UI and stuff like that - just the content it needs. You can get around this easily enough in ColdFusion of course. Have your non-JS link to foo.cfm, and your Spry link to foo.cfm?slim=1, where the existence of the URL parameter tells your layout code to suppress any output.
Lastly - be sure to check out the complete docs for the HTML panel:
Comments
also spry is bridging with Flex pretty well so we probably will see some integrations poping up soon .
The way I think of unobtrusive javascript is that it is separation of your markup from your application behavior, or your view from your model-controller. It is a good idea in our server side programming and it's also a good for client side programming.
All of your other Spry examples are done with unobtrusive javascript. If you'll notice, all the JS code was in one place and there was nothing but markup in your HTML section!
@Levan: Nested regions... do you mean nested data? If so - Spry supports that, and Spry _does_ validate now if I remember right.
http://labs.adobe.com/technologies/spry/samples/ht...
In particular, look at:
http://labs.adobe.com/technologies/spry/samples/ht...
That sample shows both the loading of pure HTML fragments, and the extraction of an HTML fragment from a static HTML page.
Unobtrusive JS is a great concept that is gaining popularity, but it does have some issues folks should be aware of:
- It sometimes makes it harder to figure out how things work, since all of the behavior is moved into code, so you can't see the relationship between the tag and the event/behavior in the same place.
- Depending on how you unobtrusively attach behaviors, and the number of elements you are attaching behaviors to, there may be a performance penalty you pay. If you have ID attributes on everything that you will attach behaviors to, it may not be that bad, but if you are using CSS class or contextual selectors (ala Spry's Element Selector, JQuery, DOMQuery, etc) there is a penalty for traversing the *entire* document to find those elements.
The first issue is something we wrestle with internally. Our samples tend to be "obtrusive" so the user reading our docs and looking at our samples can understand it right away ... but we're trending towards providing samples that do it both ways so folks can wrap their heads around how things work, and how to make it unobtrusive if they want/need to.
http://labs.adobe.com/technologies/spry/articles/b...
include:
* The ability to make incremental modifications to the HTML markup structure or the behavior code independently without having to modify the other.
* Because the behavior implementation is externalized, it can be shared across multiple HTML pages, so the bandwidth necessary to view these pages is reduced since the files related to the behaviors are downloaded and cached by the browser once. This also results in smaller HTML pages since the behavior code is not duplicated within the actual markup itself.
* Since the HTML markup is smaller and semantic, it is also easier to read which aids accessibility with screen readers, search engine web crawlers, and browsers or other user agents that don't necessarily support the behaviors you've implemented.
I use jquery a lot but craeting the content dinamically most of my js attaching event is inserted in the html template inside script tags....I do not think the purpose of a good ceveloper should be come back to hardcoding....
What I did not like of some js framework is the use of non valid attributes to attach event and I remember in the beginning spry was bad right under this point.
<a href="testform.cfm" onclick="panel.loadContent(this.href,{id:'panelform'}); return false">Click</a>
<div id="panel1">Please click</div>
<script type="text/javascript">
var panel = new Spry.Widget.HTMLPanel("panel1");
</script>
loads the div just fine:
<div id="panelform">
<form name="form" enctype="multipart/form-data" action="">
<span id="sprytextfield1">
<label>
<input type="text" name="text1" id="text1" />
</label>
<span class="textfieldRequiredMsg">A value is required.</span></span>
<input name="submit" type="submit" value="submit" />
</form>
<script type="text/javascript">
<!--
var sprytextfield1 = new Spry.Widget.ValidationTextField("sprytextfield1");
//-->
</script>
</div>
However, just like when it is placed inside a <cfdiv> spry validation is ignored (although it partially does work, since the <span class="textfieldRequiredMsg">A value is required.</span> does not display on load, meaning JS works initially.
So one reason your validation may not work is that you aren't including the CSS/JS. When you loaded the content, you said, 'just user stuff in id:panelform. Well that didn't include the CSS/JS for validation.
I did test file upload from a form inside cfdiv. It seems to work when it is not cfform. I have not tried uploading files from spry panels.
As for validation, I load both CSS and js for spry validation in the main template that contains the panel. When js file is missing, then the "value is required" span shows up on load, that suggests to me that js is loaded. and firebug lists the js file as well as CSS styles as available.
There is always a possibility I am doing something wrong or misspelling - I never underestimate my ability to chase wild geese while there is a cooked one sitting in the oven.
As a random though, in your JS block, before you make the widget, do this for me:
alert('hi')
When the content loads, do you see the alert? Just because you don't see the span doesn't mean that the JS is working - cuz the CSS file should hide it instead. (AFAIK)


Just my $0.02 worth :)