Simple jQuery experiment - automatically shortening a long list

This post is more than 2 years old.

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 &gt; 10) {

	for(var i=10; i&lt;kids.length; i++) {
		$(kids[i]).hide();
	}
	
	item.append("&lt;a href='' class='showMoreLink'&gt;Show More&lt;/a&gt;");
	
	$(".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>

Raymond Camden's Picture

About Raymond Camden

Raymond is a developer advocate for HERE Technologies. He focuses on JavaScript, serverless and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Elena posted on 10/5/2010 at 9:50 PM

The demo doesnt seem to work, Ray.

Comment 2 by Raymond Camden posted on 10/5/2010 at 9:51 PM

Because - once again - I left a darn console.log in. Try in 60 seconds. Sorry.

Comment 3 by Thor79 posted on 10/5/2010 at 10:38 PM

Interesting bit of code. Not sure I'll use it immediately but it did spark an idea on how to handle something in the website redesign I'm doing now.

Comment 4 by David Herman posted on 10/5/2010 at 11:14 PM

Also if you do a .live you won't have to re-bind this if you dynamically add a list of this nature to your page :)

Comment 5 by David Hammond posted on 10/5/2010 at 11:19 PM

jQuery is addictive :-) And every time I think "there ought to be a selector for that", there usually already is. A possible improvement to your script would be to replace your for loop with the :gt selector.
kids.filter(":gt(9)").hide()
Or, actually, now that I have looked through the jQuery docs some more:
kids.slice(10).hide()
Though slicing kids sounds extremely cruel.

Comment 6 by Raymond Camden posted on 10/5/2010 at 11:29 PM

@Dave: True - if I used a class instead of an ID, then I could make this work for N lists.

@David: Dude - awesome. I knew of gt, but I did not think I could apply it here - but your syntax makes perfect sense. When I do the plugin version I'll use it - take credit - and rule the world!

Comment 7 by Matt Gutting posted on 10/6/2010 at 4:52 PM

@David: What if we changed from
var kids = item.children();
kids.slice(10).hide()

to
var anklebiters = item.children();

Would that change your mind about slicing? ;-)

I agree with you - I don't use jQuery nearly as much as I could, and every time I look there's more I could use.

Comment 8 by David Hammond posted on 10/6/2010 at 5:43 PM

@Matt: You're right, that does sound less cruel! It does, unfortunately, still sound petty and vindictive.

Comment 9 by Raymond Camden posted on 10/6/2010 at 5:53 PM

Guys, follow up posted: http://www.coldfusionjedi.c...

Comment 10 by Wade Miller posted on 10/14/2010 at 1:52 AM

I'm sorry for the question, but I am trying to learn more about jQuery. Even if you are not displaying the data, the large amount of data is still being dumped to the web page. Couldn't you use jQuery and JSON and use the "Show More" link to retrieve more data from the database? Wouldn't this be a more efficient means of controlling the data?

Comment 11 by Raymond Camden posted on 10/14/2010 at 4:01 PM

@Wade: Yes, you could switch to an Ajax based solution, and if my list was truly large, then it would make sense. In my mind, this was more a visual thing then anything else. 50 items in a list, for example, would be long to a reader, but as for HTML download speed, it really is trivial.

Like in most CF things, there are almost always multiple ways to do things in jQuery. :)