JavaScript: сортировка строк в HTML-таблице
Хоть в этой таблице задано лишь три строки (если не считать строки-заголовка), в задаче предупреждают, что решение должно быть для таблицы с любым количеством строк.
Нужно отметить, что в текстовом исходнике HTML-страницы в песочнице для нашей задачи теги
ине прописаны (поэтому я отметил их серебристым цветом). Но по правилам языка HTML они обязательно должны быть (секция tbody в таблице по правилам HTML может быть одна или их может быть несколько), поэтому браузер (у меня — «Microsoft Edge» на базе движка «Chromium») добавляет их сам. В этом можно убедиться, запустив для загруженной HTML-страницы с данной HTML-таблицей инструмент «Elements» из инструментов разработчика (F12).
Выглядеть эта HTML-таблица в браузере может по-разному, в зависимости от стилизации с помощью языка CSS. Например, на отдельной странице обсуждаемой задачи в обсуждаемом учебнике эта HTML-таблица показана почти что без стилизации и выглядит некрасиво, примерно так:
Имя | Фамилия | Возраст |
---|---|---|
Джон | Смит | 10 |
Пётр | Браун | 15 |
Анна | Ли | 5 |
А на странице раздела 1.7 «Изменение документа» обсуждаемого учебника, в том месте, где описывается эта же задача, данная HTML-таблица выглядит гораздо лучше, примерно так:
В принципе, внешний вид тестовой HTML-таблицы не относится к сути задания. Однако, мне хотелось, чтобы тестовая HTML-таблица выглядела красиво. Поэтому я посмотрел стили обсуждаемого учебника и описал их (те из них, которые относятся к внешнему виду нашей тестовой HTML-таблицы) в заголовочной части нашей HTML-страницы:
table < width: 100%; border-collapse: collapse; font-size: 13px; font-family: "Segoe UI"; >tr:nth-child(2n) < background: #f9f9f9; >tr < border-bottom: 1px solid #ccc; >td, th < padding: 2px 1em 2px 5px; >th
Тут стоит отметить псевдокласс :nth-child (для меня он был в новинку). В данном описании стилей с помощью этого псевдокласса определяется цвет фона ( #f9f9f9 ) каждой четной строки (тег tr ) тестовой HTML-таблицы. При этом нужно иметь в виду, что нумерация строк начинается с единицы. То есть первой строкой является заголовочная строка нашей HTML-таблицы, она нечетная, поэтому ее фон имеет цвет по умолчанию (в данном случае — белый). Вторая строка — это строка, содержащая значения «Джон», «Смит» и «10», она чётная, поэтому ее фон — серого ( #f9f9f9 ) цвета. И так далее. Тут подробнее:
Итак, в заданной HTML-таблице по условиям задачи требуется отсортировать строки (то есть переставить их местами) по столбцу «Имя».
В первую очередь я решил выбрать все строки (HTML-элементы tr ) заданной HTML-таблицы. Рука потянулась к методу querySelectorAll , описанному в подразделе 1.4 «Поиск: getElement*, querySelector*» второй части обсуждаемого учебника. Но тут я вспомнил, что для HTML-таблиц существуют более удобные способы работы с частями этих таблиц. Они были описаны в подразделе 1.3 «Навигация по DOM-элементам» второй части обсуждаемого учебника. Например, получить коллекцию строк HTML-таблицы можно с помощью свойства rows HTML-таблицы. При построении тестовой HTML-таблицы ей был дан идентификатор table , поэтому получить коллекцию строк этой таблицы можно с помощью выражения table.rows .
В учебнике было сказано, что получаемая таким образом коллекция HTML-элементов не является массивом (это псевдомассив). А это значит, что к коллекции HTML-элементов нельзя будет применить методы массивов. А мы, ведь, хотим отсортировать строки HTML-таблицы, следовательно, нам нужно отсортировать полученную коллекцию строк HTML-таблицы. Можно, конечно, самому написать метод сортировки для коллекции. Но зачем, если метод сортировки уже написан, хоть и для массива? Просто преобразуем нашу коллекцию строк HTML-таблицы в массив, а затем используем готовый метод сортировки массива. Пишем код:
1.
let arr = Array.from( table.rows ); // преобразуем коллекцию в массив arr.sort(); // используем готовый метод сортировки массива
В этом коде используется сортировка массива по умолчанию (для метода сортировки не задан параметр). В этом случае элементы массива приводятся к строке, а затем происходит их сравнение. В нашем случае элементы массива являются объектами, HTML-элементами tr . При преобразовании каждого из этих объектов в строку получится одна и та же строка — «[object HTMLTableRowElement]» . Метод сортировки посчитает все элементы массива равными и сортировки не произойдет. Поэтому нам следует прописать функцию, определяющую порядок сортировки, и передать ее в метод сортировки массива первым параметром. Меняем код:
2.
let arr = Array.from( table.rows ); arr.sort( (a, b) => < let str = a.cells[0].textContent; let str2 = b.cells[0].textContent; return str.localeCompare(str2); >);
Здесь функция, определяющая порядок сортировки, не имеет имени и задана стрелочной функцией. У этой функции два входящих параметра — a и b , это два элемента массива, сравниваемых при сортировке массива, выполняемой методом sort . То есть переменные a и b являются в данном случае HTML-элементами tr .
По условиям задачи для сравнения строк нашей HTML-таблицы следует использовать значения столбца «Имя», а этот столбец состоит из первых ячеек (HTML-элементов td или th ) каждой строки нашей HTML-таблицы. Коллекцию этих ячеек можно получить с помощью свойства cells строки HTML-таблицы, это было описано в уже упомянутом подразделе 1.3 «Навигация по DOM-элементам» второй части обсуждаемого учебника. Первая ячейка каждой строки нашей HTML-таблицы и есть cells[0] , а ее текстовое содержимое — cells[0].textContent . Таким образом в переменных str и str2 мы получаем текстовые строки, каждая из которых представляет свой объект [object HTMLTableRowElement] , эти переменные мы и будем использовать для сравнения с помощью метода localeCompare .
Наш код уже отсортировал строки тестовой HTML-таблицы в массиве. Осталось лишь вставить их из массива обратно в тестовую HTML-таблицу. Сделаем это с помощью уже ставшего привычным (я его применял ранее для решения нескольких задач) метода append . В предыдущих постах я его применял для вставки одного HTML-элемента за раз, но, ведь, им можно вставлять сразу несколько HTML-элементов с помощью оператора расширения (многоточие). Меняем код:
3.
let arr = Array.from( table.rows ); arr.sort( (a, b) => < let str = a.cells[0].textContent; let str2 = b.cells[0].textContent; return str.localeCompare(str2); >); table.append(. arr);
Может возникнуть вопрос: если мы только вставляем новые строки в тестовую HTML-таблицу и не удаляем старые, то всего строк теперь должно стать не четыре, а восемь (каждая исходная строка дублируется)? На самом деле, нет, потому что метод append (как и его коллеги) сначала удаляет HTML-элемент со старого места, а затем вставляет его на новое место. То есть фактически вставляются не новые элементы, а исходные.
Наш код отсортировал строки заданной HTML-таблицы и результат отобразился в браузере. Однако, вместе со всеми отсортировалась и заголовочная строка, которая, по идее, должна остаться на своем месте, вверху HTML-таблицы. Я решил отследить эту строку внутри функции, определяющей порядок сортировки, и сделать так, что она не будет двигаться со своего места. Меняем код:
4.
let arr = Array.from( table.rows ); arr.sort( (a, b) => < if (a.cells[0].tagName == "TH" || b.cells[0].tagName == "TH") return 0; let str = a.cells[0].textContent; let str2 = b.cells[0].textContent; return str.localeCompare(str2); >); table.append(. arr);
Это сработало, теперь заголовочная строка остаётся на своем месте. Однако, авторы задачи в своем решении предложили более изящный способ:
5.
let arr = Array.from( table.rows ); arr = arr.slice(1); arr.sort( (a, b) => < let str = a.cells[0].textContent; let str2 = b.cells[0].textContent; return str.localeCompare(str2); >); table.append(. arr);
Метод slice возвращает новый массив, в данном случае — без первого элемента (который и является заголовочной строкой нашей HTML-таблицы), и полученный массив записывается на место старого, в переменную arr . Таким образом, заголовочная строка вообще не принимает участия в сортировке и не перемещается на новое место (не попадает в руки метода append ).
Теперь, казалось бы, всё в порядке. Но нет. Если просмотреть полученную HTML-страницу с помощью инструмента «Elements» из инструментов разработчика (F12) браузера, то можно увидеть, что метод append вставил HTML-элементы tr не внутрь HTML-элемента tbody (как положено по правилам языка HTML), а после него.
В некоторых случаях такое нарушение может повлиять на внешний вид страницы. Например, в случае кода из варианта 4 нашего скрипта (см. выше) собьется порядок окраски фона строк нашей тестовой HTML-таблицы: серым цветом будет окрашен фон нечетных строк (первой и третьей) вместо указанных в стиле четных (второй и четвертой). Это произойдет из-за того, что HTML-элемент tbody станет соседом HTML-элементов tr и при расчете номеров HTML-элементов в этой группе HTML-элементов он будет считаться первым (так как является первым ребенком общего для группы родителя). Таким образом, нумерация собьется и первая строка таблицы будет теперь считаться второй, вторая строка — третьей и так далее.
Чтобы поместить отсортированные строки нашей HTML-таблицы из массива обратно в HTML-элемент tbody , изменим последнюю строчку нашего кода:
6.
let arr = Array.from( table.rows ); arr = arr.slice(1); arr.sort( (a, b) => < let str = a.cells[0].textContent; let str2 = b.cells[0].textContent; return str.localeCompare(str2); >); table.tBodies[0].append(. arr);
Это финальный вариант скрипта. О свойстве tBodies рассказывалось в том же подразделе 1.3 «Навигация по DOM-элементам» второй части обсуждаемого учебника. Секций tbody , как уже упоминалось, в HTML-таблице может быть несколько. В нашем случае такая секция одна.
Javascript. Сортировка таблицы
Может для большинства здешних обитателей моя статья покажется слишком скучной и «и так понятно», но думаю новичкам будет в чем-то полезной.
Стояла передо мной как-то задача сделать сортировку таблицы по разным колонкам. Данный брались из базы, каждый раз их вытаскивать — накладно, решил задействовать JavaScript.
Были небольшие трудности из-за использования различной стилистики строк в таблице, а именно был разный цвет фона, в зависимости от данных самой таблицы.
Стилистику нарушать было нельзя. Кого заинтересовало, прошу под кат.
Подсветка фона выбрана так:
1) если время < 2012 год — подсветка красным
2) если расстояние > 9000 — подсветка желтым.
*) остальное чередование белого с серым.
Данные талицы берутся из БД, рисуется php, последняя колонка скрыта, это время в UNIX-формате, по ней сортировать колонку «Время» проще.
var TableSort = function (idTbl, defSortCol, firstRow, classes) < // номер колонки, по которой выполнена текущая сортировка, считаем с 0 var curSortCol = defSortCol; // номер колонки, у которой отрисовать картинку, показывающая направление сортировки, считаем с 0 var curImgCol = defSortCol; // направление сортировки вверх var curSortUp = true; // id таблицы, в которой производим сортировку var curIdTbl = idTbl; // номер строки, с которой идут данные, считаем с 0 var numColTr = (firstRow == null) ? 1 : firstRow; // нужно ли учитывать классы для строк if (classes == null) < var style = false; >else < var style = true; // список классов, которые нужно сохранить var needClasses = classes[0]; // список классов, которые чередуются var listClasses = classes[1]; >var tbl = document.getElementById(curIdTbl); var allImgs = new Array(); // all imgs-arrow allThs = tbl.getElementsByTagName('tr').item(0).getElementsByTagName('th'); for (i=0; i else < allImgs[i] = null; >> var tblData = new Array(); // current data in table // начинаем сортировку по колонке newCol, картинку рисуем у колонки imgCol this.initSort = function (newCol, imgCol) < if (newCol == curSortCol) < // кликнули на отсортированную колонку, меняем сортировку на обратную curSortUp = !curSortUp; >else < // сортируем по новой колонке curSortCol = newCol; curImgCol = (imgCol == null) ? newCol : imgCol; curSortUp = true; >showArrow(); getDataTable(); showSortTable(); if (style) < doStyle(); >>; // show/change arrow function showArrow() < for (i=0; ielse < allImgs[i].src = "./img/down.png"; >> else < allImgs[i].style.visibility = "hidden"; >> > > // get new data from table function getDataTable() < allTrs = tbl.getElementsByTagName('tr'); for (i=numColTr; iif (style) < tblData[i-numColTr][allTrs[i].getElementsByTagName('td').length]=allTrs[i].className; >> tblData.sort(_sort); if (!curSortUp) < tblData.reverse(); >> // rules for sorting function _sort(a1, b1) < var a = a1[curSortCol]; var b = b1[curSortCol]; if (parseFloat(a) && parseFloat(b)) < return parseFloat(a) - parseFloat(b); >else < if (a.toLowerCase() < b.toLowerCase()) < return -1; >else if (a.toLowerCase() > b.toLowerCase()) < return 1; >else < return 0; >> > function showSortTable() < allTrs = tbl.getElementsByTagName('tr'); for (i=numColTr; iif (style) < allTrs[i].className=tblData[i-numColTr][allTrs[i].getElementsByTagName('td').length]; >> > function doStyle() < allTrs = tbl.getElementsByTagName('tr'); for (i=numColTr; iif(needClasses.indexOf(allTrs[i].className) != -1) < continue; >allTrs[i].className = listClasses[(i % listClasses.length)]; > > >
var infoTblSort = new TableSort("idTbl", 0, 1, [['add', 'edd'],['odd', '']] );
onclick="infoTblSort.initSort(4,3);"
В этом примере передается 2 параметра, первый — по которому будет идти сортировка (в данном случае — это скрытая колонка UNIX-время), вторая — колонка, у которой будет стоять картинка, указывающая направление текста.
На этом все, надеюсь мой опыт будет кому-то полезен. Прошу пощения за несколько сумбурный рассказ, впервые пишу статью.
Спасибо за внимание.