Сортировка данных на 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));
    

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

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


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



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