Welcome!

ColdFusion Authors: Yakov Fain, Pat Romanski, Liz McMillan, Maureen O'Gara, Greg Ness

Related Topics: ColdFusion

ColdFusion: Article

Creating Custom ColdFusion tags as DHTML Wrappers

Creating Custom ColdFusion tags as DHTML Wrappers

On the one hand DHTML — the combination of HTML, stylesheets and JavaScript — has given us many new options to create low-bandwidth effects to enhance Web pages.

And on the other ColdFusion has given us the power to bring database contents to the Web. What's best for us, then, if we want interactive, site-enhancing effects that are data-driven? A combination of the two, of course! But it's never quite that simple....

This is especially true since DHTML has been implemented differently in Netscape and Microsoft browsers. Pages are often cluttered with a combination of ColdFusion output and lines of script that are hard to debug, modify and reuse. The good news is that ColdFusion's custom tag creation abilities allow us to transform much of the complex client-side code into clean and reusable tags.

Ideal Marriage

To show how to create a ColdFusion custom tag DHTML wrapper, I'm going to focus on the example of our old friend, the ever-popular news-scroller widget.

The concept is simple: we want to display brief news headlines hyperlinked to full articles in a compact space on the page. This type of widget is ideally suited to a marriage between DHTML and ColdFusion. A database holds information on what headlines to display and their links, and when to show them. ColdFusion is great at retrieving the items from the database and bringing them to the browser, but we need DHTML to control the showing and hiding of each one so as to create the "scrolling" effect.

The steps we take to turn this particular DHTML effect into a custom tag can be used with many other effects as well — all that's required is some careful planning.

    There are two primary elements of the scroller:

  • CSS layers, one for each news item, layered atop each other at the same window position
  • Script to control the timed showing and hiding of each layer

The four steps to build the scroller are:

    1. Query the database for items to display.
    2. Create a layer for each item.
    3. Write a JavaScript function that will show and hide a given layer.
    4. Write a timer with JavaScript to call the show-and-hide function every few seconds.

Much of this is the same regardless of the implementation of the scroller, and that's our key to reusability. Any code that remains the same from one implementation to the next can be wrapped up. The tag that will ultimately be placed on our page needs to be concerned only with the variable elements. The headlines and their accompanying URLs, the position and size of the scroller, display elements such as font face and colors — everything else can be wrapped up in our tag.

As with other DHTML effects, the key is to separate the unique elements from the ones that are repeated. A navigation widget has unique pages and URLs but uses the same code to display them each time. Popup tool tips consist of different text each time but are always revealed with the same script. The unique elements will be parameters of the custom tag and the repeated pieces get wrapped up.

For our news-scroller, a syntax for the custom tag becomes clear — a main tag instantiating the scroller and child tags for each item. Something like:

<cf_NewsScroll with style and positioning parameters>
<cf_NewsItem TheItem="_" TheURL="_">
<cf_NewsItem TheItem="_" TheURL="_">
(_)
</cf_ NewsScroll>

Each time the page loads we want the ticker items built from the results of a database query. This is the key to making our dynamic HTML really dynamic. Thus:







<cfquery name="Headlines" datasource="NewsDatabase">
SELECT Headline, URL
FROM NewsTabe
</cfquery>
<cf_NewsScroll with style and positioning parameters>
<cfoutput query="Headlines">
<cf_NewsItem TheItem="#Headline#" TheURL="#URL#">
</cfoutput>
</cf_NewsScroll>

That is to say, one child tag for each item returned from the database. For the ColdFusion developer who'll use this tag, the work is done (see Listing 3). With this simple syntax we can pass all the information necessary to construct the DHTML effect to another file for the dirty work.

The Dirty Work

The way all the elements come together to create our DHTML effect is a combination of ColdFusion custom tag development and cross-browser DHTML development. Either topic on its own would provide the basis for a lengthy article, so I won't deal with the specifics here except to show how to remove the code from the developer as we look into how to wrap up the news-scroller into a custom tag.

We don't need complex parent—child architectured effects to justify creating wrappers. Simple, frequently used JavaScript snippets could be wrapped up into a tag taking only one or two variable parameters. Again, the idea is to hide the repetitive code.

Getting back to the example of our scroller, this does require a slightly more complex architecture. Since we never know in advance how many headlines will be displayed at runtime, the tag for this wrapper is really a tag family. There's a variable number of child tags, one for each headline and one main tag that pulls them all together and writes the layers and scripts. We'll need a separate cfm file for each: a parent tag called NewsScroll.cfm and an item template called NewsItem.cfm. In the calling page they'll be invoked with the syntax we used above:

