Таблица с сортировкой столбцов на js

5 (100%) 4 vote[s]

Таблицы используются для удобного хранения, редактирования, представления данных. На странице сайта чаще всего таблица — самый простой и быстрый способ (альтернатива графикам) представить (визуализировать) какие-либо данные. Если таблица небольшая — навигация по ней не представляет сложностей. Но если объем данных большой, то для понимания её содержимого и оценки данных может потребоваться некоторое время. Для удобства просмотра таких объемных данных нужна таблица с сортировкой столбцов. А если в таблице представлены данные стоимости чего-либо, размеры, коды, даты и т.д., то такая возможность сортировки просто необходима для пользователя.



Таблица с сортировкой столбцов — пример

Для примера сделаем таблицу с названиями некоторых основных валют с их цифровым и буквенным кодом (по ISO 4217) с сортировкой столбцов и разберем, как это работает. Клик по заголовкам таблицы будет запускать сортировку по этому параметру. Пример кода демонстрирует сортировку столбцов, содержащих типы данных number и string.

Currency Буквенный Числовой Валюта
Australian Dollar AUD 036 Австралийский доллар
Austrian SchillingATS040Австрийский шиллинг
Belgian FrancBEF056Бельгийский франк
British PoundGBP826Британский фунт
Canadian DollarCAD124Канадский доллар
Czech KorunaCZK203Чешская крона
Danish KroneDKK208Датская крона
Dutch GuilderNLG528Нидерландский гульден
Estonian KroonEEK233Эстонская крона
EuroEUR978Единая европейская валюта
Finnish MarkFIM246Финская марка
French FrancFRF250Французский франк
German MarkDEM276Немецкая марка
Greek DrachmaGRD300Греческая драхма
Hong Kong DollarHKD344Гонконгский доллар
Hungarian ForintHUF348Венгерский форинт
Irish PuntIEP372Ирландский фунт
Italian LiraITL380Итальянская лира
Japanese YenJPY392Японская йена
Latvian LatLVL428Латвийский лат
Lithuanian LitaLTL440Литовский лит
Mexican PesoMXN484Мексиканский песо
New Zealand DollarNZD554Новозеландский доллар
Norway KroneNOK578Норвежская крона
Polish ZlotyPLN985Польский злотый
Portuguese EscudoPTE620Португальское эскудо
Russian RoubleRUB643Российский рубль
Singapore DollarSGD702Сингапурский доллар
Slovak KorunaSKK703Словацкая крона
South African RandZAR710Южноафриканский ранд
Spanish PesetaESP724Испанская песета
Swedish KronaSEK752Шведская крона
Swiss FrancCHF756Швейцарский франк
Ukraine HryvniaUAH980Украинская гривна
United States DollarUSD840Американский доллар

Сортировка строк

Создадим таблицу с id="sortable", обернем её в <div>. В таблице должен присутствовать заголовок (<thead>) и тело (<tbody>):

