Javascript постраничная навигация таблицы
Постраничная навигация спасает нас когда мы имеем большой объем данных в ограниченном интерфейсе. Иногда так случается, что по независящим от нас причинам мы имеем на "клиенте" таблицы данных большого размера. Это бывает при работе с веб-сервисами например, или по какой другой причине, когда мы не можем реализовать механизм постраничной навигации на стороне сервера.
Кстати, вот здесь рассказывается как сделать постраничную навигацию Но что если повлиять на вывод данных сервером мы не можем? В общем не знаю как у вас, а у меня такое пару раз было. И тогда я задумался, что было бы не плохо сделать постраничную навигацию на JavaScript. Задумался... и сделалВ общем добавить тут особо нечего предлагаю посмотреть демку постраничной навигации на JavaScript в контексте таблиц:
Cell 1 | Cell 2 | Cell 3 |
---|---|---|
1 | Click me | Some content more ... |
2 | Some content | Some content more ... |
3 | Some content | Some content more ... |
4 | Some content | Some content more ... |
5 | Click me | Some content more ... |
6 | Some content | Some content more ... |
7 | Some content | Some content more ... |
8 | Some content | Some content more ... |
9 | Some content | Some content more ... |
10 | Some content | Some content more ... |
11 | Some content | Some content more ... |
12 | Click me | Some content more ... |
13 | Some content | Some content more ... |
14 | Some content | Some content more ... |
15 | Some content | Some content more ... |
16 | Some content | Some content more ... |
17 | Some content | Some content more ... |
18 | Some content | Some content more ... |
12 | Click me | Some content more ... |
20 | Some content | Some content more ... |
21 | Some content | Some content more ... |
22 | Some content | Some content more ... |
23 | Some content | Some content more ... |
24 | Some content | Some content more ... |
25 | Some content | Some content more ... |
26 | Some content | Some content more ... |
27 | Some content | Some content more ... |
12 | Click me | Some content more ... |
29 | Some content | Some content more ... |
30 | Some content | Some content more ... |
31 | Some content | Some content more ... |
32 | Some content | Some content more ... |
33 | Some content | Some content more ... |
34 | Some content | Some content more ... |
35 | Some content | Some content more ... |
Ну, что тут объяснять - стандартный интерфейс постраничной навигации. Подключение простейшее. Забрать можно из моего репозитория постраничная навигация для html-таблиц : Итак, что бы подключить этот функционал для начала нужен такой html-каркас
<table id="paged"> <thead> <tr> <th>Cell 1</th> <th>Cell 2</th> <th>Cell 3</th> </tr> </thead> <tbody> <tr><td>1</td><td>Some content</td><td>Some content more ... </td></tr> <!-- Много - много строк таблицы //--> <tr><td>35</td><td>Some content</td><td>Some content more ... </td></tr> </tbody> <tfoot> <tr> <!-- Здесь появится блок ссылок постраничной навигации //--> <td colspan="3"></td> </tr> </tfoot> </table>
Стоить отметить, что теги tbody и tfoot следует указать явно, потому что скрипт использует их в своей работе.
Самое простое подключение постраничной навигации
После того как вы прописали html код можно добавить следующий Javascript
pagedTable( /* Ссылка на рабочую таблицу */ document.getElementById("paged"), /* Кол-во строк таблицы на "странице" */ 5 );
Если данных много
Если данных очень много, то возможна такая неприятность: до момента загрузки скрипта на какое-то короткое время таблица отобразится полностью, так сказать во весь рост. Поэтому для того, что бы верстка не дергалась при загрузке страницы используем небольшую хитрость: сделаем элемент tbody нашей таблицы - невидимым:
<table id="paged"> <thead> <tr> <th>Cell 1</th> <th>Cell 2</th> <th>Cell 3</th> </tr> </thead> <!-- СПРЯТАЛИ! //--> <tbody style="display: none"> <tr><td>1</td><td>Some content</td><td>Some content more ... </td></tr> <!-- Много - много строк таблицы //--> <tr><td>35</td><td>Some content</td><td>Some content more ... </td></tr> </tbody> <tfoot> <tr> <td colspan="3"></td> </tr> </tfoot> </table>
затем нам понадобится уже в Javascript коде при вызове функции "pagedTable" в аргументе-конфиге определить обработчик события "onAfterInit", который вызывается лишь однажды в момент после инициализации таблицы, в котором мы сделаем нашу таблицу снова видимой уже тогда, когда скрипт её обработает:
pagedTable( /* Ссылка на рабочую таблицу */ document.getElementById("paged"), /* Кол-во строк таблицы на "странице" */ 5, /* Конфигурация постраничной навигации */ { /* Событие "после инициализации" - отработает только ОДИН РАЗ! */ onAfterInit : function (table) { // Делаем данные опять видимыми table.tBodies[0].style.display = ""; } } );
Вот теперь даже если данных в таблице очень много - она не будет вам досаждать прыганием контента.
Кастомизация
Сигнатура функции постраничной навигации:
/** * @param {HTMLTableElement} HTMLTable - ссылка на таблицу * @param {number} HTMLTable - кол-во записей отображаемых на "странице" * @param {Object} HTMLTable - Объект конфигурации */ function pagedTable(HTMLTable, entryPerPage, [pagerConfig]);
Подробнее об объекте конфигурации:
// Объект конфигурации: var pagerConfig = { /** * Текст ссылки "в начало" * @type {string} */ toStart: "<<", /** * Текст ссылки "предыдущий" * @type {string} */ toPrev: "<<", /** * текст ссылки "следующий" * @type {string} */ toNext: ">", /** * текст ссылки "в конец" * @type {string} */ toEnd: ">>", /** * кол-во ссылок, отображаемых на "странице" * @type {string} */ linkPerPage: 10, /** * имя тега для ссылок постранички * @type {string} */ linkTag: "span", /** * Текст информации блока постраничной навигации. * Текст может содержать следующие плейсхолдеры: * %p - текущая страница * %c - страниц всего * %n - сам блок постраничной навигации * %r - общее количество строк в таблице * @type {string} */ template: "<span>Page: %p from %c (rows %r)</span><span>%n</span>", /** * Обработчик вызываемый после инициализации таблицы скриптом. * Вызывается только однажды. * @param {HTMLTableElement} table - ссылка на рабочую таблицу */ onAfterInit: function (table) {}, /** * Обработчик вызываемый при клике на ссылки постраничной навигации. * @param {number} pageId - ID "страницы" таблицы (по факту это отображаемый номер страницы - 1) * @return {boolean} Можно вернуть false, если нужно прервать рабочий процесс. */ onNavClick: function (pageId) {} };
Как вы можете видеть объект конфигурации на самом деле содержит еще немного свойств, которые позволят вам настроить отображение постраничной навигации под свои нужды.
Параметр объекта конфига pagerConfig.template - это строка которая позволит вам самим решать как будет выглядеть блок постраничной навигации:
pagedTable( /* Ссылка на рабочую таблицу */ document.getElementById("paged"), /* Кол-во строк таблицы на "странице" */ 5, /* Конфигурация постраничной навигации */ { /* Шаблон блока постраничной навигации: */ template : "<span class='summary'>Page: %p from %c (rows: %r)</span><span class='page-nav'>%n</span>", } );
А вот подключение "по полной" с изменением всех возможных параметров:
pagedTable( /* Ссылка на рабочую таблицу */ document.getElementById("paged"), /* Кол-во строк таблицы на "странице" */ 5, /* Конфигурация постраничной навигации */ { /* текст ссылки "в начало" */ toStart:"START", /* текст ссылки "предыдущий" */ toPrev:"PREV", /* текст ссылки "следующий" */ toNext:"NEXT", /* текст ссылки "в конец" */ toEnd:"END", /* кол-во ссылок, отображаемых на "странице" */ linkPerPage:4, /* Тег для элементов постраничной навигации: */ linkTag : "li", /* Шаблон блока постраничной навигации: */ template : "<span class='summary'>Page: %p from %c (rows: %r)</span><ul class='page-nav'>%n</ul>", /* Событие "после инициализации" - отработает только ОДИН РАЗ! */ onAfterInit : function (table) { // Делаем данные опять видимыми table.tBodies[0].style.display = ""; }, /* Это событие будет отрабатывать каждый каз при клике на элементе постраничной навигации если вернуть из ф-ции "false" то переход на страницу не произойдет. */ onNavClick : function (pageId) { // Например можно запретить переход на четные страницы return (pageId+1) % 2 ? true : false; } } );
Ну, и полный листинг ф-ции pagedTable(), если кому интересно:
var pagedTable = (function (GLOB) { "use strict"; return function (HTMLTable, entryPerPage, pagerConfig) { var table = HTMLTable, tBody = table.tBodies[0], // Дефолтные значения конфигурации: pConfig = { toStart: "<<", toPrev: "<<", toNext: ">", toEnd: ">>", linkPerPage: 10, linkTag: "span", template: "<span>Page: %p from %c (rows %r)</span><span>%n</span>", onAfterInit: undefined, onNavClick: undefined }, // Стили таблицы: tableStyle = tBody.currentStyle || GLOB.getComputedStyle(tBody), // Контейнер в футере таблицы для постранички: linksContainer = table.tFoot.rows[0].cells[0], // текущая "страница" currentPage = 0, // Подмножество ссылок постранички: linksSet, // Общее кол-во ссылок: linksCnt, // Ссылок на "странице": offset, // ссылки на строки таблицы: trRefs; /** * "Снять" копию HTMLCollection в виде массива (конвертировать) * т.к. [].slice.call(HTMLCollection) в IE не пашет, то приходится изгаляться. * @param {HTMLCollection} rows * @returns {Array} */ function copyRows(rows) { var copied = [], i; for (i = 0; i < rows.length; i += 1) { copied.push(rows.item(i)); } return copied; } /** * Отобразить подмножество со ссылками * @param {Number} curPage - номер текущей страницы */ function renderLinks(curPage) { // Определяем какому подмножеству принадлежит текущая страница: var curSetKey = Math.floor(curPage / pConfig.linkPerPage), template = pConfig.template, pagerHTML = "<" + pConfig.linkTag + " id=\"0\">" + (pConfig.toStart || "<<") + "</" + pConfig.linkTag + ">", setKey = 0, i; // Если мы имеем дело с первой страицей кнопку "Предыдущий" не показываем: if (curSetKey > 0) { pagerHTML += "<" + pConfig.linkTag + " id=\"" + (linksSet[curSetKey][0] - 1) + "\">" + (pConfig.toPrev || "<") + "</" + pConfig.linkTag + ">"; } for (i = 0; i < linksSet[curSetKey].length; i += 1) { setKey = linksSet[curSetKey][i]; pagerHTML += "<" + pConfig.linkTag + " id=\"" + setKey + "\"" + (setKey === curPage ? " class=\"current\"" : "") + ">" + (setKey + 1) + "</" + pConfig.linkTag + ">"; } // Если мы имеем дело с последней страицей кнопку "Следующий" не показываем: if (curSetKey < linksSet.length - 1) { pagerHTML += "<" + pConfig.linkTag + " id=\"" + (linksSet[curSetKey + 1][0]) + "\">" + (pConfig.toNext || ">") + "</" + pConfig.linkTag + ">"; } // Ссылка "в конец": pagerHTML += "<" + pConfig.linkTag + " id=\"" + (linksCnt - 1) + "\">" + (pConfig.toEnd || ">>") + "</" + pConfig.linkTag + ">"; // Обрабатываем шаблон и подменяем весь html-за один раз: linksContainer.innerHTML = template.replace(/%n/g, pagerHTML). replace(/%p/g, String(curPage+1)). replace(/%r/g, String(trRefs.length)). replace(/%c/g, linksCnt); } /** * Отобразить таблицу в заданном состоянии. * @param {Number} start - номер начальной строки * @param {Number} offset - кол-во отображаемых строк */ function renderTableState(start, offset) { var startRow = start * offset, endRow = Math.min(trRefs.length, startRow + offset), i; // Очищаем tBody от потомков (tBody.innerHTML - не сработает в IE) while (tBody.firstChild) { tBody.removeChild(tBody.firstChild); } for (i = startRow; i < endRow; i += 1) { tBody.appendChild(trRefs[i]); } renderLinks(currentPage); } /** * Инициализация подмножестве ссылок * @param {Number} all - Общее кол-во ссылок * @param {Number} offset - кол-во ссылок, отображаемых на странице * @returns {Array} */ function setupPager(all, offset) { /* Здесь мы создаем массив подмножеств ссылок: * [ [0,1,2,3], [4,5,6,7], ... [..., all - 1] ] * каждый подмассив (подмножество) имеет длину = offset * Далее в renderLinks() будем по номеру "страницы" определять в каком * подмножестве она находится: * Math.floor(start / offset) и будем просто * выводить на печать нужный чанк. */ var linksSet = [], key, i; for (i = 0; i < all; i += 1) { key = Math.floor(i / offset); if (linksSet[key] === undefined) { linksSet[key] = []; } linksSet[key].push(i); } return linksSet; } /** * Позволяет рекурсивно "слить" свойства двух объектов. Одноименные * свойства config перезатрут свойства defaults * @param {Object} defaults * @param {Object} override * @returns {Object} */ function mergeObjects(defaults, override) { var merged = {}, p; for (p in defaults) { if (defaults.hasOwnProperty(p)) { if (override.hasOwnProperty(p)) { merged[p] = override[p]; } else if (Object.prototype.toString.call(override[p]) === "[object Object]") { merged[p] = mergeObjects(defaults[p], override[p]); } else { merged[p] = defaults[p]; } } } return merged; } // Делегируем обработчик "кликов" linksContainer.onclick = function (e) { var event = e || GLOB.event, target = event.target || event.srcElement, start = 0; // Фильтруем "клики" только по элементам пагинатора if (target.tagName.toUpperCase() === pConfig.linkTag.toUpperCase() && isNaN(parseInt(target.id, 10)) === false) { start = parseInt(target.id, 10); // Если callback-ф-ция есть и она вернула true и кликнули не по текущей странице if ((pConfig.onNavClick && !pConfig.onNavClick(start)) ^ start !== currentPage) { currentPage = start; renderTableState(start, entryPerPage); } } }; // Сливаем конфиги pConfig = mergeObjects(pConfig, pagerConfig); offset = pConfig.linkPerPage; trRefs = copyRows(tBody.children); linksCnt = Math.ceil(trRefs.length / entryPerPage); // Строим массив подмножеств сылок для постранички linksSet = setupPager(linksCnt, offset); // Начальное отображение, далее вызовы функций зацикливаются renderTableState(currentPage, entryPerPage); // Отрабатываем событие "после инициализации": pConfig.onAfterInit && pConfig.onAfterInit(table); }; }(this));
Вот такая получилась постраничная навигация на JavaScript.
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.