3
\$\begingroup\$

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?

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.