<div class="blok">
    <table id="sortable">
         <thead>
             <tr>
              <th>Currency</th>
              <th>Буквенный</th>
              <th data-type="number">Числовой</th>
              <th>Валюта</th>
              </tr>
         </thead>
         <tbody>
			 <tr>
			  <td>Australian Dollar</td>
			  <td>AUD</td>
			  <td>036</td>
			  <td>Австралийский доллар</td>
			 </tr>
			<tr>
			 <td>Austrian Schilling</td>
			 <td>ATS</td>
			 <td>040</td>
			 <td>Австрийский шиллинг</td>
			</tr>
			<tr>
			 <td>Belgian Franc</td>
			 <td>BEF</td>
			 <td>056</td>
			 <td>Бельгийский франк</td>
			</tr>
			<tr>
			 <td>British Pound</td>
			 <td>GBP</td>
			 <td>826</td>
			 <td>Британский фунт</td>
			</tr>
			<tr>
			 <td>Canadian Dollar</td>
			 <td>CAD</td>
			 <td>124</td>
			 <td>Канадский доллар</td>
			</tr>
			<tr>
			 <td>Czech Koruna</td>
			 <td>CZK</td>
			 <td>203</td>
			 <td>Чешская крона</td>
			</tr>
			<tr>
			 <td>Danish Krone</td>
			 <td>DKK</td>
			 <td>208</td>
			 <td>Датская крона</td>
			</tr>
			<tr>
			 <td>Dutch Guilder</td>
			 <td>NLG</td>
			 <td>528</td>
			 <td>Нидерландский гульден</td>
			</tr>
			<tr>
			 <td>Estonian Kroon</td>
			 <td>EEK</td>
			 <td>233</td>
			 <td>Эстонская крона</td>
			</tr>
			<tr>
			 <td>Euro</td>
			 <td>EUR</td>
			 <td>978</td>
			 <td>Единая европейская валюта</td>
			</tr>
			<tr>
			 <td>Finnish Mark</td>
			 <td>FIM</td>
			 <td>246</td>
			 <td>Финская марка</td>
			</tr>
			<tr>
			 <td>French Franc</td>
			 <td>FRF</td>
			 <td>250</td>
			 <td>Французский франк</td>
			</tr>
			<tr>
			 <td>German Mark</td>
			 <td>DEM</td>
			 <td>276</td>
			 <td>Немецкая марка</td>
			</tr>
			<tr>
			 <td>Greek Drachma</td>
			 <td>GRD</td>
			 <td>300</td>
			 <td>Греческая драхма</td>
			</tr>
			<tr>
			 <td>Hong Kong Dollar</td>
			 <td>HKD</td>
			 <td>344</td>
			 <td>Гонконгский доллар</td>
			</tr>
			<tr>
			 <td>Hungarian Forint</td>
			 <td>HUF</td>
			 <td>348</td>
			 <td>Венгерский форинт</td>
			</tr>
			 <tr>
			 <td>Irish Punt</td>
			 <td>IEP</td>
			 <td>372</td>
			<td>Ирландский фунт</td>
			</tr>
			<tr>
			 <td>Italian Lira</td>
			 <td>ITL</td>
			 <td>380</td>
			 <td>Итальянская лира</td>
			</tr>
			<tr>
			 <td>Japanese Yen</td>
			 <td>JPY</td>
			 <td>392</td>
			 <td>Японская йена</td>
			</tr>
			<tr>
			 <td>Latvian Lat</td>
			 <td>LVL</td>
			 <td>428</td>
			 <td>Латвийский лат</td>
			</tr>
			<tr>
			 <td>Lithuanian Lita</td>
			 <td>LTL</td>
			 <td>440</td>
			 <td>Литовский лит</td>
			</tr>
			<tr>
			 <td>Mexican Peso</td>
			 <td>MXN</td>
			 <td>484</td>
			 <td>Мексиканский песо</td>
			</tr>
			<tr>
			 <td>New Zealand Dollar</td>
			 <td>NZD</td>
			 <td>554</td>
			 <td>Новозеландский доллар</td>
			</tr>
			<tr>
			 <td>Norway Krone</td>
			 <td>NOK</td>
			 <td>578</td>
			 <td>Норвежская крона</td>
			</tr>
			<tr>
			 <td>Polish Zloty</td>
			 <td>PLN</td>
			 <td>985</td>
			 <td>Польский злотый</td>
			</tr>
			<tr>
			 <td>Portuguese Escudo</td>
			 <td>PTE</td>
			 <td>620</td>
			 <td>Португальское эскудо</td>
			</tr>
			<tr>
			 <td>Russian Rouble</td>
			 <td>RUB</td>
			 <td>643</td>
			 <td>Российский рубль</td>
			</tr>
			<tr>
			 <td>Singapore Dollar</td>
			 <td>SGD</td>
			 <td>702</td>
			 <td>Сингапурский доллар</td>
			</tr>
			<tr>
			 <td>Slovak Koruna</td>
			 <td>SKK</td>
			 <td>703</td>
			 <td>Словацкая крона</td>
			</tr>
			<tr>
			 <td>South African Rand</td>
			 <td>ZAR</td>
			 <td>710</td>
		     <td>Южноафриканский ранд</td>
			</tr>
			<tr>
			 <td>Spanish Peseta</td>
			 <td>ESP</td>
			 <td>724</td>
			 <td>Испанская песета</td>
		 	</tr>
			<tr>
			 <td>Swedish Krona</td>
			 <td>SEK</td>
			 <td>752</td>
			 <td>Шведская крона</td>
			</tr>
			<tr>
			 <td>Swiss Franc</td>
			 <td>CHF</td>
			 <td>756</td>
			 <td>Швейцарский франк</td>
			</tr>
			 <tr>
			 <td>Ukraine Hryvnia</td>
			 <td>UAH</td>
			 <td>980</td>
			<td>Украинская гривна</td>
			</tr><tr>
			 <td>United States Dollar</td>
			 <td>USD</td>
			 <td>840</td>
			 <td>Американский доллар</td>
			</tr>
		</tbody>
	</table>
</div>

Прежде всего, нам нужно получить все заголовки, затем при помощи цикла (используя метод forEach) присоединяем к каждому из них обработчик события для click:

// Запрашиваем таблицу
const table = document.getElementById('sortable');

// Получить заголовки
const headers = table.querySelectorAll('th');

// Обойти циклом все заголовки
[].forEach.call(headers, function(header, index) {
    header.addEventListener('click', function() {
        // Эта функция будет выполнять сортировку
        sortColumn(index);
    });
});

Упомянутая выше функция sortColumn (index) сортирует все строки по заданному индексу столбца. Для этого:

    • Используем метод sort() массива для сортировки текущих строк.
    • Затем удаляем все текущие строки.
    • Добавляем отсортированные строки.
//Запросить все строки
const tableBody = table.querySelector('tbody');
const rows = tableBody.querySelectorAll('tr');

