Скрипт для сортировки значений в таблице HTML

Сделать таблицу сортируемой при клике по заголовку колонки легко, просто добавьте атрибут data-sort="sort_table" в тег table, а так же добавьте готовый скрипт.

JavaScript

  Как реализовать сортировку данных в HTML-таблице на сайте.

  Задача: сделать так, чтобы в таблице при клике по ячейке таблицы с названием колонки, происходила сортировка данных по возрастанию и убыванию.

  Вполне решаемая задача, для реализации которой нам понадобится JavaScript.

 


Сортировка в таблице

Демо сортировки таблицы:

  Покликайте по заголовкам колонок:

id Имя Страна Баланс
1 Владимир Россия 147
2 Александр Белорусь 0
3 Lisa Англия 65
4 Мутумба Зимбабве -43
5 Donald США 350
6 Angela Германия 350.2

 

Код HTML

<table class="table_sort">
    <thead>
        <tr>
            <th>id</th>
            <th>Имя</th>
            <th>Страна</th>
            <th>Баланс</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>Владимир</td>
            <td>Россия</td>
            <td>147</td>
        </tr>
        <tr>
            <td>2</td>
            <td>Александр</td>
            <td>Белорусь</td>
            <td>0</td>
        </tr>
        <tr>
            <td>3</td>
            <td>Lisa</td>
            <td>Англия</td>
            <td>65</td>
        </tr>
        <tr>
            <td>4</td>
            <td>Мутумба</td>
            <td>Зимбабве</td>
            <td>-43</td>
        </tr>
        <tr>
            <td>5</td>
            <td>Donald</td>
            <td>США</td>
            <td>350</td>
        </tr>
        <tr>
            <td>6</td>
            <td>Angela</td>
            <td>Германия</td>
            <td>37</td>
        </tr>
    </tbody>
</table>

  Обратите внимание, что у таблицы в открывающем теге table стоит class="table_sort". Именно этот класс для js-скрипта является индикатором того, что таблица должна быть сортируемой.

  Таблица имеет структуру по HTML 5. В её структуре обязательно должны быть thead и tbody. Присутствие в структуре таблице tfoot необязательно, но возможно. tfoot не сломает сортировку таблицы.

Javascript

  Скрипт сортирующий таблицу:

document.addEventListener('DOMContentLoaded', () => {

    const getSort = ({ target }) => {
        const order = (target.dataset.order = -(target.dataset.order || -1));
        const index = [...target.parentNode.cells].indexOf(target);
        const collator = new Intl.Collator(['en', 'ru'], { numeric: true });
        const comparator = (index, order) => (a, b) => order * collator.compare(
            a.children[index].innerHTML,
            b.children[index].innerHTML
        );
        
        for(const tBody of target.closest('table').tBodies)
            tBody.append(...[...tBody.rows].sort(comparator(index, order)));

        for(const cell of target.parentNode.cells)
            cell.classList.toggle('sorted', cell === target);
    };
    
    document.querySelectorAll('.table_sort thead').forEach(tableTH => tableTH.addEventListener('click', () => getSort(event)));
    
});

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

  Повторю, что скрипт находит только те таблицы, у которых стоит class="table_sort".

  Если данный скрит вставляете в HTML, через виджеты или модули, то не забудьте его обернуть тегами script

<script>тут код скрипта</script>

 

Стили CSS

  Так же выкладываю стили, которые я написал для демо-таблицы. Их можно взять за основу и подправить или дополнить под ваши нужды:

.table_sort table {
    border-collapse: collapse;
}

.table_sort th {
    color: #ffebcd;
    background: #008b8b;
    cursor: pointer;
}

.table_sort td,
.table_sort th {
    width: 150px;
    height: 40px;
    text-align: center;
    border: 2px solid #846868;
}

.table_sort tbody tr:nth-child(even) {
    background: #e3e3e3;
}

th.sorted[data-order="1"],
th.sorted[data-order="-1"] {
    position: relative;
}

