Javascript постраничная навигация таблицы

Javascript постраничная навигация таблицы

Постраничная навигация спасает нас когда мы имеем большой объем данных в ограниченном интерфейсе. Иногда так случается, что по независящим от нас причинам мы имеем на "клиенте" таблицы данных большого размера. Это бывает при работе с веб-сервисами например, или по какой другой причине, когда мы не можем реализовать механизм постраничной навигации на стороне сервера.

Кстати, вот здесь рассказывается как сделать постраничную навигацию Но что если повлиять на вывод данных сервером мы не можем? В общем не знаю как у вас, а у меня такое пару раз было. И тогда я задумался, что было бы не плохо сделать постраничную навигацию на JavaScript. Задумался... и сделалВ общем добавить тут особо нечего предлагаю посмотреть демку постраничной навигации на JavaScript в контексте таблиц:

Cell 1 Cell 2 Cell 3
1Click meSome content more ...
2Some contentSome content more ...
3Some contentSome content more ...
4Some contentSome content more ...
5Click meSome content more ...
6Some contentSome content more ...
7Some contentSome content more ...
8Some contentSome content more ...
9Some contentSome content more ...
10Some contentSome content more ...
11Some contentSome content more ...
12Click meSome content more ...
13Some contentSome content more ...
14Some contentSome content more ...
15Some contentSome content more ...
16Some contentSome content more ...
17Some contentSome content more ...
18Some contentSome content more ...
12Click meSome content more ...
20Some contentSome content more ...
21Some contentSome content more ...
22Some contentSome content more ...
23Some contentSome content more ...
24Some contentSome content more ...
25Some contentSome content more ...
26Some contentSome content more ...
27Some contentSome content more ...
12Click meSome content more ...
29Some contentSome content more ...
30Some contentSome content more ...
31Some contentSome content more ...
32Some contentSome content more ...
33Some contentSome content more ...
34Some contentSome content more ...
35Some contentSome 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: "&lt;&lt;",
    /**
     * Текст ссылки "предыдущий"
     * @type {string}
     */
    toPrev: "&lt;&lt;",
    /**
     * текст ссылки "следующий"
     * @type {string}
     */
    toNext: "&gt;",
    /**
     * текст ссылки "в конец"
     * @type {string}
     */
    toEnd: "&gt;&gt;",
    /**
     * кол-во ссылок, отображаемых на "странице"
     * @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: "&lt;&lt;",
                toPrev: "&lt;&lt;",
                toNext: "&gt;",
                toEnd: "&gt;&gt;",
                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 || "&lt;&lt;") + "</" + pConfig.linkTag + ">",
                setKey    = 0,
                i;
            // Если мы имеем дело с первой страицей кнопку "Предыдущий" не показываем:
            if (curSetKey > 0) {
                pagerHTML += "<" + pConfig.linkTag + " id=\"" + (linksSet[curSetKey][0] - 1) + "\">" + (pConfig.toPrev || "&lt;") + "</" + 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 || "&gt;") + "</" + pConfig.linkTag + ">";
            }
            // Ссылка "в конец":
            pagerHTML += "<" + pConfig.linkTag + " id=\"" + (linksCnt - 1) + "\">" + (pConfig.toEnd || "&gt;&gt;") + "</" + 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.

Добавить комментарий


Защитный код
Обновить



Кто на сайте
Сейчас 174 гостей онлайн