Управление DOM с помощью JavaScript в современных браузерах и IE 11+
Предположим, что мы хотим отсортировать любой столбец следующей таблицы:
<table id="sortMe" class="table">
...
</table>
Прежде всего, мы получим все заголовки, обойдем их в циклу и добавим обработчик события для события click
к каждому из них:
// Query the table
const table = document.getElementById('sortMe');
// Query the headers
const headers = table.querySelectorAll('th');
// Loop over the headers
[].forEach.call(headers, function(header, index) {
header.addEventListener('click', function() {
// This function will sort the column
sortColumn(index);
});
});
Функция sortColumn(index)
упомянутая выше будет сортировать все строки от данного столбца index
.
Для этого:
Array.sort()
для сортировки текущих строк// Query all rows
const tableBody = table.querySelector('tbody');
const rows = tableBody.querySelectorAll('tr');
const sortColumn = function(index) {
// Clone the rows
const newRows = Array.from(rows);
// Sort rows by the content of cells
newRows.sort(function(rowA, rowB) {
// Get the content of cells
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;
}
});
// Remove old rows
[].forEach.call(rows, function(row) {
tableBody.removeChild(row);
});
// Append new row
newRows.forEach(function(newRow) {
tableBody.appendChild(newRow);
});
};
Как видите, массив предоставляет встроенный метод sort
, который принимает функцию для сравнения двух элементов. В нашем случае две ячейки столбца сравниваются на основе его HTML-содержимого:
newRows.sort(function(rowA, rowB) {
// Get the content of cells
const cellA = rowA.querySelectorAll('td')[index].innerHTML;
const cellB = rowB.querySelectorAll('td')[index].innerHTML;
...
});
Он хорошо работает с ячейками, содержимое которых представляет собой строку, а не числа или другой тип, такой как дата. Переход к следующему разделу, чтобы увидеть, как мы можем поддержать эти случаи.
Мы добавляем пользовательский атрибут к каждому заголовку, чтобы указать тип его ячеек:
<thead>
<tr>
<th data-type="number">No.</th>
<th>First name</th>
<th>Last name</th>
</tr>
</thead>
Например, столбец № будет иметь атрибут data-type="number"
. Если атрибут отсутствует, типы содержимого ячеек являются строковыми. Нам нужна функция для преобразования содержимого ячеек из строки в другой тип:
// Transform the content of given cell in given column
const transform = function(index, content) {
// Get the data type of column
const type = headers[index].getAttribute('data-type');
switch (type) {
case 'number':
return parseFloat(content);
case 'string':
default:
return content;
}
};
Пример кода демонстрирует number и string столбцы, но вы можете поддерживать больше типов, такие как дата.
Теперь мы немного улучшили функцию sortColumn
для поддержки пользовательских типов контента. Вместо того, чтобы сравнивать необработанный контент, мы сравниваем значения, которые конвертируются на основе типа контента:
newRows.sort(function(rowA, rowB) {
const cellA = rowA.querySelectorAll('td')[index].innerHTML;
const cellB = rowB.querySelectorAll('td')[index].innerHTML;
// Transform the content of cells
const a = transform(index, cellA);
const b = transform(index, cellB);
// And compare them
switch (true) {
case a > b: return 1;
case a < b: return -1;
case a === b: return 0;
}
});
В данный момент щелчок по заголовку сортирует все строки. Мы должны изменить направление, если пользователь снова нажмет на заголовок. Для этого мы подготавливаем переменную для управления направлениями сортировки всех заголовков:
// Track sort directions
const directions = Array.from(headers).map(function(header) {
return '';
});
directions
является массивом, в котором каждый элемент может быть asc
или desc
указывать направление сортировки в ассоциированном столбце.
Функция sortColumn()
в настоящее время включает больше логики для сравнения двух строк на основе текущего направления:
const sortColumn = function(index) {
// Get the current direction
const direction = directions[index] || 'asc';
// A factor based on the direction
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;
}
});
...
// Reverse the direction
directions[index] = direction === 'asc' ? 'desc' : 'asc';
...
};