th.sorted[data-order="1"]::after,
th.sorted[data-order="-1"]::after {
    right: 8px;
    position: absolute;
}

th.sorted[data-order="-1"]::after {
	content: "▼"
}

th.sorted[data-order="1"]::after {
	content: "▲"
}

 

Записи по теме
Иконочный шрифт в Joomla 3
иконки Иконочный шрифт Icomoon, который используется в Joomla 3. Таблица иконок с названиями их классов и примеры использования иконок в HTML.
Таблица с липкой шапкой
Таблица с липкой шапкой Инструкция как сделать липкую шапку таблицы без использования JavaScript и без jQuery. Фиксированные заголовки таблицы сделанные на чистом CSS.

 

 

Комментарии 29
15:15 27.10.2019 #

Большое спасибо за отличный способ! Я заметила, что если в ячейке заголовка столбца - две строки, то треугольник (наверх или вниз) выравнивается по вертикали относительно нижней строки, а очень хотелось бы, чтобы выравнивание по вертикали было по центру ячейки. Подскажите, пожалуйста, как реализовать выравнивание треугольника по центру ячейки (по вертикали).

Спасибо!

15:32 27.10.2019 #

В моём примере у этого треугольничка абсолютное позиционирование. Собственно, его двигать можно при помощи следующих css-свойств: top, right, bottom и left.

Вот к этому фрагменту css:

th.sorted[data-order="1"]::after,th.sorted[data-order="-1"]::after {
    right: 8px;
    position: absolute;
}

допишите свойство top и подберите значение в пикселях. Получится примерно так:

th.sorted[data-order="1"]::after,th.sorted[data-order="-1"]::after {
    top: 15px;
    right: 8px;
    position: absolute;
}
17:43 27.10.2019 #

Большое спасибо!

К сожалению, если в нескольких разных таблицах в строке thead - разное количество текстовых строчек (например, в первой таблице заголовки столбцов состоят из одного слова; а во второй таблице в заголовках столбцов много слов, поэтому получается 2-3 строки), то настройки, подходящие для невысоких строк (напр., top: 1px;) не будут подходить для более высоких строк (там, например, нужно было бы: top: 15px;).

Подскажите, пожалуйста, может быть, есть универсальный способ, чтобы вертикальное положение треугольничка было одинаковым, несмотря на то, сколько текстовых строчек в строке заголовка? Спасибо!

22:37 27.10.2019 #

Подскажите, пожалуйста, может быть, есть универсальный способ, чтобы вертикальное положение треугольничка было одинаковым, несмотря на то, сколько текстовых строчек в строке заголовка?

Чтобы в такой ситуации отцентрировать, можно top в процентах указать, высчитывая откат на смещение через calc:

top: calc(50% - 8px);

где 8px - это значение половины высоты шрифта, то есть значение font-size делим на 2. Если у меня размер шрифта 16px - получается в calc берём 8px.

Или более универсально, чтобы от размера шрифта не зависеть, можно сделать так:

top: 50%;
transform: translate(0, -50%);
11:17 28.10.2019 #

Или более универсально, чтобы от размера шрифта не зависеть, можно сделать так:

top: 50%;
transform: translate(0, -50%);

Большое спасибо! Попробовала этот способ, всё отлично работает!

13:54 23.12.2019 #

Sos скрипт не работает в мазилле. Что делать?

14:11 23.12.2019 #

Я в мазиле вот открыл и у меня демка на этой странице работает. Может у вас мазила давно не обновлялась? Типа стоит версия браузера которая не поддерживает ES6.

Можно попробовать скрипт через бабель прогнать чтобы в старых браузерах работало.

Попробуйте так:

"use strict";

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }

