Earlier today I was looking at some HTML that was generated by ColdFusion. The HTML was simply a list of months and years where content existed in the database. Because the database was a bit old, the list was quite long. I wondered if I could use jQuery to create an automatic shortening of the list as well as creating a way to expand the list based on some user interface. Here is what I came up with.

First, let's create a long list from ColdFusion:

<ul id="list"> <cfloop index="x" from="1" to="25"> <cfoutput> <li>Item #x#</li> </cfoutput> </cfloop> </ul>

And the result display is - as you expect - quite large:

Ok, so next I loaded up jQuery and added a document.ready block:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() {

});

I began by selecting my UL:

//get the item var item = $("#list");

Next I got the kids and checked to see if there were more than 10:

//get the children var kids = item.children();

if(kids.length > 10) {

At this point we need to do 3 things. First, hide kids 11 through - whatever. Secondly add a link to let the user see the rest of the list. Finally - listen for a click on that link so we can actually show the items. Here is the first block:

for(var i=10; i<kids.length; i++) { $(kids[i]).hide(); }

I could do that on one line, but I don't really see much benefit in that. I could also write this other ways. jQuery has a nice each() function that will iterate over stuff. But since I only want to go from 11-N, this seemed better to me.

item.append("<a href='' class='showMoreLink'>Show More</a>");

This - obviously - adds the link. Note the use of append. This will actually put the text in the same 'tab' as the rest of the list. I could have used after() but I kinda like how 'Show More' lined up.

$(".showMoreLink", item).click(function(e) { item.children().show() $(this).hide(); e.preventDefault(); });

And that is the final block. Note the use of item within the jQuery selector. This means that if I repeated this code multiple times then the event listener will only respond to the one of this particular list. I end up showing all the children which is a bit wasteful, but that syntax was too easy to pass up. I then hide the link. That means the list can be opened, but not closed. That seemed ok to me as I can't imagine wanting to toggle it back and forth - but I may come back to that. Finally I prevent the default click action. So how does it work? Here is the display:

And it works! You can click the big ole Demo button below to see. It also works fine with other types of parent/child relationships:

<div id="list2"> <cfloop index="x" from="1" to="25"> <cfoutput> <div>Item #x#</div> </cfoutput> </cfloop> </div>

But it did not work well with tables at all. Again, I'll come back to it. So - this works - but is pretty simplistic. In the next blog entry I'll convert this to a plugin so I can simply do: $("#list").autoShorten() while allowing for optional values for the number of items to show and what to use for the link text.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() {

//get the item var item = $("#list");

//get the children var kids = item.children(); console.log(kids.length); if(kids.length > 10) {

for(var i=10; i<kids.length; i++) { $(kids[i]).hide(); }

item.append("<a href='' class='showMoreLink'>Show More</a>");

$(".showMoreLink", item).click(function(e) { item.children().show() $(this).hide(); e.preventDefault(); });

} }) </script>

<ul id="list"> <cfloop index="x" from="1" to="25"> <cfoutput> <li>Item #x#</li> </cfoutput> </cfloop> </ul>