Сортировка данных на 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));
Надеюсь Вам пригодится мой скрипт сортировки данных и Ваши пользователи по достоинству его оценят - удачных разработок друзья
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.
Сортировка данных на JavaScript