Сортировка данных на JavaScript
Сортировка данных в таблице довольно часто встречающаяся задача, которую приходится решать программисту. И онлайн приложения здесь не исключение. Часто встречается так что вам необходимо отдавать пользователям на "клиент" довольно внушительный объем данных. А большие объемы влекут издержки обработки, поэтому, если вы хотите облегчить вашим пользователям работу, то нужно предпринимать меры.
Есть разные способы, что бы облегчить "на клиенте" пользователям работу с большим объемом данных, в том числе можно использовать постраничную навигацию. Здесь можно почитать о том, как сделать постраничную навигацию грамотно
- Не использует внешних библиотек
- Работает во всех основных браузерах
- Небольшой по размеру
- Простой в подключении и настройке
- Строки и символы сортирует по алфавиту, цифры по значению
- Имеет события и состояние
- Занимается только своим делом
Скачать скрипт для сортировки данных
Сортировка данных демо:
Для наглядности, что бы Вы смогли понять нужен ли Вам вообще обсуждаемый в данной статье скрипт сортировки я сделал небольшое демо. Обратите внимание в заголовках колонок таблицы есть значки, указывающие на возможность сортировки.
Символы | Текст | Цифры |
Я | Арбуз 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 = "▲"; sortDesc.innerHTML = "▼";
И собственно вызываем функцию сортировщика:
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));
Надеюсь Вам пригодится мой скрипт сортировки данных и Ваши пользователи по достоинству его оценят - удачных разработок друзья
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.