Сортировка данных на JavaScript

Сортировка данных на JavaScript

Сортировка данных в таблице довольно часто встречающаяся задача, которую приходится решать программисту. И онлайн приложения здесь не исключение. Часто встречается так что вам необходимо отдавать пользователям на "клиент" довольно внушительный объем данных. А большие объемы влекут издержки обработки, поэтому, если вы хотите облегчить вашим пользователям работу, то нужно предпринимать меры.

Есть разные способы, что бы облегчить "на клиенте" пользователям работу с большим объемом данных, в том числе можно использовать постраничную навигацию. Здесь можно почитать о том, как сделать постраничную навигацию грамотно

  1. Не использует внешних библиотек
  2. Работает во всех основных браузерах
  3. Небольшой по размеру
  4. Простой в подключении и настройке
  5. Строки и символы сортирует по алфавиту, цифры по значению
  6. Имеет события и состояние
  7. Занимается только своим делом

Скачать скрипт для сортировки данных

Сортировка данных демо:

Для наглядности, что бы Вы смогли понять нужен ли Вам вообще обсуждаемый в данной статье скрипт сортировки я сделал небольшое демо. Обратите внимание в заголовках колонок таблицы есть значки, указывающие на возможность сортировки.

Символы▲▼ Текст▲▼ Цифры▲▼
Я Арбуз 1 2
П Стрелок 1
К Арбуз 2 3
А Фанат 2
Б Стрелок 1
Г Арбуз 4 9
Г Арбуз 4 10
Г Балбес 4 11
Г Вода 4 12
Г Груша 4 13

Подключение скрипта сортировки:

Итак, как же подключить скрипт сортировки? Во первых нужна таблица с данными, примерно такой разметки (данные таблицы взяты произвольные, но со смыслом, что бы показать, что мы можем сортировать не только цифры, но и строки и символы):

 

<table id="target">

    <thead>

        <tr>

            <td>Символы</td>

            <td>Текст</td>

            <td>Цифры</td>

        </tr>

    </thead>

    <tbody>

        <tr>

            <td>Я</td>

            <td>Арбуз 1</td>

            <td>2</td>

        </tr>

        <tr>

            <td>П</td>

            <td>Стрелок</td>

            <td>1</td>

        </tr>

        <tr>

            <td>К</td>

            <td>Арбуз 2</td>

            <td>3</td>

        </tr

        <tr>

            <td>А</td>

            <td>Фанат</td>

            <td>2</td>

        </tr

        <tr>

            <td>Б</td>

            <td>Стрелок</td>

            <td>1</td>

        </tr>

        <tr>

            <td>Г</td>

            <td>Арбуз 4</td>

            <td>9</td>

        </tr>  

        <tr>

            <td>Г</td>

            <td>Арбуз 4</td>

            <td>10</td>

        </tr>

        <tr>

            <td>Г</td>

            <td>Балбес 4</td>

            <td>11</td>

        </tr>

        <tr>

            <td>Г</td>

            <td>Вода 4</td>

            <td>12</td>

        </tr>

        <tr>

            <td>Г</td>

            <td>Груша 4</td>

            <td>13</td>

        </tr>            

    </tbody>

</table>

Есть некоторые требования к разметке самой таблицы:

Никаких rowspan, colspan и т.п. Все строки должны иметь одинаковое количество столбцов, а все столбцы должны иметь одинаковое количество строк. В таблицах подразумевающих сортировку данных это логично и не должно вызвать неудобств.

Так же обязательно наличие тега THEAD с одной строкой заголовков(см. листинг выше). К содержимому заголовков, скрипт добавит значки сортировщика. Заголовки не участвуют в сортировке.

Эти ограничения логичны и не противоречат стандартам (скорее наоборот), поэтому они не должны быть Вам помехой.

Функцию которая выполняет сортировку я назвал sortTable она принимает 3 обязательных и 2 необязательных параметра:

sortTable(elementTable, elementAsc, elementDesc, onSortAsc, onSortDesc); 

elementTable – Ссылка на html-элемент-таблицу
Ссылка на DOM-элемент, которую можно получить например через document.getElementById
elementAsc – Ссылка на html-элемент-значок прямой сортировки
Как было сказано выше скрипт автоматически добавит в каждую ячейку строки, заключённой в тег THEAD по паре значков сортировки, кликая по которым можно будет сортировать данные в прямом и обратном порядке. Через этот параметр нужно передать ссылку на значок для прямой сортировки, например элемент SPAN, содержащий знак ▲
elementDesc – Ссылка на html-элемент-значок обратной сортировки
Через этот параметр нужно передать ссылку на значок для обратной сортировки, например элемент SPAN, содержащий знак ▼
onSortAsc – (Необязательный) Ссылка на callback – функцию прямой сортировки
Эта функция будет вызвана тогда, когда произойдёт прямая сортировка, ей будет передан объект – состояние, имеющий два свойства:

 