const sortColumn = function(index) {
    // Клонируем все строки
    const newRows = Array.from(rows);

    // Сортируем строки по содержимому ячеек. Массив предоставляет встроенный метод сортировки, который принимает функцию для сравнения двух элементов. Здесь две ячейки столбца сравниваются на основе их HTML-содержимого
    newRows.sort(function(rowA, rowB) {
        // Получаем содержимое ячеек
        const cellA = rowA.querySelectorAll('td')[index].innerHTML;
        const cellB = rowB.querySelectorAll('td')[index].innerHTML;

        switch (true) {
            case cellA > cellB: return 1;
            case cellA < cellB: return -1;
            case cellA === cellB: return 0;
        }
    });

    // Удаляем старые строки
    [].forEach.call(rows, function(row) {
        tableBody.removeChild(row);
    });

    // Добавляем новую строку
    newRows.forEach(function(newRow) {
        tableBody.appendChild(newRow);
    });
};

Поддержка других типов

Чтобы работать с другими типами, нужно добавить настраиваемый атрибут к каждому заголовку, чтобы указать тип его ячеекю В нашем случае мы добавили к столбцу с цифровыми кодами валют тип data-type="number":

         <thead>
             <tr>
              <th>Currency</th>
              <th>Буквенный</th>
              <th data-type="number">Числовой</th>
              <th>Валюта</th>
              </tr>
         </thead>

Если атрибут отсутствует, типы содержимого ячеек являются строковыми. Создадим функцию для преобразования содержимого ячеек из строкового в другой тип (в нашем случае number), используя инструкцию switch:

// Преобразовать содержимое данной ячейки в заданном столбце
const transform = function(index, content) {
    // Получаем тип данных столбца
    const type = headers[index].getAttribute('data-type');
    switch (type) {
        case 'number':
            return parseFloat(content);
        case 'string':
        default:
            return content;
    }
};

Теперь вместо сравнения необработанного контента мы сравниваем значения, которые конвертируются в зависимости от типа контента:

newRows.sort(function(rowA, rowB) {
    const cellA = rowA.querySelectorAll('td')[index].innerHTML;
    const cellB = rowB.querySelectorAll('td')[index].innerHTML;

    // Преобразуем содержимое ячеек
    const a = transform(index, cellA);
    const b = transform(index, cellB);    

    // И сравним их
    switch (true) {
        case a > b: return 1;
        case a < b: return -1;
        case a === b: return 0;
    }
});

Поддержка обоих направлений

Для этого мы подготавливаем переменную для управления направлениями сортировки всех заголовков:

// Направление сортировки
const directions = Array.from(headers).map(function(header) {
    return '';
});

Направления — это массив, каждый элемент которого может иметь вид asc или desc, указывающий направление сортировки в соответствующем столбце. Функция sortColumn () теперь включает больше логики для сравнения двух строк в зависимости от текущего направления:

const sortColumn = function(index) {
    // Получить текущее направление
    const direction = directions[index] || 'asc';

    // Фактор по направлению
    const multiplier = (direction === 'asc') ? 1 : -1;

    ...

    newRows.sort(function(rowA, rowB) {
        const cellA = rowA.querySelectorAll('td')[index].innerHTML;
        const cellB = rowB.querySelectorAll('td')[index].innerHTML;

        const a = transform(index, cellA);
        const b = transform(index, cellB);    

        switch (true) {
            case a > b: return 1 * multiplier;
            case a < b: return -1 * multiplier;
            case a === b: return 0;
        }
    });

    ...

    // Поменять направление
    directions[index] = direction === 'asc' ? 'desc' : 'asc';

    ...
};

Вот полный код для нашей таблицы JS:

document.addEventListener('DOMContentLoaded', function() {
    const table = document.getElementById('sortable');
    const headers = table.querySelectorAll('th');
    const tableBody = table.querySelector('tbody');
    const rows = tableBody.querySelectorAll('tr');

    // Направление сортировки
    const directions = Array.from(headers).map(function(header) {
        return '';
    });

    // Преобразовать содержимое данной ячейки в заданном столбце
    const transform = function(index, content) {
        // Получить тип данных столбца
        const type = headers[index].getAttribute('data-type');
        switch (type) {
            case 'number':
                return parseFloat(content);
            case 'string':
            default:
                return content;
        }
    };

    const sortColumn = function(index) {
        // Получить текущее направление
        const direction = directions[index] || 'asc';

        // Фактор по направлению
        const multiplier = (direction === 'asc') ? 1 : -1;

        const newRows = Array.from(rows);

        newRows.sort(function(rowA, rowB) {
            const cellA = rowA.querySelectorAll('td')[index].innerHTML;
            const cellB = rowB.querySelectorAll('td')[index].innerHTML;

            const a = transform(index, cellA);
            const b = transform(index, cellB);    

            switch (true) {
                case a > b: return 1 * multiplier;
                case a < b: return -1 * multiplier;
                case a === b: return 0;
            }
        });

        // Удалить старые строки
        [].forEach.call(rows, function(row) {
            tableBody.removeChild(row);
        });

        // Поменять направление
        directions[index] = direction === 'asc' ? 'desc' : 'asc';

        // Добавить новую строку
        newRows.forEach(function(newRow) {
            tableBody.appendChild(newRow);
        });
    };

    [].forEach.call(headers, function(header, index) {
        header.addEventListener('click', function() {
            sortColumn(index);
        });
    });
});

По материалам https://htmldom.dev/

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 937

Популярные записи