Save your visitor's clickstreams
Capturing a Visitor?s Clickstream
A nice feature that I?ve seen implemented on a few Web sites is the ability for the visitor to view their clickstream (essentially, the path they?ve taken through your site). This allows the user to jump back to a particular page they?ve visited at any time.
For a (very ugly) sample, see http://charlie.griefer.com/code/clickstream/
By providing this list to your users, you allow them to go, ?hey...where was I a couple of pages back when I saw that one picture...??...and then peruse the list of recently viewed pages.
I?ve chosen to build this as a custom tag. For those unfamiliar with custom tags, it?s simply a ColdFusion template that acts as a
<cfinclude>, but can accept parameters. The reason for doing it as a custom tag is because it?s essentially the same code repeated over and over on each page. While this would ordinarily make it a candidate to simply be
<cfinclude>?d, there is one difference from page to page...each page must identify itself in order to be added to the clickstream list. I will be passing that page name to the custom tag as an attribute.
The custom tag is called breadcrumb.cfm. A custom tag is called by adding ?cf_? to the name of the template, and dropping the extension.
So to include the custom tag in a particular template, the code would be:
<cf_breadcrumb>
The way I?ve put this together, breadcrumb.cfm accepts two attributes. One is the title of the current page (to be added to the list of visited pages). The other is the number of pages to ?remember? (by default, it will display the last 10 pages visited). So to call the tag with the attributes, the code would look like:
<cf_breadcrumb
pageTitle=?Home Page?
trail_length=?10?>
This code would be on the home page, of course. To call the tag from the ?About Us? page, it would be:
<cf_breadcrumb
pageTitle=?About Us?
trail_length=?10?>
...and so on.
Because the clickstream persists from page to page, and is unique for each user, it will be stored in a session variable. The name of the session variable is session.clickstream (original, yes?).
Because we?re using session variables, there needs to be an Application.cfm. This Application.cfm is very simple. It creates a name for the application, using the <cfapplication> tag...which also specifies the timeout value of the session variables. It then checks to see if the session variable exists yet. If not, it creates it.
Here?s the complete code for Application.cfm:
<cfapplication
name="clickstream"
sessionmanagement="yes"
sessiontimeout="#createTimeSpan(0,0,20,0)#">
<cfscript>
if (NOT structKeyExists(session, 'clickstream')) {
session.clickstream = arrayNew(1);
}
</cfscript>
The <cfapplication> tag is self-explanatory. The
<cfscript> block is simply checking for the existence of the variable ?clickstream? within the session scope. It might look a bit unfamiliar, but it?s essentially the same thing as
<cfparam name=?session.clickstream?
default=?#arrayNew(1)#?>.
As you can see, session.clickstream is an array. This makes sense, as we?re going to be storing multiple values in one location.
Now to dissect the custom tag itself. The code for breadcrumb.cfm:
1. <cfparam name="attributes.trail_length"
default="10">
2. <cfif NOT structKeyExists(attributes,
'pageTitle')>
3. <cfexit>
4. </cfif>
5. <cflock name="addNewPage"
type="exclusive" timeout="10">
6. <cfscript>
7. if ((arrayIsEmpty(session.clickstream)) OR (compare(attributes.pageTitle,
session.clickstream[arrayLen(session.clickstream)].title))) {
8. if (arrayLen(session.clickstream) EQ attributes.trail_length) {
9. temp = arrayDeleteAt(session.clickstream, 1);
10. }
11. temp = arrayAppend(session.clickstream, structNew());
12. session.clickstream[arrayLen(session.clickstream)].title =
attributes.pageTitle;
13. session.clickstream[arrayLen(session.clickstream)].path = listLast(cgi.script_name, '/');
14. }
15. </cfscript>
16. </cflock>
17. <br /><br /><br /><br />
18. <cfoutput>Your Click Path:</cfoutput>
19. <br />
20. <table style="border:solid #000000 1px; width:150px;"
cellpadding="1" cellspacing="0">
21. <cfoutput>
22. <cfloop
from="#arrayLen(session.clickstream)#"
to=?1" index="i"
step="-1">
23. <tr>
24.
<td><a href="#session.clickstream[i].path#">#session.clickstream[i].title#</a></td>
25. </tr>
26. </cfloop>
27. </cfoutput>
28. </table>
Since breadcrumb.cfm is a bit involved, let?s go over it line by line.
- Breadcrumb.cfm is expecting two attributes to be passed to it. One is trail_length. If this attribute is not passed, we set it to a value of 10 by default.
- The other attribute that the tag expects is ?pageTitle?. This is important, as it is the name that will be appended to the clickstream. If it is not passed, we really don?t know what value to assign it by default. For that reason, we?re not even going to try. If it?s not provided, we use
<cfexit> to gracefully exit the custom tag.
- The cfexit
- </cfif>
- Because we?re dealing with session variables, it is considered a ?best practice? to use
<cflock>. This helps to ensure the integrity of the shared scope variables.
- Open a <cfscript> block
- Line 7 can get a bit tricky. On my first draft of this code, I could hit ?Home Page? 10 times in a row, and my clickstream would be ?Home Page? repeated 10 times. Likewise, if I was on a page and I hit refresh, that page was added twice in succession to the clickstream. I didn?t want this behavior. No one page should show up twice (or more) in succession. So I realized I needed to check the title of the last element in the array with the current title. If they are the same, I don?t want to do anything. The compare() function returns 0 if the two arguments are equal.
The one problem with that is that if the array was empty (if the current page was the first page in my clickstream), the application errors out on the compare() function (because there *is* no last element to compare (there can?t be a last element if there are no elements). So the one condition under which I don?t want the compare() function to run is if the array is currently empty. Hence the if arrayIsEmpty(session.clickstream) which is added to condition.
- Line 8 checks the length of the array to see if it has met the maximum length as specified by the attributes.trail_length value. There?s no use in continuing to store values if the user is only going to see the first 10 (or whatever value is specified). So in order to avoid exceeding the value of attributes.trail_length, we remove the first element from the array once we hit the specified length.
- The actual deletion of the first element in the array (remember, CF arrays are dynamic in nature, and when the element at position 1 is deleted, the elements at positions 2 and higher will shift down one position to fill the void).
- Closing curly brace for the condition in #8.
- Now we?re ready to append the current template into the clickstream. Now you?ll see that each array position in session.clickstream is actually a structure (because we want to store both the name of the page and the URL). You?ll understand that a bit more on lines 12 and 13. For now, suffice it to say that we need to create a new array position, which will be a structure :)
- One of the keys of the structure is ?title?. This is referenced as session.clickstream[n].title (where n is a position in the array between 1 and the array length). The value of the key is the value passed in attributes.pageTitle. This is the value that is actually displayed to the user in the
clickstream.
- The other key in the structure is ?path?. This value was not passed to the custom tag, but can be ascertained using the cgi environment variable #cgi.script_name# and the listLast() function. This value will allow us to make the individual items in the clickstream into links.
- ?
- ?
- ?
- ?
- Now we?re ready to display the clickstream to the user.
- ?
- Start an HTML table...
- Open a <cfoutput> tag...
- Now we?re going to loop over the session.clickstream array. So we loop from the length of the array to 1, with a step of ?1 (we want to loop backwards, so the most recently viewed page is at the top of the clickstream list).
- For each iteration of the loop, we want new table row (<tr>)
- Inside of a <td>, output the value of session.clickstream[i].title, inside of
<a href> tags that contain the value of session.clickstream[i].path. The user now clearly sees the clickstream, with a link to any page contained therein.
The rest of the code simply closes <tr>s and
<table>s.
To implement, simply place the call to the
custom tag on any pages that you would like included in the clickstream.
As shown earlier, it would look like this:
<cf_breadcrumb
pageTitle="Contact
Us"
trail_length="10">
This would, of course, go on a Contact Us page (contactus.cfm(?)), and specifies
that the clickstream should 'remember' 10 pages back.
While you'd probably want this code on most of the pages in your site, one
caveat would be do NOT place the tag on form action pages. A form's action
page should only be accessible via the form itself. If you do include the
action page it would most likely simply display a "Warning Page Has
Expired" message, which is of no use to your visitors.
That?s all there is to it. As always, questions, comments, criticisms, and large inheritances are welcome. Feel
free to e-mail me, or post a question on the
Easycfm.com forums.
-
arrays and structures - part 1
part one of a three-part tutorial designed to gently introduce you to the world of complex variables.
Author: Charlie Griefer (CJ)
Views: 21,008
Posted Date: Monday, August 11, 2003
-
arrays and structures - part 2
part two of a three-part tutorial designed to gently introduce you to the world of complex variables.
Author: Charlie Griefer (CJ)
Views: 14,058
Posted Date: Monday, August 11, 2003
-
arrays and structures - part 3
part three of a three-part tutorial designed to gently introduce you to the world of complex variables.
Author: Charlie Griefer (CJ)
Views: 15,433
Posted Date: Monday, August 11, 2003
-
CF 'Best Practices'
Some tips and techniques that I've picked up over the years. I don't maintain that these are 'official' or 'absolute'...they are simply my preference and things that have worked for me. I would like to share them here, and leave you to make the decision as to whether or not they fit in your 'code arsenal' :)
Author: Charlie Griefer (CJ)
Views: 19,728
Posted Date: Friday, August 15, 2003
-
CFSCRIPT Intro
An introductory look at CFSCRIPT. Rules, some basic syntax, and a couple of examples of loops and conditional processing.
Author: Charlie Griefer (CJ)
Views: 25,247
Posted Date: Saturday, January 18, 2003
-
ColdFusion Mad Libs - Part I
A silly but fun time-waster that you can easily include on your Web site. You might be surprised at how addicting it can become :)
Author: Charlie Griefer (CJ)
Views: 15,500
Posted Date: Thursday, May 29, 2003
-
ColdFusion Mad Libs - Part II
You've finished the first Mad Libs tutorial, but you feel like there's something missing. Of course there is! You want to be able to save the final output to a database to let your visitors browse through other user's stories. Includes a bad-words filter for the more conservative among us :)
Author: Charlie Griefer (CJ)
Views: 12,595
Posted Date: Thursday, May 29, 2003
-
Dynamic Column Output (Part One)
Have you ever wanted to display your content in rows of 3 columns?
If you ever wanted to specify the number of columns per row within your content, here's the tutorial for you.
Author: Charlie Griefer (CJ)
Views: 18,470
Posted Date: Thursday, May 29, 2003
-
Dynamic Column Output (Part Two)
This tutorial picks up where the Dynamic Columns tutorial left off, showing you how to not only output your data in a specified number of columns, but how to do it while still publishing well formed HTML.
Author: Charlie Griefer (CJ)
Views: 14,070
Posted Date: Saturday, May 31, 2003
-
Grouping Output in CF
How to group cfquery output in order to effectively display relational database data. Includes an overview of how to output nested groups as well.
Author: Charlie Griefer (CJ)
Views: 16,559
Posted Date: Tuesday, June 17, 2003
-
Helping users obtain their passwords
Your site requires your visitors to log in. of course, some of your visitors are going to forget their passwords (ok, most will forget their passwords). You don't want them to have to send you an e-mail, and then wait for a response. They need immediate access.
This tutorial shows two methods by which you can accomodate them.
Author: Charlie Griefer (CJ)
Views: 15,091
Posted Date: Thursday, August 28, 2003
-
JavaScript Form Validation
Yes, I know we're a ColdFusion site...but ColdFusion does not live in a vacuum. We have to know SQL, HTML, CSS...and sometimes...JavaScript! This tutorial focuses on using JavaScript (in lieu of cfform) to create client side form validation (and explains why writing your own is better than using ).
Author: Charlie Griefer (CJ)
Views: 32,254
Posted Date: Thursday, August 14, 2003
-
Remote File Management
Manage text-based files on your server from any Web browser. Create a new file, edit a file, or delete a file. Can be a life saver if you're on the road, and find an error in some of your code that needs a quick fix.
Author: Charlie Griefer (CJ)
Views: 15,337
Posted Date: Tuesday, June 3, 2003
-
Save your visitor's clickstreams
A nifty little custom tag that will allow you to save a visitor's clickstream through your site, as well as display it back to them (with links).
Did I really just say 'nifty'?
Author: Charlie Griefer (CJ)
Views: 13,146
Posted Date: Monday, June 16, 2003
-
to cfqueryparam or not to cfqueryparam
It's been out there since ColdFusion 4.5...most of us have heard of it...few of us use it. Here are some compelling reasons why you should get into the habit of using the tag.
Author: Charlie Griefer (CJ)
Views: 18,330
Posted Date: Thursday, May 29, 2003