The math to correctly display a pagination menu via Javascript is driving me insane. Hopefully someone who understands this a little better than me can help me out.
All I need is to understand how to properly display a pagination menu on the client side with javascript.
Lets say I have 10 total items.
The max I want show at once is 1.
The menu should display 5 numbers and the last number should be the last page with a ... if we aren't on the last set of numbers.
So for instance < 1 2 3 4 ... 10 >
User clicks 2 - Start number should be 1, last 4 < 1 2 3 4 ... 10 >
User clicks 4 - start number should 2, last 5 < 2 3 4 5 ... 10 >
User clicks 5 - start should be 3, last 6 < 3 4 5 6 ... 10 >
User clicks 7 - we are on the last set so < 6 7 8 9 10 >
So far all I have is:
var pages = Math.ceil(count / amount);
var maxIterations = 5;
var iterations = Math.min(pages, maxIterations);
var offset = Math.max(0, page-Math.round(maxIterations/2));
for(var i=0; i<iterations; i++) {
var label = page + i;
if(i == maxIterations-1) label = pages; //Last page number
paginatorObjects.push({label: label}); //This create the menu button
}
Basically the point is that I need the numbers to iterate up and down as the user clicks through them (how google does it) without having to use the arrow buttons. I understand jQuery has a nice plugin for this sort of thing, but this has to be done in vanilla javascript.
The math to correctly display a pagination menu via Javascript is driving me insane. Hopefully someone who understands this a little better than me can help me out.
All I need is to understand how to properly display a pagination menu on the client side with javascript.
Lets say I have 10 total items.
The max I want show at once is 1.
The menu should display 5 numbers and the last number should be the last page with a ... if we aren't on the last set of numbers.
So for instance < 1 2 3 4 ... 10 >
User clicks 2 - Start number should be 1, last 4 < 1 2 3 4 ... 10 >
User clicks 4 - start number should 2, last 5 < 2 3 4 5 ... 10 >
User clicks 5 - start should be 3, last 6 < 3 4 5 6 ... 10 >
User clicks 7 - we are on the last set so < 6 7 8 9 10 >
So far all I have is:
var pages = Math.ceil(count / amount);
var maxIterations = 5;
var iterations = Math.min(pages, maxIterations);
var offset = Math.max(0, page-Math.round(maxIterations/2));
for(var i=0; i<iterations; i++) {
var label = page + i;
if(i == maxIterations-1) label = pages; //Last page number
paginatorObjects.push({label: label}); //This create the menu button
}
Basically the point is that I need the numbers to iterate up and down as the user clicks through them (how google does it) without having to use the arrow buttons. I understand jQuery has a nice plugin for this sort of thing, but this has to be done in vanilla javascript.
Share Improve this question asked Aug 20, 2012 at 14:36 ryandlfryandlf 28.6k37 gold badges111 silver badges164 bronze badges2 Answers
Reset to default 4This problem had me scratching my head for a while too, I recently had to implement it for an AngularJS application, but the pagination logic is pure javascript.
There's a working demo on CodePen at http://codepen.io/cornflourblue/pen/KVeaQL
I also wrote this blog post with further details
Here's the pagination logic
function GetPager(totalItems, currentPage, pageSize) {
// default to first page
currentPage = currentPage || 1;
// default page size is 10
pageSize = pageSize || 10;
// calculate total pages
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
} else {
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage + 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage + 4;
}
}
// calculate start and end item indexes
var startIndex = (currentPage - 1) * pageSize;
var endIndex = startIndex + pageSize;
// create an array of pages to ng-repeat in the pager control
var pages = _.range(startPage, endPage + 1);
// return object with all pager properties required by the view
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
What I ended up doing is this. This is directly from my source code, so there is some templating logic that doesn't make sense, but anyone who might be reading this in the future should be able to adjust according to their needs.
function paginate() {
var paginator = sb.find('#' + moduleName + 'Pagination')[0];
var container = paginator.getElementsByClass('container')[0];
sb.dom.clearAll(container);
if(count && count > 0) {
sb.dom.removeClass(paginator, 'hidden');
var pages = Math.ceil(count / amount);
var maxIterations = 5;
var iterations = Math.min(pages, maxIterations);
var paginatorObjects = [];
var center = Math.round(maxIterations/2);
var offset = page-center;
if(page < center) offset = 0; //Don't go lower than first page.
if(offset + iterations > pages) offset -= (offset + iterations) - pages; //Don't go higher than total pages.
for(var i=0; i<iterations; i++) {
var label = (i+1) + offset;
paginatorObjects.push({label: label});
}
sb.template.loadFrom(templateUrl, 'paginator').toHTML(paginatorObjects).appendTo(container, function(template) {
var pageNumber = template.obj.label;
if(pageNumber != page) {
sb.addEvent(template, 'click', function() {
getAppointments(pageNumber);
});
} else {
sb.dom.addClass(template, 'highlight');
}
});
if(offset + iterations < pages) {
var dots = document.createTextNode(' ... ');
container.appendChild(dots);
sb.template.loadFrom(templateUrl, 'paginator').toHTML({label: pages}).appendTo(container, function(template) {
sb.addEvent(template, 'click', function() {
getAppointments(pages);
});
});
}
var backBtn = paginator.getElementsByClass('icon back')[0];
var forwardBtn = paginator.getElementsByClass('icon forward')[0];
sb.removeEvent(backBtn, 'click');
sb.removeEvent(forwardBtn, 'click');
if(page - 1 > 0) {
sb.dom.removeClass(backBtn, 'hidden');
sb.addEvent(backBtn, 'click', function() {
getAppointments(page-1);
});
} else {
sb.dom.addClass(backBtn, 'hidden');
}
if(page + 1 <= pages) {
sb.dom.removeClass(forwardBtn, 'hidden');
sb.addEvent(forwardBtn, 'click', function() {
getAppointments(page+1);
});
} else {
sb.dom.addClass(forwardBtn, 'hidden');
}
} else {
sb.dom.addClass(paginator, 'hidden');
}
}
When getAppointments() is called from the buttons, I use AJAX to get the next pages results, then this pagination() function is called again from that function so everything gets reset. The variables that determine the amount to show per page and the current page number are global (to the container function) and are not shown in this code sample.
The logic behind this is fairly simple. The center is determined (in my case the max it shows at once is 5, so the center is 3) and then an offset is calculated based on the page number. Clicking 4 will adjust the start number +1, and 2 would adjust it -1.