I'm working in an application where there's a long unordered list of items that have to be paginated. New items are added to the list regularly, and the items come from a database.
The only control I have is via the front end. I cannot control how they are all loaded in the DOM before page load.
I was able to work this out using Ben Everard's jqPagination plugin.
All list items are hidden with display:none. Then, when the next/previous buttons are clicked, a group of items are given the 'active' class - which sets them to display:block
.
//get the total number
var maxItems = $('li').length,
numPages = maxItems / 5,
oldSet,
pageSet = 5;
function showPages(s,c) {
$('li').slice(s, c).addClass('active');
pageSet = c;
}
$(document).ready(function() {
//Show first set of items
showPages(0, pageSet);
$('.pagination').jqPagination({
max_page : Math.ceil(numPages),
paged : function(page) {
//Remove the active class from previously active items
$('.active').removeClass('active');
//Reset the pageSet number
pageSet = 5 * page;
oldSet = pageSet - 5;
showPages(oldSet, pageSet);
}
});
});
This works pretty fast as is, but I'm concerned about whether it will run slow once the list grows (say to 1200 items).
How could I prevent any performance issues? Is it unavoidable?
1 Answer 1
You can't totally guarantee performance here, since so much is up to how the browser handles DOM manipulation and reflowing content when it's shown/hidden. But there are some things you can improve. Most notably: Don't find and re-find all the elements every time the page changes.
But first, in terms of structure, there's something weird going on. Only the jqPagination
stuff is inside a $(document).ready()
callback. Yet you're also trying to access page elements before that. The whole point of the ready
event is that it fires when you can start using the DOM; if you try before that, the page might not have finished loading. So the browser might literally only have half the page, with the rest still on its way across the interwebs.
So when you do this:
var maxItems = $('li').length
you risk finding zero li
elements, because the page isn't ready
yet.
Secondly, I worry about the way you're selecting elements. $('li')
will select all list item elements anywhere on the page. Doesn't matter if they're part of the list you want to paginate or not. In other words, you're selector casts its net much too wide.
Similarly with the $('active').removeClass('active');
call. Again, that's selecting any element with the active
class, regardless of where it is on the page. Using a class named active
is a pretty common thing for highlighting links, buttons, and tons of other things. So that line could easily wreak havoc on other parts of the page. Again, too greedy a selector.
Incidentally, I'd also use an ID of the list-pagination buttons, if there's only that one list. Having multiple elements with the class pagination
is perhaps less common, but your code certainly doesn't expect more than one, so better to use an ID, which is what you'd use for unique, non-repeated elements.
Back to structure: Your code is split in two. You have the showPage
function, which does indeed show pages. But the code to hide the previous page (the removeClass
call), is somewhere else. Why? If I call showPage
I'd expect it to show a page. Not just show the requested page, and leave previous stuff around. You're also calculating some stuff before calling showPage
, despite it being able to do so itself.
In other words, you've added a function, but not given it all the functionality it should probably have.
Finally, you have 5
as a magic number repeated in multiple places. Stick it in a variable and reference that variable instead. That way we a) know what the number is (it's got a name now), and b) you only have to change it in one spot to change the behavior. When it's spread out, maintenance becomes much harder.
I'd do this:
$(function () { // equivalent to $(document).ready(...)
var perPage = 5,
allItems = $('#theBigList').children('li'), // change to fit your list's selector
previousItems = null; // stores the last set of elements
// a simpler implementation
function goToPage(page) {
var offset = (page - 1) * perPage;
// hide previous items, if any
if(previousItems) {
previousItems.removeClass('active');
}
// show new items, and store the set
previousItems = allItems.slice(offset, perPage).addClass('active');
}
$('#paginationControls').jqPagination({
maxPage: Math.ceil(allItems.length / perPage),
paged: goToPage
});
goToPage(1); // might not be necessary if jqPagination automatically invokes its "paged" callback with the argument 1 upon initialization
});
Again, though, if things slow down for very long lists, it's probably because they're very, very long lists. It's not really the javascript that's at fault; it's how fast the browser is.
Explore related questions
See similar questions with these tags.