<CF_NewsScroll>
<CF_NewsItem>
<CF_NewsItem>
(_)
</CF_NewsScroll>

The item template NewsItem.cfm consists of a single <CFASSOCIATE> tag (see Listing 1). Its sole purpose is to make its values — in our case each individual headline and its URL — visible to the parent tag via the AssocAttribs array. The length of this array will be crucial to the wrapper since it specifies the number of news items to be displayed.

The parent tag NewsScroll.cfm is where the real work is done. Since there's a start and end tag used on our calling page, NewsScroll.cfm will have two periods of activity identified as ExecutionMode. The Start mode is when the tag is first called and the End mode is when the close tag is called. We can't really build the DHTML until we've read in all of the headlines, so most of the processing will be in the end ExecutionMode.

<CFIF ThisTag.ExecutionMode IS "END">
<!---Do the work here_-à
</CFIF>

Parameters of CF_NewsScroll on the calling page can describe where and how to display the scroller: fonts, colors, screen coordinates. In NewsScroll.cfm we read in these parameters with the Attributes type and use defaults if no values are provided for the optional items.





Required attribute...
<CFSET TheHeight=#Attributes.Height#>

Optional attribute...
<CFIF ParameterExists(Attributes.FontFace)>
<CFSET TheFontFace=#Attributes.FontFace#>
<cfelse>
<CFSET TheFontFace="Arial,Helvetica">
</CFIF>

Now that we know where the scroller will be and how it will look, we're ready to begin creating the containers for each news item. Much of this work is done once for each news item, so start by setting a variable to this number. Again, the number of items is equal to the length of the AssocAttribs array.

<CFSET NumberOfNewsItems = #ArrayLen(ThisTag.AssocAttribs)#>

Since later we'll be creating for... loops that start with zero, let's set a variable that's one less than the number of news items. (You'll see where we use this shortly!)

<CFSET ForNumber = NumberOfNewsItems - 1>

Each new item will be in its own container, here an HTML

tag, with the container's properties defined by a style. We dynamically create each DIV and style with JavaScript's document.write command. See the complete code in Listing 2.

A style for each DIV...

document.write('<STYLE TYPE="text/css">\n')
<!--- Loop through items --->
<CFLOOP INDEX="i" FROM="0" TO="#ForNumber#">
document.write('#NewsScrollItem<cfoutput>#i#</cfoutput>{
// Use additional document.write commands for properties
document.write('}\n')
</CFLOOP>
document.write('</STYLE>\n')

will make something looking like this on the calling page:

<STYLE TYPE="text/css">
#NewsScrollItem1{
property: value;}
#NewsScrollItem2{
property: value;}
</STYLE>

And each DIV itself...

<CFLOOP INDEX="i" FROM="1" TO="#NumberOfNewsItems#"> document.write('<DIV id="NewsScrollItem<cfoutput>#NumOfLayer#</cfoutput>">') //Write the HTML for the item and its link here_ document.write('</DIV>') </CFLOOP>

will result in something like this:

<DIV id="NewsScrollItem1"> A Big News Headline!

At this point we have one hidden container for each news item. All that remains is to create some more script to show and hide the containers on a timer. For our effect to be cross-browser compatible, we need to do some syntactical workarounds. See Listing 2 for how it's done.

As I said before, a full look into cross-browser DHTML is beyond the scope of this article, so try to take the code lightly. (If you're not familiar with DHTML, one look at this block alone ought to be sufficient argument for creating these wrappers as it's precisely this complexity that we're hiding!)

The last element is to set a timer for the show/hide function. The time between rotations is specified in milliseconds; in Listing 2 the value is 3,000 (three seconds). This too could well be a parameter of the tag to allow the developer to control the speed of the scroll.

If we didn't create this scroller as a custom tag we'd need to write this script on every page. By wrapping it up, our effect is achieved with the simple syntax we specified above:

<CF_NewsScroll with style and positioning parameters>
<CF_NewsItem TheItem="_" TheURL="_">
<CF_NewsItem TheItem="_" TheURL="_">
(_)
</CF_NewsScroll>

The complexities are concealed, and by adding the tag to your ColdFusion custom tags directory you enable the scroller to be called from within any of your ColdFusion pages.

More Stories By Tim Buntel

Tim Buntel is product manager for ColdFusion at Macromedia. His Web site is at www.buntel.com.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.