document.addEventListener('DOMContentLoaded', function () {
  var getSort = function getSort(_ref) {
    var target = _ref.target;
    var order = target.dataset.order = -(target.dataset.order || -1);

    var index = _toConsumableArray(target.parentNode.cells).indexOf(target);

    var collator = new Intl.Collator(['en', 'ru'], {
      numeric: true
    });

    var comparator = function comparator(index, order) {
      return function (a, b) {
        return order * collator.compare(a.children[index].innerHTML, b.children[index].innerHTML);
      };
    };

    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      for (var _iterator = target.closest('table').tBodies[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var tBody = _step.value;
        tBody.append.apply(tBody, _toConsumableArray(_toConsumableArray(tBody.rows).sort(comparator(index, order))));
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return != null) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }

    var _iteratorNormalCompletion2 = true;
    var _didIteratorError2 = false;
    var _iteratorError2 = undefined;

    try {
      for (var _iterator2 = target.parentNode.cells[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
        var cell = _step2.value;
        cell.classList.toggle('sorted', cell === target);
      }
    } catch (err) {
      _didIteratorError2 = true;
      _iteratorError2 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
          _iterator2.return();
        }
      } finally {
        if (_didIteratorError2) {
          throw _iteratorError2;
        }
      }
    }
  };

  document.querySelectorAll('.table_sort thead').forEach(function (tableTH) {
    return tableTH.addEventListener('click', function () {
      return getSort(event);
    });
  });
});

Я это в онлайновом инструменте на сайте бабеля перекомпилировал. На работоспособность не проверял. Если сработает, то могу скрипт у себя локально в гальпе прогнать через бабель и минификатор, чтобы по весу не растягивать скрипт.

20:52 25.12.2019 #

Здравствуйте, спасибо вам за интересный, нативный способ сортировки для таблиц.  Единственное о чем хотелось бы вас попросить оставляйте пожалуйста комментарии для кода. Попытался самостоятельно разобраться, все вроде понял, но сам бы на вряд ли додумался) Возможно, тем кто захочет разобраться будет полезно. https://codepen.io/Sergei_Sergeevu4/pen/OJPgOao?editors=0010

p.s Белорусь -> Беларусь

16:30 29.01.2020 #

Вы нереально круты. Огромное спасибо за классный способ сортировки. С уважением и признательностью, Андрей К.

22:01 11.02.2020 #

Отличный скрипт! А как можно сделать, чтобы по-умолчанию сортировало в обратном порядке?

22:03 11.02.2020 #

Сортировку по умолчанию задаёте вы когда заполняете таблицу.

23:04 11.02.2020 #

Я имею в виду при клике чтобы от Я до А была сортировка. А то два клика делать неудобно каждый раз.

23:25 11.02.2020 #

Я понял. Может вам нужен скрипт попроще чтобы только по одной колонке сортировал?

А в данном скрипте с логикой всё правильно. Он сортирует по любой из колонок таблицы по алфавиту и по числам. В начале по порядку, потом в обратном порядке.

Возможно у вас какой то индувидуальный случай, где нужно сортировать по числам от большего к меньшему и не по всем колонкам а по каким то отдельным. Тогда вам нужно писать уникальный скрипт.

А у меня в данном случае универсальный скрипт для общего применения.

23:44 11.02.2020 #

Спасибо за быстрый ответ. Попробую поискать подобный скрипт.

09:08 18.02.2020 #

Добрый день,как можно добавить сохранение состояние сортировки (убывание,увеличение) между страниц при пагинации?

09:23 18.02.2020 #

Я так понимаю это список записей выводится в табличном виде.

При помощи js можно записывать данные в память браузера. Свойство называется localStorage. Но тут плохо тем что будет перерисовка (сортировка) после загрузки дома страницы.

Я может не совсем правильно понял задачу, но по моему тут лучше делать сортировку на сервере, чтобы данные приходили уже в отсортированном виде.

12:31 04.03.2020 #

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

12:34 04.03.2020 #

float это вы имеете в виду числа с плавающей точкой?

Может у вас запятая проставлена в этих числах, а не точка?

10:20 12.03.2020 #

а как отсортировать допустим все кроме первого столбца? первый столбец будет указывать просто номер строки? то есть если мы сортируем по другим значениям, то значения в первом столбце с id не сортируются

11:28 12.03.2020 #

