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

Зачем перегружать страницу при сортировке HTML-таблицы? Нельзя ли перегруппировать HTML элементы таблицы прямо в браузере, с помощью Javascript?.js (55 строк).

Сортировка таблицы средствами JavaScript - DOM

Вопрос когда-то с неизбежностью возникает перед любым добросовестным веб-мастером: зачем перегружать HTML страницу при сортировке таблицы? Нельзя ли перегруппировать HTML элементы таблицы прямо в браузере, без серверных скриптов, с помощью одного только Javascript? Решение задачи и описание решения являются в данном случае одинаково интересными. К моменту, когда вопрос привлёк наше внимание, мы исходили из следующих посылок:

  • HTML элементы таблицы априори можно менять друг с другом местами (в т.ч. сортировать) средствами DOM-Javascript;
  • подобные манипуляции с DOM Node можно (и, в идеале, нужно!) производить с помощью кода Javascript очень небольшого размера, понятного и «прозрачного»;
  • на каком-то популярном ресурсе нам уже встречалась довольно большая таблица, сортируемая без перезагрузки;
  • невероятно, чтобы по такому очевидному вопросу никто ничего не написал в Интернете.
  • Приступив к детальному исследованию, мы, в первую очередь, нашли подозреваемый ресурс; это оказался форум torrents.ru. Ничего утешительного, однако, он нам не принёс: там для сортировки таблицы используется колоссальный плагин с сайта tablesorter.com к чудовищному 200-килобайтному супер-кроссбраузерному «фреймворку» jQuery (который веб-мастера уже довольно привычно пихают куда попало, к месту и не к месту).

    Нет, это не наш путь, это противоречит второй посылке. Нормальный код должен быть коротким и понятным, доступным для объяснения. К тому же, отсутствует «расширяемость» (кому надо копаться в этих монстрах?). Например, довольно быстро натыкаешься на следующее неудобство при сортировке таблиц в torrents.ru: при возвращении на страницу с отсортированной ранее javascript"ом таблицей, таблица открывается в первозданном виде. Как «закрепить» статус сортировки на странице? Это можно было бы сделать с помощью cookie (о чём мы недавно писали в статье о сохранении настроек пользователя), но куда потом в этом jQuery-сортере пихать сохранённые параметры?

    Полезли в Яндугль – проверять 4-ю посылку; там тоже ничего утешительного. На первых местах не очень корректные решения, а два более-менее заинтересовавших нас – на сайтах usa.org.ua и HTMLcoder.visions.ru – оказались «позаимствованными» у буржуев – с сайтов www.leigeber.com/2009/03/table-sorter/ и idontsmoke.co.uk соответственно. Решения при беглом знакомстве показались неплохими, но всё-таки чрезмерно сложными; да и за державу немного обидно.

    Надеемся, что наш скрипт сортировки HTML таблиц будет короче и лучше, а его описание – понятнее. Вот, кстати, он сам – tabsort.js , а вот результат его работы:

    Общая постановка задачи (приблизительный алгоритм)
  • Заголовочная строка. Сортировать надо не все строки таблицы, первая строка должна быть с заголовками, которые остаются на месте. Сортировка должна производиться при щелчке по заголовку столбца; значит, курсор в строке заголовков должен быть «Указатель». При повторном щелчке по тому же заголовку сортировать в обратном порядке.
  • Что именно сортировать? Средствами DOM-Javascript легко получить HTML-коллекцию элементов, в том числе, строк таблицы. Но как сортировать эти элементы (которые в представлении Javascript являются объектами и в примитивном своём значении выглядят все одинаково – как HTMLRowElement)?
  • Очевидно, что сортировать можно только текст , к которому можно получить доступ двумя (примерно) путями: через свойство innerHTML и через nodeValue текстовых узлов.
  • В первом приближении: получаем строки таблицы, innerHTML для каждой – и сортируем. Так возможна сортировка по первому столбцу. Как сортировать по другим?
  • Мы увидели два варианта: 1) насортировать элементы выбранного столбца и потом привести порядок строк в соответствие с полученными новыми индексами; 2) сортировать сразу строки – с помощью пользовательской функции, которая будет сравнивать тексты элементов «щёлкнутого» столбца.
  • Возникла дополнительная задача – сравнить скорость вариантов и выбрать лучший.
  • Заголовки. «Полосатая» таблица.

    Следуя хорошей практике ненавязчивого javascript , мы привязываем какие-то события к объектам не внутри HTML кода, а в скрипте, расположенном в конце страницы. В данном случае объект – таблица. Может быть, не одна таблица на странице. И, может быть, не все таблицы, а избранные. Мы решили избирать их по одному атрибуту: class="sortable". Если у элемента table есть такой атрибут, таблица будет привязана к нужному событию для сортировки.

    Var tex=document.getElementsByTagName("TABLE"); for (var el in tex) { var elem=tex; if (1==elem.nodeType && "TABLE"==elem.tagName && "sortable"==elem.className) { elem.onclick=clicktab; elem.rows.style.cursor="pointer"; //полосатая таблица for (var j = 1; j < elem.rows.length; j++) { if (0==j%2) elem.rows[j].style.backgroundColor="f0f0f0"; elem.rows[j].onmouseover=overtab; elem.rows[j].onmouseout=outtab; } } }

    Попытка получить свойства какого-то объекта (или элементы HTML-коллекции) способом for (var el in obj) не в теории, а в конкретном браузере (не будем показывать пальцем), иногда даёт странные результаты. Именно этим объясняются наши перестраховочные проверки: является ли узел элементом (nodeType 1) и даже имеет ли он tagName "TABLE" (не затесалось ли при выборке getElementsByTagName("TABLE") туда нечто иное!). Нам трудно привести рациональные причины своего поведения в этом случае, но «это работает», а без «этого» у нас когда-то где-то возникали проблемы.

    Назначение избранным таблицам события onclick=clicktab занимает первые 6 строк «инициирующего» кода. Следующие 6 строк посвящены боевой раскраске таблиц в чередующиеся чёрно-белые полосы. Всё равно мы планировали её когда-то описывать, почему бы не сэкономить место и не сделать это заодно. Н-да. И почему бы заодно («Остапа несло») не запрограммировать «подсвечивание» строк таблицы при движении над ними мыши? Это займёт ещё 12 строк:

    Var trcolor; function overtab(e) { e = e || window.event; var obj=e.target || e.srcElement; var tr=obj.parentNode; trcolor=tr.style.backgroundColor; tr.style.backgroundColor="#ccf"; }

    Код функции outtab , возвращающей первоначальный цвет покинутым строкам, как две капли воды похож на overtab (что, конечно, является недостатком и поводом для оптимизации в ближайшем будущем) и находится в файле tabsort.js . Впрочем, оптимизация будет не очень: в коде останутся те же 12 строк; зато логика лучше.

    Подводные камни: как получить строки таблицы?

    Кто знаком с DOM, сразу может подумать о такой конструкции: находим элемент «таблица», получаем список его «детей» – вот тебе и массив строк для сортировки! Вроде document.getElementById("mytable").childNodes . И это будет ошибкой. Таблица – сложный элемент; в каждой таблице обязательно есть (пишем мы его в HTML коде явно или нет) вложенный элемент tbody , и только в нём уже могут находится строки (row ). А ещё в таблице может быть элемент thead , и в нём тоже могут быть строки. А ещё в таблице может быть несколько элементов tbody ! Поэтому получение строк таблицы через интерфейс DOM Node может оказаться довольно сложным. Или надо будет обязать пользователей нашего DOM-сортировщика чётко выдерживать структуру HTML кода таблицы – с обязательным thead и (единственным) tbody .

    К счастью, у DOM интерфейса HTML таблицы HTMLTableElement , в отличие от других HTML элементов, есть особое свойство rows , которое и даёт сквозной список всех строк таблицы, независимо от их положения в thead и разных tbody . С учётом этого код получения всех строк таблицы будет примерно таким: document.getElementById("mytable").rows . Далее, у HTMLTableRowElement , конечно же, есть свойство cells , а у HTMLTableCellElement есть очень важное для нас свойство cellIndex , указывающее порядковое место ячейки в строке, т.е., фактически, номер колонки (которую мы планируем сортировать!).

    Следующая сложность. Ну, получили мы, допустим, список строк. Как его теперь сортировать? То, что мы получили через интерфейсы DOM, называется HTML коллекция ; у этого объекта (или набора объектов) нет метода сортировки (как, впрочем, нет его и ни у одного другого объекта DOM в HTML). Ясно, придётся переводить всё в массив Javascript. Массив сортировать можно. Как потом помещать элементы обратно в дерево DOM в правильном порядке? Как потом (или сначала?) удалять старые элементы, стоящие в неправильных местах?

    Функция сортировки таблицы clicktab

    Код всей системы (с функцией сортировки и привязкой к избранным таблицам) находится в файле tabsort.js и занимает сто с небольшим строк (треть которых – комментарии!:-).В начале и в конце функции стоят строки для учёта времени работы. Основная строка учёта – с alert – закомментирована, если её раскомментировать, будет выскакивать сообщение с числом миллисекунд, затраченных на сортировку таблицы.

    Работа функции начинается при щелчке по любой ячейке «избранной» таблицы (не только по заголовку). В первую очередь, из события «щелчок» (click) мы извлекаем именно тот элемент, по которому был клик. Это, в общем случае, ячейка (элемент td или th ). Мы исходим из того, что это верно. Мы вообще декларируем работу скрипта только при щелчке по заголовкам столбцом. Если в ячейке заголовка планируются вложенные элементы (например, ради особого оформления), в начало функции следует добавить поиск основного элемента th , по аналогии с поиском «родительской» таблицы (этот фрагмент сейчас в функции clicktab закомментирован):

    If ("TD"!=obj.tagName && "TH"!=obj.tagName) { while (("TD"!=obj.tagName && "TH"!=obj.tagName) && null!=obj.parentNode) { obj=obj.parentNode; //ячейка } }

    По причине структурной сложности самой таблицы, примерно так же находится сам элемент table (и записывается в переменную tab). Впрочем, пока вы это читали, данный фрагмент кода устарел и удалён из рабочей версии (до нас вдруг дошло, что именно на таблицу навешено событие click , и значит она доступна внутри функции по слову this ).

    На первый взгляд, несколько спорная строка var tbody=tab.lastChild; – вдруг в таблице будет несколько вложенных tbody? Но, поскольку мы получаем строки таблицы через сквозное свойство rows , для извлечения всех строк нам неважно количество tbody; мы выбираем последний из всех, чтобы поместить туда новый , отсортированный массив со строками.

    Для функции cmprow (a, b), которая непосредственно сортирует массив, нужен тип сортируемых строк. А то для чисел сортировка может получиться в виде «1, 10, 2, 20». Принимаем тип по умолчанию числовой (sorttype="num"); затем, при прохождении в цикле всего сортируемого столбца, проверяем тип каждого текста; и если тип хоть одной ячейки будет не числовым (isNaN), тип всей сортировки назначается строковым (sorttype="str").

    Ещё один спорный момент – innerHTML. У одного из буржуйских предшественников нашего скрипта используется кропотливое циклическое извлечение всех текстов (nodeValue узла textNode) из элементов, вложенных в ячейку. Результат, однако, напоминает нечто вроде strip_tags в PHP. Мы упрощаем этот момент, что, по сути, приводит к группировке однородных по структуре ячеек (например, со ссылками) и к сортировке уже внутри этих групп. Ну, это уже больше вопрос идеологии – как именно надо сортировать. Можно и по извлечённым текстам, игнорируя структуру тэгов – это, скорее всего, не должно сильно замедлить всю процедуру. Да. Поэтому мы таки тоже будем извлекать тексты, но менее изощрённо (точнее, извращённо) - именно по кальке метода strip_tags ; а в резерве останется возможность сортировки с тэгами при удержании клавиши Shift (например). Вот оно, создание массива rarr для последующей сортировки:

    For (var j=1; j 1.

    Сокращать записи вида 1,-1,3,1 нельзя. Это связано с одной особенностью, которая может работать на благо пользователя: последовательные сортировки обрабатывают не начальные данные, а результат последней сортировки. Т.е. последовательные сортировки разных столбцов работают a la Excel, а не как в базе данных (каждый раз с исходной таблицей). Это значит, для сортировки по двум и более столбцам не нужно никаких ухищрений с удержанием клавиши Shift (как предлагает tablesorter.com) и соответствующих усложнений кода - у нас всё и так при последовательных щелчках по разным столбцам работает, как интуитивно ожидает пользователь.

    И это работает быстро, быстрее, чем у плагина tablesorter! Таблица в 400 строк (800К) сортируется за 1.5 секунды (Athlon 999МГц, 512 МБ ОЗУ, Windows XP). А клавишу Shift мы резервируем для обработки разных типов данных: 1) при удержании этой клавиши (в следующей версии программы) будем сортировать HTML код, а не ободранный strip-тэгами текст; 2) добавим сортировку даты, по умолчанию дата будет считаться русской (dd.mm.yy), а при удержании Shift - импортной (mm.dd.yy).

    © 2010, «Деловая неделя», Михаил Гутентог

    Комментарии

    Дилетант 10.02.2013 17:47:04

    Nik_neman 17.02.2012 01:37:17

    День добрый. Использую Big Table Sorter v2.1 (+ JS Data Filter). Данные перед этим вывожу из БД с помощью PHP+MySql... через каждые 10 строк необходимо выводить баннер, названия которого совпадает с текстом предшествующей строчки... решил начать делать постепено и, вывести 10 строчку и 3 столбец... с помощью данного кода: var l = document.getElementById("banners").rows.cells.innerHTML; document.write(l); но мне выводит название, которое хранится в БД. СОРТИРОВКА не учитывается. В javascript не силён. Помогите, пожалуйста, советом. Заранее спасибо.

    Лесник 10.05.2011 16:31:55

    Чайник , можно, если у вас есть доступ к изменению HTML-кода страницы: вы можете добавить свою HTML-таблицу (с атрибутом class="sortable") на любую страницу, сохранить файл и радоваться.

    Чайник 10.05.2011 02:06:57

    Очень нужная задача! А нельзя ли взять работающий сайт с сортировкой таблицы и подсунуть в него свою таблицу (если я владею только HTML)?

    Лесник 08.12.2010 05:19:15

    Iris_din , нет: функция подготовки таблиц к сортировке запускается один раз, по событию "загрузка страницы" (window.onload); если после этого создать (динамически) новую таблицу, она не будет подключена к системе сортировки.

    Впрочем, в текущей версии (файл tabsort.js) я сделал для вас такую возможность: после создания вашей динамической таблицы следует запустить функцию prepTab(t), принимающую теперь в качестве параметра "t" HTML-элемент - таблицу, которую нужно сортировать.

    Лесник 11.12.2010 23:30:41

    Всё, сделал возможность подключать Сортировщики к динамически созданной таблице в обеих версиях: и . Как подключать, написано (с примером) в статье tabsort0.aspx).

    Да, и тут с добавлением комментариев какой-то бардак был - вроде, теперь исправил.

    Iris_din 08.12.2010 01:19:15

    Скажите, работает ли сортировка для динамически созданных таблиц? У меня почему-то не получается.

    Лесник 30.09.2010 18:07:00

    О, да, получить «реальный» индекс ячейки можно. С помощью функции realCellIndex() . Которую специально для этого мне пришлось написать:-). Наверное, можно считать, что готов «релиз» Сортировщика v1.3 – tabsort1.3.js (не забывайте обновлять также ir2.js).

    Краткое описание алгоритмов и трудностей – в статье tabsort1-3.aspx . Примеры работы: thead2.htm , limit1001.htm (большая таблица).

    MrTopa 30.09.2010 02:17:46

    Рано обрадовался. В таком варианте таблицы неправильно определяется реальный индекс колонки второй строки шапки.

    id0TickerPrice Open id1NameName1
    Company
    53ABD126.1
    72AAC512.59
    61ADB47.45
    84DCA19.82
    Можно ли как-то уникально идентифицировать ячейку кроме cell.cellIndex?

    Лесник 29.09.2010 16:06:56

    Да, с innerHTML в ИЕ оказалась полная задница. Работаю сейчас над этим. Потому что всё равно надо – большие таблицы (1000 строк) с использованием innerHTML отрисовываются в ИЕ в десять раз быстрее, чем с помощью appendChild . В других браузерах в 5-7 раз быстрее. Для маленьких таблиц (100-200 строк) это не принципиально.

    Ну, заодно и раскраску проверю.

    MrTopa 29.09.2010 02:59:37

    Спасибо, заработало. Только IE8 (в более ранних не проверял) не захотел принимать строку tbody.innerHTML = newtbody в функции draw(table). Потому закомментировал newtbody и раскомментировал старый вариант, добавив delclass(table.rarr[l].val, "even"); delclass(table.rarr[l].val, "odd"); перед var classN = (l%2) ? "odd" : "even", иначе строки подкрашивались неправильно, а через несколько сортировок подкраска вообще исчезала

    Лесник 24.09.2010 03:57:42

    MrTopa , спасибо за интересную задачу. Заставили меня попотеть. Скачайте скрипт tabsort1.3a.js – с ним указанная вами таблица с заголовком из двух строк (thead.htm) должна сортироваться нормально. Пока не проверял новый скрипт на сохранение параметров сортировки (в тестовом стоит use_cookie = false), остальное, вроде, работает.

    MrTopa 23.09.2010 00:29:38

    Пробую использовать ваш скрипт, все отлично работает если гапка таблицы состоит из одной строки. Мне же нужно сделать сортировку по сложной шапке. Проще объяснить на примере. Есть таблица:

    TickerPrice Open idNameName1
    Company
    3ABD126.1
    2BAC512.59
    1CDB47.45
    4DCA19.82
    возможную ошибку. Теперь работает и в Опере (только может теряться состояние сортировки для отдельных таблиц)./ppВ старых версиях tabsort.js, как вы помните, несколько таблиц перемешивались, именно потому что был один глобальный массив сортировки. Сейчас такой массив у каждой таблицы свой, привязанный к объекту btable /b как свойство (btable.rarr /b). Опера вдруг ни с того ни с хрена удаляет свойства.rarr у некоторых таблиц на странице (если таблиц больше одной)./ppМожно, в принципе, радикально решить проблему, создав глобальный массив массивов для всех таблиц, и сопоставить элементы глобального массива номеру каждой таблицы. Но ведь привязывать нужные свойства к DOM-элементам гораздо удобнее, проще (меньше запоминать программисту). Надеюсь, Оперу всё же починят в этом плане./p pClusta 21.08.2010 03:42:25 /ppА как прикрутить к таблице фильтр? например, к столбцу ID дропдаун-селект со значениями 100, 100-300, 300-1000, 1000/ppкак-то так./p pPlayer0 20.08.2010 19:38:27 /ppда уж странно.. проблема оказалась в опере – на всём остальном работает. у меня опера 10.61 (дальше не обновляет). старые версии tabsort.js шли норм; ir2.js обновлять не забывал. может это у меня опера криво стоит? вроде проблем не замечал. отпишитесь кто-нибудь, у кого работает./p pЛесник 20.08.2010 16:12:03 /p pPlayer0 29.06.2010 01:16:19 /ppещё раз благодарю)/p p1 05.07.2010 00:24:33 /ppa вот кстати новая не работает (даже в тесте)/p pЛесник 16.06.2010 18:08:17 /ppЯ не то чтобы очень уж ответственный, просто мне интересно то, что я делаю, и я использую любые возможности улучшить свою работу./ppВопрос о числах не так прост. Сейчас используется такой алгоритм: проверяются все значения столбца, и если все они – числа, тип сортировки «число»; иначе – строка («как винда»)./ppА как надо делать? Видел на одном форуме другую схему: в заголовочной ячейке явно прописывается тип в атрибуте (style="sort:string"). Но мне не очень нравится. Можно проверять тип значения только у первой ячейки; но тогда где гарантия, что все остальные ячейки не будут другого типа?/ppБольше всего склоняюсь к мысли «произвольного управления» сортировкой: например, при нажатой клавише SHIFT рассматривать тип столбца как «число», а при нажатой «ALT» – русская дата... Ну, в спецификацию можно добавить также и (необязательное!) условие обозначать тип данных в заголовке (только, конечно, не в атрибуте bstyle /b – для этого существует атрибут baxis /b). Приоритет будет у клавиш пользователя. Да, пожалуй, так. Завтра же и займусь./p pPlayer0 16.06.2010 05:59:20 /ppрас уж вы такие ответственные, и отвечаете на комментарии:)) то вот ещё пара моментов из личного опыта, с которыми возникают неудобства: я заметил, что скрипт может сортировать числа 2-мя способами – тупо по алфавиту (как винда)т.е.: 1 14 15 18 193 2 23 271 и т.д/ppи как "числа" (по нормальному) 1 2 3 11 24 136 .... для второго способа в сортируемой колонке не должно быть никаких символов, кроме цифр. но очень часто нужно, например, поставить знак – , так как значение отсутствует, или например знак?, так как значение неизвестно, ну и т.д. и тут то нужная сортировка перестаёт работать:((приходится хитрить и ставить либо 0 либо просто ничего./p pЛесник 14.06.2010 18:28:31 /ppДа, действительно оказалось перемешивание двух таблиц. Раньше не попадал в такие ситуации, потому что, видимо, не так активно тестировал (не очень нужно было несколько таблиц на странице сортировать)./ppПроисходило потому, что сортировочный массив brarr /b был глобальным (для всего окна). Сейчас исправил: сделал у каждой сортируемой таблицы новое свойство – свой массив brarr /b./p pЛесник 14.06.2010 00:20:55 /ppспасибо за отзыв! Да, чтобы скрипт заработал надо всё сделать именно так:br/pp1) подключить к странице два скрипта (библиотеку ir2.js и tabsort.js) (лучше вставлять их в элемент HEAD);br/pp2) к тегу TABLE сортируемой таблицы (их может быть несколько) добавить атрибут class="sortable".br/ppНепонятно, правда, что там с последней строкой и почему не сортируются две таблицы. Вообще скрипты уже немного изменились. Проверяю их работоспособность (почти каждый день) на стр. . Можете проверить сами – например, введя в окно запросов такой текст:br/ppselect * from ir2_dn.`msgs` order by id desc limit 5;br/ppselect * from ir2_dn.`msgs` order by id limit 5;br/ppПолученные на экран две таблицы сортируются нормально и независимо друг от друга.br/ppДа, и самое главное изменение: всё-таки решил сортировать таблицы только щелчком по заголовку (а не по любой строке), а то очень неудобно в работе, когда надо что-то скопировать из ячейки, а вся таблица от щелчка вдруг прыгает (сортируется)./p pPlayer0 10.06.2010 05:52:54 /ppотличный скрипт+ простота в использовании. пользуюсь им у себя на сайте a target="_blank" href="http://minifaq.clan.su/publ/front_mission_3/front_mission_3_navyki_po_alfavitu/2-1-0-38"http://minifaq.clan.su/publ/front_mission_3/front_mission_3_navyki_po_alfavitu/2-1-0-38/a . иногда возникают проблемы с последней строкой, которая не сортируется. так же не получилось сделать 2 таблицы на одной странице – при сортировке они смешивались. в остальном же – огромное спасибо! ps – из текста немного непонятно, как его установить, поэтому для тех, кому надо (установлено методом тыка:)) table class="sortable".......... – вставить в начало. прописать 2! скрипта в конце (второй я тупо из сохранённой страницы взял) script type="text/javascript" src="ir2.js"/script script type="text/javascript" src="tabsort.js"/script/p pВстала задача отсортировать таблицу на яваскрипте по любому столбику. Известный всем плагин tablesorter у меня не заработал, и я решил, что быстрее и удобнее будет написать свой скрипт. Вот, что получилось через час:/ppСкрипт получился из 20 строк, 1 кб (готовый tablesorter весит 13 кб и дольше «усваивается»). Ещё одно отличие: подготовительные операции для сортировки происходят по клику; это имеет смысл, потому что сортировка нужна не всем./ppПервым делом нужно присвоить идентификаторы каждой строчке таблицы. Это лучше сделать сразу на сервере при выводе таблицы:/ppbrbr.../ppТеперь идентификатор будет привязан к строке с данными, и если её переставить в другое место, ничего не перепутается./p h2Сортировка массива/h2pВ интернете можно найти решения, где таблица целиком переводится в многомерный массив, а затем этот массив сортируется, после чего формируется и выводится новая таблица на место старой. Однако мне этот способ показался слишком громоздким, ведь достаточно держать в памяти всего два столбика:br1) по которому происходит сортировка,br2) столбик идентификаторов./ppМожно обойтись простым массивом (ключ = значение), где ключ отвечает за номер строки. Но лично мне проще использовать двумерный массив, чтобы не запутаться между ключами и значениями./ppНа примере моей таблицы:/ppimg src='https://i1.wp.com/plunix.ru/w/table-sort-1.png' height="202" width="298"/ppimg src='https://i0.wp.com/plunix.ru/w/table-sort-2.png' height="202" width="298"/ppА первые значения массива указывают, в каком порядке будут идти строки отсортированной таблицы./ppКстати, данный метод аналогичен сортировке в MySQL с использованием вре́менных таблиц./ppВо временный массив желательно класть подготовленные значения, которые быстро и легко сравнивать. Лишнее (тэги и css) надо отбросить, отформатированные числа перевести в обычные числа (20 000 → 20000), а строки можно превратить в некие эквиваленты. Например, в моём случае единственный текстовый столбик содержит только два слова: iсвободен /i и iбронь /i. У них разная длина, а значит, во временный массив можно записать только длину слов — этого хватит для корректного сравнения./ppvar z=$(this).children("td").eq(i_s).html().split(" ").join(""); //убираем пробелыbrif(isFinite(z)) {z=parseInt(z);} else {z=z.length;} //число остаётся числом, а у строк оставляем только длину/ppВ яваскрипте есть готовый алгоритм сортировки, использующий функцию сравнения двух элементов:/ppfunction sName(a,b) { //задаём функцию сравненияbrif(a < b) {return (-1)*s_vozr;}brelse if(a > b) {return s_vozr;}
    else {return 0;}
    }

    multi.sort(sName); //сортируем массив multi, указав функцию sName

    Функция sName(a,b) возвращает −1, 0 или 1. Если умножить возвращаемое значение на −1, то это изменит направление сортировки (по возрастанию или по убыванию). Таким образом, при помощи параметра s_vozr можно управлять направлением сортировки (если повторно кликнули по столбцу, параметр s_vozr меняет знак).

    Перестановка строк таблицы

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

    Как переделать старую таблицу в новую, используя перемещение строк? Пройдёмся в цикле по нашему вре́менному массиву, перемещая нужные строчки таблицы в её конец.

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

    Достаточно часто пользователям интернет приходится сталкиваться с большим объемом информации, представленным в виде таблицы. Не менее часто требуется получить результаты в ином порядке, чем они представлены первоначально. Большинство web-мастеров решает эту проблему применением сортировки на сервере, для чего используется перезагрузка страницы. Действительно, серверные языки предоставляют гораздо больше возможностей отсортировать многомерный массив по определенному значению, чем скриптовые языки, выполняющиеся непосредственно на стороне клиента. Решение сортировать данные на стороне сервера вполне оправданно с точки зрения трудозатрат программиста, так как многие серверные языки имеют встроенные функции сортировки многомерных массивов, и поэтому не требуется вдумываться в алгоритмы сортировки, что-то изобретать или перестраивать алгоритм под свои нужды. Но все-таки такое решение не оправдано, если подходить к этой проблеме с точки зрения пользователя. Вначале посетителю сайта требуется дождаться достаточно длительной загрузки страницы, просмотреть результаты, нажать на кнопку "Отсортировать" и... опять дожидаться, пока сервер закончит работу и вернет результат. Такие множественные перезагрузки страницы никак не способствуют популярности сайта у посетителя. В конце-концов, устав дожидаться очередной загрузки страницы, или испугавшись лишнего траффика, он покинет сайт в поисках более лояльного к нему web-мастера. Решением данной проблемы, а именно эффективной сортировки данных и формирования результирующей таблицы, я и предлагаю заняться в этой статье. А в качестве примера данных будет выступать информация о книгах: дата написания, название книги и ее автор.

    Начнем с функции fillArray, которая и будет эмулировать многомерный массив, а вернее создать класс-объект с членами - данными многомерного массива. Приведем ее код:

    Листинг 1: 1 function fillArray(years, books, authors) { 2 authors = upCs(authors, " "); 3 authors = upCs(authors, "-"); 4 books = upCs(books, ""); 5 6 this.years = years; 7 this.yweight = weight(years); 8 this.books = books; 9 this.bweight = weight(books); 10 this.authors = authors; 11 this.aweight = weight(authors); 12 }

    Разберем данные по строкам. Во второй строке вызывается функция upCs с параметром authors и пробелом в качестве второго параметра. Функция upCs создает заглавные буквы в начале строки и перед каждым вхождением второго аргумента. Эта функция была написана исключительно потому, что не все обрабатывают данные должным образом и вполне могут написать имя писателя, например, с маленькой буквы. Применение такой функции устраняет возможную ошибку программиста в заполнении массива данными, а также позволяет быть уверенным, что в функции сортировки не будет неверного сравнения заглавных букв со строчными. Впрочем, если вы уверены в том, что данные будут занесены верно, можете убрать вызовы этой функции:

    Листинг 2: 1 function upCs(str, param) { 2 var tmpStr = str.substring(0, 1).toUpperCase() + str.substring(1); 3 if(!param) 4 return tmpStr; 5 var separator = tmpStr.indexOf(param); 6 var retStr = tmpStr; 7 if(separator != -1) 8 retStr = tmpStr.substring(0, separator); 9 10 while(separator != -1) { 11 tmpStr = tmpStr.substr(separator + 1, 1).toUpperCase() + tmpStr.substring(separator + 2); 12 separator = tmpStr.indexOf(param); 13 if(separator != -1) 14 retStr += param + tmpStr.substring(0, separator); 15 else 16 retStr += param + tmpStr; 17 } 18 19 return retStr; 20 }

    Эта же функция, вызванная с пустым вторым аргументом, возвращает результат с заглавной буквой только в начале строки. Теперь обратимся к строке 7, листинга 1. Здесь вызывается функция weight, которая возвращает числовые значения каждого символа аргумента в виде массива. Для чего я ее написал? Дело в том, что коды символов русского алфавита в браузере идут последовательно, кроме кода символа буквы "ё". Код этого символа больше кодов символов остального алфавита, поэтому приходится присваивать такое "весовое" значение этому символу, которое соответствовало бы его позиции в алфавите. Вот код этой функции:

    Листинг 3: 1 function weight(str) { 2 var retArray = new Array(); 3 4 for(var i = 0; i < str.length; i++) { 5 var tmp = str.charCodeAt(i); 6 if(tmp >= 1046 && tmp < 1078) 7 tmp++; 8 else if(tmp == 1025) 9 tmp = 1046; 10 else if(tmp >= 1078) 11 tmp++; 12 else if(tmp == 1105) 13 tmp = 1078; 14 retArray[ i ] = tmp; 15 } 16 17 return retArray; 18 }

    Ну что же, функции, необходимые для создания нашего многомерного массива готовы. Осталось создать массив и заполнить его данными:

    Листинг 4: 1 var txt = ; 2 txt[ 0 ] = new fillArray("1959", "фрейд", "сартр жан-поль"); 3 txt[ 1 ] = new fillArray("1940", "подростки", "сэлинджер джером"); 4 txt[ 2 ] = new fillArray("1946", "пена дней", "виан борис"); 5 txt[ 3 ] = new fillArray("1948", "осадное положение", "камю альбер"); 6 txt[ 4 ] = new fillArray("1899", "об иноческой жизни", "рильке райнер мария"); 7 txt[ 5 ] = new fillArray("1849", "аннабель Ли", "по эдгар"); 8 txt[ 6 ] = new fillArray("1917", "дагон", "лавкрафт говард"); 9 txt[ 7 ] = new fillArray("1915", "процесс", "кафка франц"); 10 txt[ 8 ] = new fillArray("1989", "египет Рамсесов", "монтэ пьер"); 11 txt[ 9 ] = new fillArray("1932", "мастер и Маргарита", "булгаков михаил");

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

    Насколько быстр такой метод заполнения массива? На компьютере со средней производительностью загрузка 1000 элементов происходит за 450 - 800 миллисекунд. Но поскольку пользователь будет получать массив такого размера не сразу, а по частям, и размер этих частей будет сильно зависеть от скорости его соединения с интернет, то время, затрачиваемое на создание элемента массива и определение весовых значений его данных, будет незаметно пользователю.

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

    Листинг 5: 1 function quickSort(l, h, type) { 2 var low = l; 3 var high = h; 4 var rt = eval("txt[ " + Math.round((l + h) / 2) + " ]"); 5 var middle = new fillArray(rt.years, rt.books, rt.authors); 6 7 do { 8 9 while(isLow(eval("txt[ " + low + " ]"), middle, type)) 10 low++; 11 12 while(isLow(middle, eval("txt[ " + high + " ]"), type)) 13 high--; 14 15 if(low = 1046 && tmp < 1078) tmp++; else if(tmp == 1025) tmp = 1046; else if(tmp >= 1078) tmp++; else if(tmp == 1105) tmp = 1078; retArray[ i ] = tmp; } return retArray; } function fillArray(years, books, authors) { authors = upCs(authors, " "); authors = upCs(authors, "-"); books = upCs(books, ""); this.years = years; this.yweight = weight(years); this.books = books; this.bweight = weight(books); this.authors = authors; this.aweight = weight(authors); } function isLow(low, high, type) { var len1 = low[ type ].length; var len2 = high[ type ].length; var length = len1 < len2 ? len1: len2; for(var i = 0; i < length; i++) { var str1 = low[ type ][ i ]; var str2 = high[ type ][ i ]; if(str1 < str2) return true; if(str1 > str2) return false; } if(len1 < len2) return true; return false; } function quickSort(l, h, type) { var low = l; var high = h; var rt = eval("txt[ " + Math.round((l + h) / 2) + " ]"); var middle = new fillArray(rt.years, rt.books, rt.authors); do { while(isLow(eval("txt[ " + low + " ]"), middle, type)) low++; while(isLow(middle, eval("txt[ " + high + " ]"), type)) high--; if(low

    Есть вопросы?

    Сообщить об опечатке

    Текст, который будет отправлен нашим редакторам: