ColdFusion Quickie - Generating JavaScript from CFML

Have you ever wondered how you could create JavaScript data from ColdFusion? Now before you say anything, I'm not talking about AJAX. While using AJAX to load data into the client is probably the most often used way to do this, it certainly isn't the only way. Consider the following example.

Links

Just a quick recap of external blog posts. My last entry for this was back on April 26th.

CFTHREAD with a loading message

As yet another followup to my blog entry on CFTHREAD, a user asked about how to present a 'Please stand by' type message while the threads were running. This is fairly trivial with JavaScript and CFFLUSH:

New ADC Article: Using Dreamweaver, InContext Editing, and Spry to build a dynamic site

Just a quick note to say my latest Adobe Developer Center article just went live: Using Dreamweaver, InContext Editing, and Spry to build a dynamic site. Looks like I need to update the bio though. If you've never looked at, or even heard of, InContext Editing, definitely read the article. I was really pleasantly surprised by how cool it worked.

Simple CFCHART/jQuery Demo

I had some time to kill today and I decide to mix cfchart up with some jQuery love. You can see the demo of this here. When the chart loads, click on the bars, and you will see a detail load beneath the graph.

The code behind this is fairly trivial. I've got a file I include to generate my chart data. Normally this would be a proper database query. The main template's code consists of:

<cfinclude template="data.cfm">

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
function loadData(l) {
   $("#detail").load('detail.cfm?name='+escape(l)).hide().fadeIn('slow')
}
</script>

<cfchart chartWidth="500" chartHeight="250" format="flash" url="javascript:loadData('$ITEMLABEL$')">
   <cfchartseries type="bar" query="data" itemcolumn="name" valuecolumn="sales" />
</cfchart>

<div id="detail" style="width:500"></div>

The cfchart makes use of the url attribute to specify what should happen when you click. In this case, I'm simply calling a function, loadData(). This then uses jQuery to make a remote call to detail.cfm. Note that I pass the name. Normally you would pass a primary key, but we don't have (easy) access to that (see this entry for more info) value so we have to work with the name instead. That's it. All detail.cfm does is look up the detail information:

<cfinclude template="data.cfm">

<cfparam name="url.name" default="">

<!--- get detail based on label --->
<cfquery name="detail" dbtype="query">
select   *
from   data
where   name = <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.name#">
</cfquery>
   
<cfif detail.recordCount is 1>

   <cfoutput>
   <h2>#url.name#</h2>
   <p>
   Founded: #detail.yearfounded#<br/>
   Sales: #dollarFormat(detail.sales)#<br/>
   #paragraphFormat(detail.bio)#
   </cfoutput>

</cfif>

Not terribly useful I guess, but fun.

Simple example of a Form post to ColdFusion with jQuery

Following up on my earlier post demonstrating loading ColdFusion query data via jQuery, I've decided to do a few more simple jQuery+ColdFusion examples to give folks a taste of how easy it is to work with them both at the same time. For today's entry I'm going to show a very simple form post. This is as about as trivial as you can get, but I'm going to follow it up the next few days with a few more examples that will build upon it. So with that in mind, let's take a quick look at the code.

Presentation files (and thanks)

I was shocked - and pleased - by the large group of people who turned out for my presentation today. Charlie has posted the recording URL: http://experts.na3.acrobat.com/p65969256/. You can download the files below as well as a PDF export of the presentation. (Actually, I always use PDFs for presentations. I find it a bit quicker when alt-tabbing between slides and code.) Anyway, thank you to everyone (all 100+ of you!) for turning out. I hope you enjoyed it and learned something as well. I'm thinking my next presentation will focus specifically on jQuery and ColdFusion. Most folks know I'm something of a jQuery fan-boy now, but it did take me a while to get over a few initial humps, so I'd like to perhaps present on jQuery and help people pick up the framework quicker.

Making a "sticky" CFWINDOW

Ok, I'm not really a huge fan of CFWINDOW, despite this being the second blog post in a row about them. That being said, I thought I'd recreate a trick (see PS below) with CFWINDOW that maybe some folks will find useful. The trick involves keeping CFWINDOW in a position and making it stick there as you scroll. It is probably best if you see it live:

http://www.coldfusionjedi.com/demos/stalker/wintest.cfm

Scroll down and note the CFWINDOW will adjust itself back to the original position. The code simply uses JavaScript to do the following:

  • Notice scrolls
  • When they scroll, note the position of the scroll and start an interval
  • Figure out how far 'off' the CFWINDOW is from where it should be and go 90% of the way there.
  • If the distance is less than some threshold, just set it and stop the interval

A bit silly, but fun! The complete code is here:

<cfajaximport tags="cfwindow" />
<html>

<head>
<script>
var origx = 50;
var origy = 50;
var origheight = 200;
var targety = "";
var moving = false;

function init() {
   ColdFusion.Window.create('mywin','Windows Rules','win.cfm',{x:origx,y:origy,width:200,height:origheight,draggable:false});
   window.onscroll = handleScroll;
}

