ColdFusion and jQuery definition auto-linking (A DB driven implementation)

So, this post is inspired by Ray Camden's post on the same subject, Using jQuery and ColdFusion to create an auto-link for definition application, so all credit goes to Ray. I wanted to implement something similar, and also make it database driven as I beleive any implementation of this in an application would be DB driven anyway. You can take a look at the demo here: http://coldfusion-ria.com/test/term.cfm I recommend using Firefox, as I have encountered an issue where the jQuery click event stops working for the first word on IE7 after you click it once.

First things first, the DB structure, looks like this:

The TermName is the term that will be linked, the term HTML is what we will replace the term word with, the TermDef is the definition of the term, and track can be used to track URL clicks.

The HTML code, is very simple. I am using jqModal for my Dialogs instead of cfwindows (In my opinion, cfwindow looks ugly). So we have a div, that is used for the dialog portion of the implementation in addition to the content div.
   view plainprintabout
 <div class="jqmWindow" id="dialog" style="display:none;">
     <a href="#" class="jqmClose">Close</a>
     <br/>
 </div>
     
 <div id="content" style="position:relative">
     <p>
     This is some fun text.
     </p>
10      <p>
11      This is some boring text.
12      </p>
13      <p>
14      Ajax? Ajax is awesome.
15      </p>
16      <p>
17      No Definition exists on this like *sad panda*.
18      </p>
19  </div>
The rest of the page looks like:
   view plainprintabout
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html>
 <cfajaxproxy cfc="term" jsclassname="term">
 <head>
 <link rel="stylesheet" type="text/css" media="screen" href="jqModal.css" />
 <script src="jquery.js" type="text/javascript"></script>
 <script src="jqModal.js" type="text/javascript"></script>
     
 <script>
10      var objTerm = new term();
11      var termHTML = objTerm.getTermsHTML();
12      $(document).ready(function(){
13          $('#dialog').jqm();
14          var currentContent = $("#content").html();            
15          for(var i in termHTML)
16          {
17              var word = new RegExp(i,"gi")                
18              currentContent = currentContent.replace(word,termHTML[i]);
19          }
20          
21          $("#content").html(currentContent);
22          
23          $(".hint").bind("click",function(e)
24              {                    
25              var clickElement = e.target;
26              var clickText = $(clickElement).text();
27              $.post('term.cfc?method=getTermDef&returnFormat=json',{term:clickText},showHint,"json");
28              }            
29          )
30              
31          function showHint(data,textStatus)
32          {
33              var currentHink = $("#dialog").html()
34              var newContent = currentHink + data;
35              $('#dialog').html(newContent);                                        
36              $('#dialog').jqmShow();
37                  
38          }
39              
40      })
41  </script>
42  </head>
The first thing I do, is use cfajaxproxy, to get the Terms and their corresponding HTML replacements. Then in the document.ready function (to make sure everything has been loaded), we call the .jqm function to let jqModal know which div will be used for the dialog purpose. Then we get all the current HTML content for the content div. Next, we iterate through the termsHTML object that was returned from our CFC, and replace the term words in the HTML with the termHTML. This then allows us to use jQuery's bind function to bind the "click" event to all elements with the class hint, $(".hint").bind("click",function(e). In this function, we get the text that was clicked on and pass it into the CFC, which returns the Term Definition. showHint function is the callback, which takes the definition, adds it to the dialog div, and then we use .jqmShow() function to show the dialog. The CFC is a fairly simple one,
   view plainprintabout
 <cfcomponent>
 
     <cffunction name="getTermDef" access="remote" returnFormat="json" output="false">
         <cfargument name="term" type="string" required="true" hint="Gets Term Defition from DB">
         
         <cfset result = "">
         
         <cfset clickTracker(Arguments.term)>
         
10          <cfquery name="selTermDef" datasource="RIADemo">
11              SELECT
12                  TermDef
13              FROM
14                  Terms
15              WHERE
16                  TermName = '#Arguments.term#'
17          </cfquery>
18          
19          <cfif selTermDef.RecordCount GT 0>
20              <cfset result = selTermDef.TermDef>
21          </cfif>
22      
23       <cfreturn result>
24      
25      </cffunction>
26      
27      <cffunction name="getTermsHTML" access="remote" returnFormat="json" output="false">    
28          
29          <cfset stcTerms = StructNew()>
30          
31          <cfquery name="selTermHTML" datasource="RIADemo">
32              SELECT
33                  TermName, TermHTML
34              FROM
35                  Terms            
36          </cfquery>
37          
38          
39          
40          <cfloop query="selTermHTML">
41              <cfset stcTerms["#TermName#"] = "#TermHTML#">                        
42          </cfloop>
43          
44          <cfreturn stcTerms>
45      
46      </cffunction>
47      
48      <cffunction name="clickTracker" access="private">
49           <cfargument name="term" type="string" required="true" hint="Term for which we will track the click">
50          
51          <cfquery name="updTrack" datasource="RIADemo">
52              UPDATE
53                  Terms
54              SET
55                  track = track + 1
56              WHERE
57                  TermName = '#Arguments.term#'
58          </cfquery>
59          
60      </cffunction>
61      
62  </cfcomponent>
I use some simple CSS, to highlight the term words (which are set to class hint).
   view plainprintabout
 .hint
 {
     border-bottom:1px dotted #333;
     cursor:help;    
 }
The main thing here is for getTermsHTML function, I am not using the default ColdFusion JSON return for the query. Instead, I build a structure and return it in JSON (I found it easier to work with on the JS side). The only issue right now is that on Internet Explorer, for the first word (in the demo that is fun), stops registering the click event after it has been clicked once. I have a message out on the jQuery google groups to try and see what could be wrong here. So, there you have it, another take on the same problem, a DB driven implementation though. Full Source Code: Here (CSS, JS or DB Files Not Included).
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Dan Vega's Gravatar Great stuff! One observation though with regards to your components. You really need to var your local variables, you can run into some major issues down the line if you don't.
# Posted By Dan Vega | 2/1/09 7:15 PM
Kumar's Gravatar Yes, I am guilty of that. I am more careful when I am writing production application code though.

Glad you liked the blog post though Dan. I am still going through your RocketFM series.
# Posted By Kumar | 2/1/09 7:17 PM
Raymond Camden's Gravatar Interesting. I'd only add the comment that I would not return the marked up HTML via AJAX. I'd simply say that every hot word uses a specific form of markup. That would a) reduce the network traffic and b) let you modify the links w/o having to modify db data.
# Posted By Raymond Camden | 2/2/09 9:16 AM
Kumar's Gravatar Agreed Ray, that would certainly be a better implementation. Probably the only way if I ever make a plugin out of this.
# Posted By Kumar | 2/2/09 11:11 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.5.003.  Design based on ARCLITE by: digitalnature