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>