Самое простое решение, можно после сортировки взять и переписать индексы первого столбца:

document.querySelectorAll('.table_sort tbody tr td:first-child').forEach((el, i) => el.textContent = i + 1);

Я из консоли браузера попробовал, вроде всё правильно.

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

document.addEventListener('DOMContentLoaded', () => {

    const getSort = ({ target }) => {
        const order = (target.dataset.order = -(target.dataset.order || -1));
        const index = [...target.parentNode.cells].indexOf(target);
        const collator = new Intl.Collator(['en', 'ru'], { numeric: true });
        const comparator = (index, order) => (a, b) => order * collator.compare(
            a.children[index].innerHTML,
            b.children[index].innerHTML
        );

        for(const tBody of target.closest('table').tBodies)
            tBody.append(...[...tBody.rows].sort(comparator(index, order)));

        for(const cell of target.parentNode.cells)
            cell.classList.toggle('sorted', cell === target);

        document.querySelectorAll('.table_sort tbody tr td:first-child').forEach((el, i) => el.textContent = i + 1);
    };

    document.querySelectorAll('.table_sort thead').forEach(tableTH => tableTH.addEventListener('click', () => getSort(event)));

});
10:42 12.03.2020 #

Отличный скрипт, все работает. но подскажите пожалуйста, как сделать что бы изначально в первой колонке (в случае с примером - в ячейке "id") отображалась стрелочка вниз, типо что бы пользователю было наглядно, что есть сортировка и изначальна она по первой колонке?)

10:52 12.03.2020 #

Чтобы такое сделать, нужно для этой ячейки в шапке таблицы в хтмле прописать class="sorted" и дата-атрибут data-order="-1". Должно получиться так:

<th data-order="-1" class="sorted">id</th>

Это в css-стилях прописано.

Если data-order="-1" то будет ▼

А если data-order="1" то ▲

12:36 23.03.2020 #

Скрипт работает великолепно, спасибо! Единственное, подскажите, пожалуйста, можно ли как-то "выкинуть" последнюю строчку из сортировки? Дело в том, что в ней у меня объединены все ячейки и с таким условием сортировка не работает =(

12:42 23.03.2020 #

Так вы в такой ситуации делайте футер таблицы.

Просто эту последнюю строку прописывайте не в <tbody> а в <tfoot>

14:41 23.03.2020 #

И правда, спасибо огромнейшее!

10:53 05.04.2020 #

Здравствуйте! Хочу поблагодарить за скрипт! Часто его использую, он великолепен))

Только возникла проблема в ie11, при получении индекса он вылетает с ошибкой неитерируемый объект.(через babel скрипт прогнал) Думаю ошибка в том что в ie11 для querySelectorAll не работает forEach и я переписал [].forEach.call().

Не могу разобраться как пофиксить

11:52 05.04.2020 #

Если честно, я даже не знаю как работает IE и никаких полифилов для него никогда не писал.

Я игнорирую браузер IE и само его существование. Что в css, что в js я его просто игнорирую. Считаю что у кого IE или оська Android ниже 4.4 пусть меняют свои устройства. Я использую флексы и svg.

Но тут ещё такой момент, что это всё должен делать бабель. Там как и у автопрефексера можно задать в настройках поддержку браузеров. Вот посмотрите здесь https://babeljs.io/docs/en/babel-preset-env#options есть такой пример:

{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}
11:52 05.04.2020 #

Оказалось у ie11 HTMLCollection не имеют метода перебора, поэтому for in не работал, переписал под тот же самый forEach)

[].forEach.call(target.closest('table').tBodies, function (tBody) {
    tBody.append.apply(tBody, _toConsumableArray(_toConsumableArray(tBody.rows).sort(comparator(index, order))));
  });

[].forEach.call(target.parentNode.cells, function (cell) {
  cell.classList.toggle('sorted', cell === target);
});
09:35 22.04.2020 #

Респект автору! Один из самых не капризных и простых скриптов! Удачи в жизни, в самоизоляции и без неё!


*** чтобы писать комментарии.