function handleScroll(e) {
   var cury = window.scrollY;
   var win = ColdFusion.Window.getWindowObject('mywin');
   //var newy = origy+curY;
   //console.log('set y to '+newy)
   //win.moveTo(origx,newy);
   targety = origy+cury;
   if (!moving) {
      moving = true;
      heartbeat = window.setInterval('moveit()', 10);
   }
}

function moveit() {
   
   var win = ColdFusion.Window.getWindowObject('mywin');
   var pos = win.xy;
   //find out how far I'm away from target
   //console.log('my targety is '+targety+' and my current y is '+pos[1])
   var distance = targety - pos[1];
      
   if (distance == 0) {
      window.clearInterval(heartbeat);
      moving = false;
      return;
   }

   //we want to go X%, unless the X% is < threshhold of &, then just go there
   if(distance > 3 || distance < -3) var tomove = Math.round(0.09 * distance);
   else var tomove = distance;
   var newy = pos[1]+tomove;
   //console.log('my newy is '+newy)
   win.moveTo(origx,newy);
}
</script>
</head>

<body>
<h2>Header</h2>
<cfoutput>#repeatString("<br/>",200)#</cfoutput>
<h2>Footer</h2>

</body>
</html>

<cfset ajaxOnLoad("init")>

p.s. Ok, so this effect has been done before, and probably with better JavaScript, but I think, stress think I was the first one to do it. Way back in the old days, around 96 or so, the company I worked for did a lot of custom web development for Netscape. One day we were tasked to do a company timeline for them. The timeline was a very wide graph inside a frame. The timeline tracked 3 things I think, and they wanted a little control you could click to turn on/off the lines. The problem was that as you scrolled along the timeline, you lost the little control doohicky.

I created what I called the Stalker, a bit of JS code that simply did, well, what I described above. I was pretty surprised when it actually worked. Later on I wrote an IE compatible version and eventually wrapped it into a custom tag for the Allaire ColdFusion tag gallery. Unfortunately the company I worked for back then wasn't really into the OS thing so the tag was encrypted and it belonged to them. Anyway, not trying to brag (ok, maybe I am) but I thought it was an interesting story.

Ok, another quick side story to this side story. I did some Perl work at Netscape and would, from time to time, check their intranet. They had a stats page for netscape.com. If I remember right the #s were insane, something like millions and millions of hits per day - all from folks who didn't know how to change their homepage. The Perl project is a story for another day.

Today's Weird JavaScript Issue

I ran into an interesting JavaScript issue yesterday. A user reported a bug with ColdFusionBloggers and searching. If he searched for one word, like 'object', then he could navigate through multiple pages of results just fine. If he search for two words, 'object oriented', then as soon as he clicked next the page would break.

This immediately made me think that I had forgotten a urlEncodedFormat() call somewhere. I'm very good about remembering that but it was certainly possible I could have made a mistake. (Although I'd probably blame Microsoft somehow.) When I opened the file though I could plainly see that I was indeed urlEncodeFormating the string before passing it to my JavaScript code to handle navigation.

I wanted to blame jQuery for this. When you use jQuery to do an Ajax-based content load into a div, they support loading a URL and optionally filtering to a div. What I mean is, you can say: I want you to load the contents of foo.cfm into my div called content, however, I don't want you to load all of foo.cfm, I want you to load the goo div's content only.

Here is an example that does a plain load of foo.cfm, and then another one that loads the goo div from foo.cfm:

$("content").load('foo.cfm')

versus...

$("content").load('foo.cfm goo')

Notice that the syntax uses a space to signify that the goo div is what we want from foo.cfm. My code was passing a URL that looked something like this:

content.cfm?start=11&search=foo%20goo (a search for foo goo)

Notice the escaped space. I thought perhaps jQuery was un-encoding the string somehow and getting confused. However, as I dug more, it seemed more as if that the string was being undecoded as it arrived to the function. I was able to recreate this bug without any jQuery at all:

<cfset s = "frank and beans">
<cfset u = "test3.cfm?x=1&foo=#urlEncodedFormat(s)#">

<script>
function testit(s) { console.log(s); }
</script>
<cfoutput>
<a href="javaScript:testit('#u#')">Test1</a><br />
<a href="" onClick="testit('#u#');return false">Test2</a><br />
</cfoutput>

So get this. Notice how the first link uses a javaScript: style link while the second uses an onClick. When you run this test, the first one will log test3.cfm?x=1&foo=frank. No beans! Edit: I had a brain fart there. The console shows frank and beans, but w/o the %20, the escape. End Edit The second test will correctly show the entire string with the proper escape values. I also tested with an input type=button and it worked fine as well.

So for some reason, when the string was passed using the href property of the anchor, the encoded part of the string is lost somehow. Now I'm still getting used to JavaScript after my long, dark, cold war with it, and I believe that I've read the using the event handler way is 'more proper', but this is the first time I've been bitten by something like this.

Does anyone know of any place where this behavior is documented?

Ask a Jedi: Detect JavaScript with ColdFusion?

Jose asks:

Is there a way to check if JavaScript is enabled using Coldfusion?

The answer is absolutely not. JavaScript is client side. ColdFusion is server side. That being said, if you don't mind hacking it up a bit, it isn't too difficult to accomplish.

More Entries