Using jQuery to scroll to the bottom of a div, revised

I am a firm believer that every day you should learn something new.  In this particular case, I learned something new quite some time ago and just never applied it to my code samples.  This is something I really want to pay more attention to because I do not want be a purveyor of misinformation.

Nick commented on this original post that once the animation began he could not scroll back up to the top of the post.  Looking into the problem I immediately noticed what was happening, the scrollHeight attribute was not taking into account the height of the scrolling div.  If the scrolling div has a height of 100px initially and enough text has been entered to scroll it, it may have a scrollHeight of 120px.  Moving down to the bottom of the div, the scrollTop would be 20px which is the scrollHeight – $(‘#div’).height().  Adjusting for that in the jQuery code will make it function as expected.

The corrected code is below:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery Scroller</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
</head>
<body>
<div id="myDiv" style="width: 500px; height: 100px; overflow: auto; border: 1px solid black;"></div>
<input type="text" id="myTextInput" />
<input type="button" value="Send" id="postButton" />
<div id="info"/>
<script type="text/javascript">
 $(document).ready(function(){
  $('#postButton').click(function(){
   $('#myDiv').append($('#myTextInput').val() + '<br/>');
   $('#myTextInput').val('');
   $("#myDiv").animate({ scrollTop: $("#myDiv").attr("scrollHeight") - $('#myDiv').height() }, 3000);
  });
 });
</script>
</body>
</html>

You can see the corrected demo here:  http://www.kisdigital.com/tests/scroll/

Sorry about that!

Categories: jQuery

Day two with FW/1

Playing around with FW/1 today I decided to take a look at the differences at how services differ from working with domain objects.  Skimming through the FW/1 documentation I did notice the framework has a built in function to populate a bean, namely the fw.populate() function.  Calling a bean (an object with getter/setter methods) is not much more involved in calling a service.  First, you create an instance of your bean.  Then call populate() method passing rc keys that you would like to set and the populate function will find the setX method in the bean and set the appropriate value.

I have an extremely simple application I have setup that has two views.  The default view is just a form that gets posted to the main.submit action which is just cfdumping the rc scope.

The sample “bean” setup for testing purposes:

<cfcomponent>
 <cffunction name="init">
  <cfscript>
   variables.instance = structNew();
   instance['fw'] = arguments.fw;
  </cfscript>
 </cffunction>
 <cffunction name="setFirstname">
  <cfscript>
   instance['firstName'] = arguments.firstName;
  </cfscript>
 </cffunction>
 <cffunction name="setLastname">
  <cfscript>
   instance['lastName'] = arguments.lastName;
  </cfscript>
 </cffunction>
 <cffunction name="saveUser" returntype="struct">
  <cfscript>
   var response = structNew();
   response['message'] = "Saving user: " & instance.firstName & " " & instance.lastName;
  </cfscript>
  <cfreturn response/>
 </cffunction>
</cfcomponent>

Nothing too complicated going on here, just a basic init method to setup the instance variables, two setter methods and finally a method for “saving” our user information.  The controller method for handing the submit is as follows:

<cffunction name="submit">
 <cfargument name="rc" type="struct"/>
 <cfscript>
  var userObj = createObject("component", "model.user");
  variables.fw.populate(userObj, "firstName,lastName");
  structAppend(rc, userObj.saveUser());
 </cfscript>
</cffunction>

This creates the user object, populates the values and then saves the user and appending the results into the rc scope.  Services are a little easier to implement as they have a one line invocation.  I am still trying to figure out when it would be best to use a service to move data in and out of the application as opposed to creating a bean in the model.  I would imagine the ultimate answer would be whatever works best for you, but I can see where services could be advantageous as well.  I welcome any comments.

Categories: ColdFusion, FW/1, Railo

Day one with FW/1

Aside from fun jQuery projects, I have really been meaning to pickup Framework One and really check it out.  After using XML controllers for a while not having to worry about defining all the behaviors is nice.  Best of all, it is really intuitive to work with.  After installing the FW/1 package I immediately deleted all the example applications and the introduction folder, that is just how I roll.

Then I started with a minimum framework: a default layout, a default view and a default controller.  I had everything up and running quickly.  Next I decided to write a sample application.  Nothing too fancy, enter a first and last name in a form and a submit button.  I could have went with the typical “Hello World” application, but I am kinda finicky about form processing.  The framework I use does not automatically append the form scope into the request context. Generally I default all my form values for my view in the controller and then roll the form scope in,  overwriting my defaults with any values that might be in the form scope.  If validation fails, I can always fall back to my submission page and have form values defaulted.  It did take me a little while to adjust to FW/1’s way of doing things, but it was not a large adjustment.

I also like how FW/1 allows for a startItem, endItem for controller methods.  It allows you to easily handle validation before the main method gets called.  I suppose the validation could be handled in the main method but I find it a little clearer when I break the logic into distinct pieces.

Finally I setup a simple “user” service.  In this case, it was just to return a string letting me know the user’s first and last name that was entered on the form.  The whole thing came together in less than an hour from start to finish, including time spent looking over the documentation when I did not fully understand something.

Although there are several aspects of the framework that will require me to change the way I handle certain things, I can see the framework is flexible and extremely easy to use.  I will definitely be using FW/1 in some future projects.

Categories: ColdFusion, FW/1, Railo

jQuery source code up

For those of you who were interested in getting the source for my demo app, it can be found here:

http://dl.dropbox.com/u/868773/030510.rar

Categories: jQuery, jQuery UI

What can 150 lines of jQuery do?

Lately I have been working on a side project meaning to use it as a administration tool of sorts for some up and coming projects I have planned.  Basically the interface mimics a computer desktop.  You click on application links on the desktop and it opens a window.  You can drag it around, resize it, minimize it, etc.  You can drag the application links around on the desktop and arrange them however you like.

To be fair, I got the idea from a co-worker of mine, Aaron,  who wrote a similar application.  On the other hand, I wanted to leverage the power of the jQuery UI so changing a theme would just be a matter of changing which stylesheet to include.  I thought it would be an involved process getting everything working but actually it was deceptively easy.  It still doesn’t layout correctly in IE…  Imagine that.

cpanel

I don’t know if I will ever actually ever use it in a project but it was pretty fun to play around with.  I will probably put it up for download later if anyone is interested in playing around with it.

Categories: ColdFusion, jQuery, jQuery UI

Always remember complex data types are passed by reference

I am working on a pet project currently that is written primarily in jQuery.  The initial state of the application is set by an  ajax call to a remote CFC method that sends back the data.  I was rushing through the CFC side of things trying to get the code written and came up with something similar to this:

<cfscript>
 result = structNew();
 appData = structNew();
 result['apps'] = arrayNew(1);

 appData['appName'] = "help";
 appData['windowTitle'] = "Help Content";
 appData['site'] = "";
 result['apps'][1] = appData;
 appData['appName'] = "cfbloggers";
 appData['windowTitle'] = "ColdFusion Bloggers";
 appData['site'] = "http://www.coldfusionbloggers.org";
 result['apps'][2] = appData;
</cfscript>

While this code is completely valid it definitely did not return the expected results.  Since ColdFusion passes complex data types (structures, components, and Java objects, etc.) by reference  both of the structures in my array elements contained the cfbloggers information instead of what I expected.  In the rush to get the code done, I forgot to set appData = structNew() after setting the first array element which would have returned the expected result.  A duplicate() would have worked equally well, but that is a bit of overkill for this simple application.

Edit: As Aaron pointed out, arrays are indeed passed by value as with other simple data types (strings, dates and numbers).

Categories: ColdFusion Tags:

Creating Javascript arrays on the fly

The need to build a Javascript array is pretty non-existent since many scripting engines (or at least the ones I use) can natively return objects in JSON.  However, if for some reason you cannot get your data back as JSON I whipped up a quick example of how it can be done.  This is more a note for myself, but maybe someone will find it useful.

First we take the data that we already have available in ColdFusion and build the Javascript array.  In our Javascript we create a new variable and set it to the value ColdFusion has built for us.  Below is the code:

<cfscript>
 dataString = "";
 // Build an array of people to play with
 people = arrayNew(1);
 people[1].name = "Jim";
 people[1].hairColor = "Brown";
 people[1].eyeColor = "Brown";
 people[2].name = "John";
 people[2].hairColor = "Black";
 people[2].eyeColor = "Blue";
 people[3].name = "Jane";
 people[3].hairColor = "Blonde";
 people[3].eyeColor = "Green";
 // The "important" stuff (tm)
 jsArray = "[";
 for(y = 1; y lte 3; y = y + 1){
  dataString = listAppend(dataString, "{ name: '#people[y].name#', hairColor: '#people[y].hairColor#', eyeColor: '#people[y].eyeColor#' }");
 }
 jsArray = jsArray & dataString;
 jsArray = jsArray & "]";   
</cfscript>
<cfoutput>
<script type="text/javascript">
 var peopleArray = #jsArray#;
 function dumpArray( obj ){
  for(var i = 0; i < obj.length; i++){
   for(j in obj[i]){
    document.write('<strong>' + j + ':</strong> ' + obj[i][j] + '<br />');
   }
   document.write('<br />');
  }
 }
 dumpArray(peopleArray);
</script>
</cfoutput>
Categories: Javascript

jqGrid: The hard way

I have been using the jQuery jqGrid plug-in quite a bit lately and I would have to say it is one of the best data grids I have used.  However, if you are pulling your data from a local array, until you are familiar with the inner workings of the plug-in it can be a bit intimidating.

Read more…

Categories: jQuery Tags:

Dynamic filtering with jQuery and XML

February 3, 2010 Robert Shane Zehnder 4 comments

Back in November I wrote a short piece on dynamic search filtering using jQuery and using ajax.load() to populate a search div.  A reader asked if it was possible to do the same thing with an XML document and it is quite easy to do.  I do not have it searching over multiple nodes, but with a little array-fu I imagine it can be modified to handle it.

Also I will note I did get most of the code from learncf.com, and more specifically http://tutorial18.learncf.com by Scott Bennett.  I just wanted to make sure I gave credit where credit was due.

The search page is exactly the same as the previous post, we have just modified the query.cfm to use an XML document instead.  Here is our new query.cfm file:

<cfparam name="url.search" type="string" default="">
<cfsavecontent variable="books">
<?xml version="1.0"?>
<books>
 <book>
 <isbn>0321330110</isbn>
 <title>Macromedia ColdFusion MX 7 Certified Developer Study Guide</title>
 <author>Ben Forta</author>
 </book>
 <book>
 <isbn>0596004206</isbn>
 <title>Learning XML, Second Edition</title>
 <author>Erik Ray</author>
 </book>
 <book>
 <isbn>0782140297</isbn>
 <title>Coldfusion MX Developer's Handbook</title>
 <author>Raymond Camden</author>
 </book>
</books>
</cfsavecontent>

Search page
<cfset booksXml = XmlParse(books)/>

<cfset SearchByTitle = xmlSearch(booksXml,'//book [contains(title,"#url.search#")]')>
<cfoutput>
 #arrayLen(SearchByTitle)# Book(s) Found<br /><br />
 <cfloop from="1" to="#arraylen(SearchByTitle)#" index="i">
 <cfset BookXML = xmlparse(SearchByTitle[i])>
 <b>ISBN:</b> #BookXML.book.isbn.xmltext# - <b>Title:</b> #BookXML.book.title.xmlText# - <b>Author:</b> #BookXML.book.author.xmlText# <br />
 </cfloop>
</cfoutput>

I have used the hard-coded XML Scott used in his tutorial, but it could easily be modified to dynamically load the XML.   We are searching the Title nodes for each title that contains our search string and it is case-sensitive.   I might play around with this a little more later today and see if I can make it more robust.

I have a demo of this code here: http://www.kisdigital.com/tests/filter

Edit:

I did some research and found this post by Ben Nadel explaining the XmlSearch() function in detail.  Expanding the search field to other nodes in the XML document is very easy.  For instance, if you wanted to search on titles and authors you could modify the XmlSearch string as follows:

‘//book [(contains(title,"#url.search#")) or (contains(author,"#url.search#")) ]‘

I think Ben’s site covers just about everything.  ;)

Categories: ColdFusion, jQuery

A hectic few weeks

January 22, 2010 Robert Shane Zehnder 6 comments

After getting laid off from my full-time job last year, I spent the last few months of 2009 doing free-lance work whenever possible and just trying to make ends meet.  While it tends to be entertaining, it definitely does nothing to diminish my impressive collection of gray hair.  However, my Christmas present this year was a full-time ColdFusion development position in Greensboro, NC.  After a few rough months and a year of jumping from contract to contract that was probably the best gift I have ever received.

That being said, 2010 is off to a great start even if the first few weeks have been a little crazy.  Unfortunately my only laptop decided to burn itself up, but it was on its last leg so that wasn’t a huge surprise.  I am saving up for a new machine so I can get back to work on the personal and collaboration projects planned for this year.  I definitely intend to pick up some new frameworks and write a lot more jQuery.  If I learn something new, I will share with all of you.

I hope you all are having a great year as well.

Categories: General