Ok, is it just me, or has JavaScript spoiled me? It seems like almost daily I come across a web page with a long list of image thumbnails. I click on one and the entire page reloads. I click back and get a whole other large page load. I click on another image, and, well, you get the idea. I hate this. I've blogged before about the jQuery Thickbox plugin. It is a great solution to this problem, especially when you team it up with ColdFusion. Here is a great example.
I want to build a page which will make use of the Thickbox plugin, but I don't want to hard code the images. Instead, I'll let ColdFusion scan a folder and create the list for me. Since I'm using ColdFusion 8, I can also use it to create the thumbnails as well. This means I can copy images right off my camera, dump them in a folder, and leave it at that. I like easy. Let's look at the code.
<!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>Untitled Document</title>
<script type="text/javascript" src="http://www.coldfusionjedi.com/js/jquery.js"></script>
<script type="text/javascript" src="http://www.coldfusionjedi.com/js/thickbox/thickbox.js"></script>
<link rel="stylesheet" href="http://www.coldfusionjedi.com/js/thickbox/thickbox.css" type="text/css" media="screen" />
</head>
<body>
This is the header of the page. Most of it comes right from the default template Dreamweaver made for me. What you really want to care about are the 2 JavaScript includes and the CSS file. These are all the requirements needed for Thickbox.
<cfset imageFolder = "folder2">
<cfset imageDir = expandPath("./#imageFolder#")>
<cfdirectory directory="#imageDir#" name="images">
<!--- make thumbs --->
<cfif not directoryExists("#imageDir#/thumbs")>
<cfdirectory action="create" directory="#imageDir#/thumbs">
</cfif>
The variable imageFolder, simply refers to the subdirectory where I'm storing the images. The variable imageDir is a full path version of imageFolder. I need both a real full folder for ColdFusion and a relative one for my HTML later on. The cfdirectory tag simply grabs the files in the folder.
I check for a subdirectory named thumbs. If it doesn't exist, I create it. This should only be run once. If you needed to regenerate the thumbnails for whatever reason you can simply delete the folder.
<cfloop query="images">
<!--- valid image? --->
<cfif isImageFile("#directory#/#name#")>
<!--- check for thumbnail --->
<cfif not fileExists("#directory#/thumbs/#name#")>
<cfimage action="read" source="#directory#/#name#" name="image">
<cfset imageScaleToFit(image, 125, 125)>
<cfset imageWrite(image, "#directory#/thumbs/#name#",true)>
</cfif>
<cfoutput>
<a href="#imageFolder#/#name#" title="#name#" class="thickbox" rel="gallery-ss"><img src="#imageFolder#/thumbs/#name#" alt="#name#" /></a>
</cfoutput>
</cfif>
</cfloop>
Now for the complex part (and it really isn't that complex). I loop over the images query returned from the cfdirectory tag. For each file, I check to see if it is a valid image. If so, I then see if the thumbnail exists. If it does not, I read the image in, resize it, and save it in the thumbnails folder.
The last thing to do is output the HTML. I have to use the proper class/rel attributes for Thickbox but the only real dynamic part is the URL used for the link and image tag.
And that's it. You can see this code in action here. I've also zipped the entire thing up and attached it to the blog post. Please download the Thickbox JS code yourself though.
Archived Comments
I'm so excited by all this... Thanks for bringing JQuery into my life Ray :)
Ray,
If you want to use the writetobrowser action and resize each image on the fly you can with your imageUtils. <a href="http://imageutils.riaforge....">http://imageutils.riaforge.... using the getURL() method. Just a thought so you don't have to write all the thumbs to disk. Might be expensive depending on the application.
I used almost this exact same solution a few weeks ago here: http://missga.org/winnergal...
The only difference is I used Lightbox (http://plugins.jquery.com/p... instead of Thickbox. No particular reason why I chose one over the other, I think it was just the first one I found.
@Curt: I would definitely recommend against that. Resizing is a slow operation at times. I'd only resize once. My method writes to disk just once.
As for getURL, that is for converting a URL into an image, ie, taking a screen shot of a web page. It is not for resizing images.
I will definitely check this out. Have you tried it with a lot of images, say 100?
I would expect the first hit to be significantly slow. So I'd probably copy 20 at a time. Maybe 50. Of course, once shrunk, it will be much faster.
Why not - instead of deleting the thumbs folder - just use your little blog cfc trick and put in some kind of reinit key to trigger the last portion of your code? After all the last section only created a thumb if the image did not have one.
So you might do
<cfparam name="url.gogogadgetreset" default="">
<cfswitch expression="url.gogogadgetreset">
<cfcase value="rebuild"> Dump the thumb folder and start over</cfcase>
<cfcase value="refresh">simply refresh the missing thumbs</cfcase>
<cfdefaultcase>do nothing</cfdefaultcase>
</cfswitch>
Just a thought
Chris
Great work but adding any CF ajax tags breaks it. CF ajax tags seems not to want to work with any other Javascript.
Nothing in jQuery should 'break' with CFAjax, and vice versa. I'd dig into your code more. Got to be something simple. :)
After reading your posts about jquery I quickly became a fan, but I have the latest jquery release, and thickbox does not work with it. Only after I used the release you have in this code does it work.
That gives me some concerns about using jquery. I guess nothing is perfect. I am just trying to convince others at my company of its benefits, and this didn't help.
Wade - I'm using the latest jquery myself and it worked fine with thickbox. I'd try again and see if it was something else.
Although ajax is handy and improves user experience for forms and similar applications, i prefer rest style, easily bookmark-able image galleries.
Oh, I am about to give up on it. Using jQuery v.1.3.1, the code does not work for me. The larger images do not pull up. Using the version that you reference in the code above, jQuery v. 1.2.6, everything works fine.
I know there is something I'm probably missing. I'm just going to go back to CF.
@Bilgehan: So what if there was a way to bookmark one of the images? If I did a demo of that, would you approve?
@Wade: Again, my local server was using 1.3.1. Not sure what you mean by saying you will just go back to CF - as this IS CF, CF and JavaScript. :) I wouldn't give up yet. Can you put this online where we can look?
Yes, providing permalinks for the images would be great for accessibility.
Ok, I've got it on my TODO list. I'm thinking that, by default, you will land on a page and just see the thumbnails, but if you follow the permalink, it will auto load the right image. I can do this - I know I can - just give me few hours. :)
Okay, here are the links. It is the same exact code except for the jquery.js
http://www.bray.com/tboxtes...
http://www.bray.com/tboxtes...
What I meant by "back to using CF" I left off and actionscript.
Use Firebug. Go to the net tab, and you will see it fails on
http://www.bray.com/tboxtes...
On the docs for Thickbox, they talk about the fixes you have to do in code for this.
I looked all over the documentation. Two of us did. I can't believe we missed that. Thank you.
I've been meaning to install Firebug, but I always put it off. Now, I will do it while it is fresh on my mind.
@Ray, not sure if you looked at Fancybox, but i've used it and it's a really nice modal for the gallery stuff:
http://fancy.klade.lv/
That's pretty snazzy.
@Chris: Yes, that's an idea as well. I started to build a custom tag version of this to make it simpler, but the code is so short I didn't really think it worthwhile.
Ray, please try to display the images in a cflayout type border as an examples of how things breaks with cf ajax when adding external javascript.
@Marwan: Well, Thickbox is looking for items that match a class when the page loads. If the cflayout loaded content via Ajax then the content wouldn't be there when thickbox was trying to run it.
Is doing this loading via ajax? And how do you make this kind of calls work? Excuse my poor Javascript skills. Thanks.
<cflayout type="border">
<cflayoutarea name="whatever" position="center">
<script type="text/javascript" src="http://www.coldfusionjedi.c..."></script>
<script type="text/javascript" src="http://www.coldfusionjedi.c..."></script>
<link rel="stylesheet" href="http://www.coldfusionjedi.c..." type="text/css" media="screen" />
</head>
<body>
<cfset imageFolder = "folder2">
<cfset imageDir = expandPath("./#imageFolder#")>
<cfdirectory directory="#imageDir#" name="images">
<!--- make thumbs --->
<cfif not directoryExists("#imageDir#/thumbs")>
<cfdirectory action="create" directory="#imageDir#/thumbs">
</cfif>
<cfloop query="images">
<!--- valid image? --->
<cfif isImageFile("#directory#/#name#")>
<!--- check for thumbnail --->
<cfif not fileExists("#directory#/thumbs/#name#")>
<cfimage action="read" source="#directory#/#name#" name="image">
<cfset imageScaleToFit(image, 125, 125)>
<cfset imageWrite(image, "#directory#/thumbs/#name#",true)>
</cfif>
<cfoutput>
<a href="#imageFolder#/#name#" title="#name#" class="thickbox" rel="gallery-ss"><img src="#imageFolder#/thumbs/#name#" alt="#name#" /></a>
</cfoutput>
</cfif>
</cfloop>
</cflayoutarea>
</cflayout>
That code worked ok for me.
Glad the code works for you. This code and any code that contains cflayout of type border that you insert javascript any where in it stops to work on all 3 of our servers.
And you used the exact same code? Are you running CF801 with the latest hotfixes?
I am experiencing the same problem as Wade Miller. If I use the current 1.3.2 release of jquery the code does not work, revert back to an older version it works fine. I did change the path for macFFBgHack.png and all other paths as well. Any ideas on this? I have experienced this problem on a few of our apps at work as well, they work fine with the older jquery builds but the latest release seems to break them.
Any help would be appreciated!
I've confirmed - and I'm digging. May have to check with the plugin author.
Ok, I punted. I contacted the plugin author.
I should have just checked his forums first:
http://codylindley.com/thic...
I replace the line mentioned and it worked for me.
Thank you so much! I guess I could have checked the forums too, but letting others who come across this post know is a good thing as well.
Thanks again Jedi!!!
I had looked, but never found this solution. I had been cheating and including the older jQuery just for this particular plugin. It was working, but I was never happy since I was fudging.
Awesome and easy... thanks for another great tutorial! Just set this up on a site I'm converting from php to cfm that has many galleries and no database... this made my day a whole lot easier.
Great tutorial! It has helped me immensely. I am wondering about adding captions to describe the images. It seems that the quick and easy way would be to rename the image using the description. Is this wrong or bad? How else should I perform this?
Well, it might get messy. I mean, this is ok:
A_Picture_Of_My_Dog.jpg
But a caption that was a bit longer would be ugly. I'd maybe consider storing the captions in a db table that related file names to captions.
Hi ray, I am using the Fancybox Plugin this way:
<cflayoutarea title="Business Images">
<cfset listid = url.lid>
<cfinclude template="userpics.cfm">
</cflayoutarea>
my userpics.cfm file has 100 pictures and i had created a paging system there, showing 10 Images on a Page
Now the issue is for the first time, it loads Good, when i navigate to another page, it just show the image on full Page
Can u guide a bit.
Code for that included page is like this
<link rel="stylesheet" type="text/css" href="fancybox/jquery.fancybox-1.3.4.css" media="screen" />
<script type="text/javascript" src="fancybox/jquery.fancybox-1.3.4.pack.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("a.zoom").fancybox();
$("a.zoom1").fancybox({
'overlayOpacity' : 0.7,
'overlayColor' : '#FFF'
});
$("a.zoom2").fancybox({
'zoomSpeedIn' : 800,
'zoomSpeedOut' : 800,
'overlayShow' : false
});
});
</script>
<cfoutput>
<cfif startRowBack gt 0>
<a href="#AjaxLink('userpics.cfm?startRow=#startRowBack#&listid=#images.pic_listid#')#"><span title="Back #rowsPerPage# Records"><img src="images/arrow_back.gif" alt="Back #rowsPerPage# Records" border="0"></span></a>
</cfif>
<cfif startRowNext lte totalRows>
<a href="#AjaxLink('userpics.cfm?startRow=#startRowNext#&listid=#images.pic_listid#')#"><span title="Next #rowsPerPage# Records"><img src="images/arrow_next.gif" alt="Next #rowsPerPage# Records" border="0"></span></a>
</cfif>
</cfoutput>
I think your JavaScript needs to be outside of the cflayoutarea. When you go to page 2, the entire area is being replaced, which means your JS is as well.
Even Tried that way, Does notwork, when it moves to Page 2
Is this could be an issue that jquery Dom is redy once and when 2 page is navigated it does not load that again
Well, you got me. I can dig into this offline if you would like, although it crosses into the paid support category. ;)
TO be honest, I do NOT recommend mixing jQuery in with CF's built in Ajax stuff. I'd get rid of the cflayoutareas.
Hi Ray, Thanks for everything
I solved it, Let me share code with you so if this annoying things comes up with someone else they can solve it
just change this code:
<script type="text/javascript">
$(document).ready(function() {
$("a.zoom").fancybox();
$("a.zoom1").fancybox({
'overlayOpacity' : 0.7,
'overlayColor' : '#FFF'
});
$("a.zoom2").fancybox({
'zoomSpeedIn' : 800,
'zoomSpeedOut' : 800,
'overlayShow' : false
});
});
</script> to
<script type="text/javascript">
doFancyBox = function() {
$("a.zoom").fancybox();
$("a.zoom1").fancybox({
'overlayOpacity' : 0.7,
'overlayColor' : '#FFF'
});
$("a.zoom2").fancybox({
'zoomSpeedIn' : 800,
'zoomSpeedOut' : 800,
'overlayShow' : false
});
}
</script>
and write the following in the cflayoutarea tag
<cfset AjaxOnLoad('doFancyBox')>
and Boom it will work
Cheers
:)