{

    // Направление сортировки - может иметь два значения : asc или desc

    dir:"asc",

    // Индекс столбца, по которому производилась сортировка (отсчёт от 0)

    idx: 0

onSortDesc – (Необязательный) Ссылка на callback – функцию обратной сортировки
Эта функция будет вызвана тогда, когда произойдёт обратная сортировка, ей так же будет передан объект – состояние.

Ситуация, когда данные отсортированы определённым образом, по определённому столбцу и когда пользователь все-равно кликает на ту же самую сортировку – разруливается следующим образом: ничего не происходит. Событие сортировки не вызывается. Это сделано для экономии ресурсов: если сортировка произведена – незачем производить её ещё раз.

Итак давайте сначала подготовим элементы для значков сортировки. Для этого нам нужно просто создать пару элементов SPAN (их даже не обязательно помещать в древо DOM):

 

var sortAsc2  = document.createElement("SPAN"),

    sortDesc2 = document.createElement("SPAN");  

Затем, если вы ярый сторонник стандартов можно поместить значки ▲ и ▼ в эти SPAN-ы таким образом:

 

sortAsc.appendChild(document.createTextNode(String.fromCharCode(Number("9650"))));

sortDesc.appendChild(document.createTextNode(String.fromCharCode(Number("9660"))));

А тем, кто чхать хотел на все опасения : Чем плох innerHTML можно поступить проще:

 

sortAsc.innerHTML = "&#9650;";

sortDesc.innerHTML = "&#9660;";

И собственно вызываем функцию сортировщика:

sortTable(document.getElementById("target"), sortAsc2, sortDesc2 );

Это в самом простом случае. Если мы хотим вместо значков ▲ и ▼ поставить картинки, ак так же назначить обработчики событий сортировки, то читаем дальше.

Расширенное подключение скрипта сортировки:

Для расширенного подключения скрипта сортировки используем ту же разметку для таблицы, которая показана в первом примере, для значков сортировки так же используем SPAN-ы:

 

var sortAsc2  = document.createElement("SPAN"),

    sortDesc2 = document.createElement("SPAN");  

Но вместо того, чтобы помещать внутрь ▲ и ▼ мы назначим им такие классы стилей:

 

sortAsc2.className  = "asc-span";

sortDesc2.className = "desc-span";

Вот с такими правилами:

 

.asc-span, .desc-span {

    width  : 16px;  

    height : 16px;

    display: inline-block;      

    margin-left: 10px;

    background-repeat: no-repeat;

}

.asc-span{

    background: url("/images/asc_sort.jpg");

}

.desc-span{

    background: url("/images/desc_sort.jpg");

}          

Основная задача css показать картинку сортировки, как бэкграунд, ну, и задать значкам размеры. Далее передаём их в функцию:

 

sortTable(

    document.getElementById("target"),

    sortAsc2,

    sortDesc2,

    function (sortState) {

        alert("Was sorted by " + sortState.dir + " cell index:" + sortState.idx);

    },

    function (sortState) {

        alert("Was sorted by " + sortState.dir + " cell index:" + sortState.idx);

    }

);

Так же можно видеть, что 4-м и 5-м параметрами я передал в функцию ссылки на callback функции – обработчики событий сортировки.

Смотрим что получилось:

Символы Текст Цифры
Я Арбуз 1 2
П Стрелок 1
К Арбуз 2 3
А Фанат 2
Б Стрелок 1
Г Арбуз 4 9

Исходный код скрипта сортировки:

Скрипт сортировки данных использует для работы алгоритм "сортировки пузырьком" этот алгоритм не отличается выдающимися результатами, поэтому, если у вас огромное количество данных и скрипт плохо с ними справляется, то вы можете реализовать другой – более производительный алгоритм. Для этого я в листинге слелал отметки в виде комментариев в двух местах (те кто "в теме" легко узнают алгоритм в коде). Вот кстати и сам листинг:

 

var sortTable = (function (GLOB) {

    var DOC = GLOB.document;

    /**

     * @param {HTMLTable} elementTable - ссылка на таблицу

     * @param {HTMLElement} elementAsc - ссылка на html - элемент-значек для прямой сортировки

     * @param {HTMLElement} elementDesc - ссылка на html - элемент-значек для обратной сортировки

     * @param {[][]} [sortRules] - набор правил определения содержимого ячеек столбцов типа: [ ["number", 1], ["string"], ..., ["integer", 2] ]

     * @param {Function} [onSortAsc] - коллбек, вызываемый при прямой сортировке

     * @param {Function} [onSortDesc] - коллбек, вызываемый при обратной сортировке

     */

    return function (elementTable, elementAsc, elementDesc, sortRules, onSortAsc, onSortDesc) {

     

        if (!elementTable || !elementAsc || !elementDesc) {

            throw new Error ("sortTabe ERROR: Parameters 1,2,3 - is required!");               

        }       

        var rows = elementTable.tBodies[0].rows,

            cellsCnt = elementTable.rows[0].cells.length,

            sortState = {dir:"none", idx: null},

            rules = sortRules || [],

            presets = {

                integer : /\d+/g,

                number  : /(\d+(\.\d+)?)/g,

                word    : /\S+/g,

                string  : "string"

            },

            hcell, sorter, i, j;

        // озаполняем массив правил извлечения чисел

        for (i = 0; i < cellsCnt; i++) {

            (({}).toString.call(rules[i]) !== "[object Array]") && (rules[i] = ["string", 0]);

        }       

         

        /* Получить элемент управления сортировкой */

        function getSorter(idx, table, type) {         

            var sorterAsc = DOC.createElement("SPAN"),

                sorterDesc = DOC.createElement("SPAN");

                 

            sorterAsc.appendChild(elementAsc.cloneNode(true));

            sorterDesc.appendChild(elementDesc.cloneNode(true));

             

            /* Функция извлечения данных из строки */

            function extractData(str, rule, index) {

                var pattern, result;

                // Если в правиле указано "string"

                if (rule === "string") {

                    return str;

                }

                // Если в правиле пришло регулярное выражение:

                else if (({}).toString.call(rule) === "[object RegExp]") {

                    pattern = rule;

                }

                // Если в правиле не указан тип или указан неправильно:

                else if (typeof presets[rule] !== "undefined") {

                    pattern = presets[rule];

                }               

                // Если индекс не указан:

                index = index ? index : 0;               

                // Массив совпадений

                result = str.match(pattern);

                // Если есть результат и он содержит нужный элемент

                if (result && result[index]) {

                    // Это частный случай правила "word"

                    if (rule == "word") return result[index];

                    return new Number(result[index]);

                }

                return str;

            }

             

            /* Вынесем операции сравнения в функции.

                Это позволит оптимизировать код обработчика кликов*/

            function gt(a, b) {return (a > b);}

            function lt(a, b) {return (a < b);}     

             

            /* Функция кроссбраузерно меняет местами узлы DOM */

            function swapNode(node1, node2) {

                var next = node1.nextSibling,

                    parentNode = node1.parentNode;                 

                if (node1.swapNode) {

                    return node1.swapNode(node2);

                }              

                node2.parentNode.replaceChild(node1, node2);

                parentNode.insertBefore(node2, next);

            }

            /* Это обработчик кликов по элементам управления сортировщика */

            function clickHandler(compareFunc, sortDir, callback) {

                /* Если колонка уже отсортирована в нужном направлении - на выход */

                if (sortState.dir === sortDir && sortState.idx === idx) {

                    return false;

                }              

                /* Пузырьковая сортировка */

                for (i = 0; i < rows.length - 1; i += 1) {

                    for (j = i + 1; j < rows.length; j += 1) {

                        compareFunc(

                            extractData(rows[i].cells[idx].innerText, rules[idx][0], rules[idx][1]),

                            extractData(rows[j].cells[idx].innerText, rules[idx][0], rules[idx][1])

                        ) && swapNode(rows[i], rows[j]);

                    }

                }

                /* Пузырьковая сортировка конец */

                 

                /* Установка состояния "Отсортировано" (направление и колонка) */

                sortState.dir = sortDir;

                sortState.idx = idx;

                 

                /* Вызываем обработчик, если он назначен */

                callback && callback(sortState);

            }

             

            /* Сортировка в прямом порядке */

            sorterAsc.onclick   = function () {

                clickHandler(lt, "asc", onSortAsc);

            };

            /* Сортировка в обратном порядке */

            sorterDesc.onclick  = function () {

                clickHandler(gt, "desc", onSortDesc);

            };

             

            return {asc : sorterAsc, desc : sorterDesc};

        }

        /* Пройдёмся по всем ячейкам первой строки и вставим в них значки-сортировщика: */

        for (i = 0; i < cellsCnt; i += 1) {

            sorter = getSorter(i, elementTable);

            hcell  = elementTable.rows[0].cells[i];

             

            hcell.appendChild(sorter.asc);

            hcell.appendChild(sorter.desc);

        }

        return;

    };

}(this));

Надеюсь Вам пригодится мой скрипт сортировки данных и Ваши пользователи по достоинству его оценят – удачных разработок друзья

 

Возможно Вас заинтересуют эти материалы

Javascript выпадающий список

Javascript выпадающий список

Часто на форумах втречаются вопросы как сделать динамический Javascript выпадающий список? Да ещё с возможностью
Javascript динамическая html таблица

Javascript динамическая html таблица

Понадобилось мне в одном проекте на днях сделать html таблицу, которую бы выводил серверный php
Свои события или observer на Javascript

Свои события или observer на Javascript

В этой статье, я по сути "убил двух зайцев" - Написал интересный и полезный Javascript