Зачем перегружать страницу при сортировке HTML-таблицы? Нельзя ли перегруппировать HTML элементы таблицы прямо в браузере, с помощью Javascript? Решение предлагается в файле http://ir2.ru/static/tabsort.js (55 строк).

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

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

  1. HTML элементы таблицы априори можно менять друг с другом местами (в т.ч. сортировать) средствами DOM-Javascript;
  2. подобные манипуляции с DOM Node можно (и, в идеале, нужно!) производить с помощью кода Javascript очень небольшого размера, понятного и «прозрачного»;
  3. на каком-то популярном ресурсе нам уже встречалась довольно большая таблица, сортируемая без перезагрузки;
  4. невероятно, чтобы по такому очевидному вопросу никто ничего не написал в Интернете.

Приступив к детальному исследованию, мы, в первую очередь, нашли подозреваемый ресурс; это оказался форум 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, а вот результат его работы:

Ntitleidfilename
1DHTML: условная разметка376dhtml5edit
2DHTML: защита от спама375dhtml4spam
3DHTML: хранение настроек37dhtml3opt
4DHTML: валидация формы7dhtml2form

Общая постановка задачи (приблизительный алгоритм)

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

Заголовки. «Полосатая» таблица.

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

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

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

Код функции 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 закомментирован):

По причине структурной сложности самой таблицы, примерно так же находится сам элемент 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 для последующей сортировки:

Мы намеренно оставили закомментированную ошибочную строку: не надо удалять строки таблицы после помещения в массив Javascript! Как выяснилось в дальнейшем, система взаимодействия DOM с Javascript в нашем случае сильно упрощает работу:

  1. Массив Javascript, составленный из строк таблицы, является только набором ссылок на объекты DOM (строки таблицы), массив НЕ копирует объекты DOM!
  2. При сортировке массива все строки (объекты DOM) остаются на месте, сортировка – просто создание новых («виртуальных») индексов.
  3. При обращении к элементу массива rarr[j] через Javascript мы всё-таки обращаемся к DOM: если, например, создать новую таблицу и попытаться присоединить к ней один из элементов (newTable.appendChild(rarr[1])), этот элемент (на который указывает ссылка rarr[1]) будет «физически» удалён из старой таблицы и помещён в новую, т.е. он будет перемещён.

Что позволяет нам после сортировки массива очень легко привести все строки к новому порядку следования – мы просто перебираем в цикле отсортированный массив и добавляем через обращение к его элементам каждую строку в конец нашей таблицы (при этом она исчезает со старого места, перемещается):

Функция striptags состоит всего из одной строки: str=str.replace(/\<[^\<\>]+\>/ig,"");.

В нашей «пользовательской» функции сортировки массива cmprow важен, пожалуй, только один момент – использование toLowerCase(), приведение сортируемых строк к нижнему регистру (если сортируется строковый тип данных). Это нужно для регистронезависимой сортировки, которую мы выбрали по умолчанию (так как именно она чаще всего и требуется). Ещё важнее, однако, учитывать арифметику: функция примитивного сравнения «каждого элемента с каждым» должна в самом плохом случае (теоретически) вызываться n*(n-1)/2 раз (где n – число элементов). В Javascript, конечно, это всё как-то оптимизировано, поэтому, например, для таблицы в 400 строк функция сортировки вызывается примерно 3200 (а не ожидаемых 80000) раз; но всё равно пользовательскую функцию сортировки надо максимально освободить от анализа данных.

Возвращаясь к общей постановке задачи (пункт 5), можем сказать, что при использовании предварительной сортировки отдельного столбца (а не сразу строк таблицы), время работы скрипта у нас получилось точно таким же, поэтому мы остановились на более «прямом» варианте работы, при котором поначалу данные сортируемой ячейки извлекались внутри функции сортировки. Позже на ум пришла здравая мысль: всё равно в сортируемом массиве лежат объекты, так почему бы к каждому объекту не добавить по ещё одному свойству – готовой для сортировки строке (чтобы получать и преобразовывать её в цикле-400, а не в цикле-3200)? Это свойство мы назвали sortcell.

Перспективы

Надеемся, что в наш сортировщик несложно добавить сохранённые в куки начальные параметры (как описано в одной из предыдущих статей). На наших сайтах это, в общем-то, не требуется (как и вообще на сайтах с правильным HTTP-кэшированием) – при возвращении на страницу по Alt+Left таблица остаётся в отсортированном виде. Восстановление куки-параметров, разумеется, имеет смысл, если на странице расположена только одна HTML-таблица. На этот случай в инициирующих строках кода мы закомментировали (типа зарезервировали) создание массива для сортировки. То есть до начала щелчков пользователя по таблице. Туда же можно добавить и процесс сортировки по извлечённым из куки параметрам.

Что это, однако, могут быть за параметры? Как можно записать состояние таблицы, если в ней, например, был отсортирован сначала 1-й столбец, потом 3-й, потом 3-й задом наперёд, потом опять 1-й?.. Думаем, что вот только так и можно – записав ВСЕ действия, которые привели таблицу к такому состоянию. Записывать можно рядом чисел - индексов сортируемых столбцов; для обратной сортировки использовать отрицательные числа; сокращать записи по правилу: 1,-1,1 => 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, «Деловая неделя», Михаил Гутентог

Комментарии

Артем 22.03.2013 19:46:10

А вот, кому интересно, мой пример javascript сортировщик таблиц с настройками и callback – функциями: http://php-zametki.ru/javascript-laboratoriya/114-javascript-sort-table.html

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

Gitaman,

в скрипте страницы http://ir2.ru/static/sort1/table2000_1.htm есть несколько неточностей. Я не могу так с ходу показать их пальцем, просто не помню. Я их вычищал года полтора. Теперь сильно изменённый скрипт используется в коммерческой CMS Бикубик. Пример можно посмотреть на стр. http://arcadia-irk.ru/constructor/config.asp (логин: admin, пароль: admin)

Gitaman 11.01.2013 12:05:03

Вот этот пример брал за основу http://ir2.ru/static/sort1/table2000_1.htm

Gitaman 11.01.2013 12:01:01

Здравствуйте возникает проблема с сортировщиком никак не могу разобраться использую скрипт sorttable1 html страница с сортировкой, с общим поиском на 2000 позиций. Что я сделал... начал прикручивать к своей таблице в, которой увеличено количество колонок до 15, вроде всё шоколадно подумал я... отсортировав 1 колонку и произведя по ней поиск, всё работает, так же работает общий поиск, но остальные колонки ведут себя очень странно нажимаю по 5, а сортируется 3, нажимаю по 7, а сортируется 5, тоже самое происходит и с поиском внутри колонок хотя подсветка работает внутри сортируемой мной колонки. Голову сломал, что не так сделал ума не приложу, может что конечно где проглядел? Подскажите пожалуйста... Буду очень признателен! Заранее спасибо.

Дилетант 28.11.2012 03:25:41

Igor,

в скрипте есть строка: vi = Math.round(100 * parseFloat(v)).toString()

- в ней надо обработать v, например, написать вместо просто v: v.replace(/\s+/g, '') – если у вас разделители цифр – пробелы. Тогда нормально сработает parseFloat

Igor 27.11.2012 08:23:59

Блин, все нормально сортирует, кроме цифр с разделением <? php echo number_format ($number); ?>

Funky_Rusher 18.06.2012 21:36:05

Проблема после обновления tbody через ajax – сортируются старые сроки (которые были до обновления), а новые исчезают. Как это исправить?

лесник 13.06.2012 14:39:23

xchesh, не может быть :-). Хотя на самом деле – может. Я не тестировал скрипт с динамически подгружаемыми таблицами, но ясно, что тут проблема в идентификации таблиц и их сортировочных массивов. Для начала надо добавить проверку существования сортировочного массива t.sarr в строку if (i !== t.sorted).

xchesh 10.05.2012 19:43:10

Хотя есть один баг. Если загружать таблицу заново(загружать новую по средствам ajax), но сортировать по тому же столбцу, то предыдущая таблица будет добавляться к новой и только она[предыдущая] будет сортироваться. Хотя если сортировать по другому столбцу – все работает нормально.

xchesh 10.05.2012 19:34:04

Отличный скрипт. То что нужно. Работает с динамически подгружаемыми таблицами! И что важно очень мало весит! Лучшее что можно найти и применить.

лесник 21.02.2012 20:13:52

Вот это я слажал! добавил 7 одинаковых сообщений. Добавляю, а оно не отображается, добавляю, а его так и нет... А оказалось, там limit на отображение стоит!

лесник 21.02.2012 20:03:15

nik_neman, вы не сможете это сделать на javascript (мало знаний). Я даже не понял из вашего описания, что надо сделать. Лучше попробуйте внедрить баннеры на стороне сервера, при генерации HTML.

Да, а Big Table Sorter в любом случае лучше использовать последний: http://ir2.ru/static/sort1/tabsort1.01.zip (описание: tabsort1_01.aspx)

nik_neman 17.02.2012 01:37:17

День добрый. Использую Big Table Sorter v2.1 (+ JS Data Filter). Данные перед этим вывожу из БД с помощью PHP+MySql... через каждые 10 строк необходимо выводить баннер, названия которого совпадает с текстом предшествующей строчки... решил начать делать постепено и, вывести 10 строчку и 3 столбец... с помощью данного кода: <script type="text/javascript" languages="javascript"> var l = document.getElementById("banners").rows[10].cells[2].innerHTML; document.write(l); </script> но мне выводит название, которое хранится в БД. СОРТИРОВКА не учитывается. В 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-элемент - таблицу, которую нужно сортировать.

Позже сделаю это для Big Table Sorter

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

Всё, сделал возможность подключать Сортировщики к динамически созданной таблице в обеих версиях: Simple Table Sorter и Big Table Sorter. Как подключать, написано (с примером) в статье 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

Рано обрадовался. В таком варианте таблицы неправильно определяется реальный индекс колонки второй строки шапки. <table class="sortable" border="1"> <thead> <tr> <th rowspan="2">id0</th><td colspan="3">Company</td><th rowspan="2">Ticker</th><th axis="num" rowspan="2">Price Open</th></tr> <tr> <th>id1</th><th>Name</th><th>Name1</th></tr> </thead> <tbody> <tr><td>5</td><td>3</td><td>A</td><td>B</td><td>D</td><td>126.1</td></tr> <tr><td>7</td><td>2</td><td>A</td><td>A</td><td>C</td><td>512.59</td></tr> <tr><td>6</td><td>1</td><td>A</td><td>D</td><td>B</td><td>47.45</td></tr> <tr><td>8</td><td>4</td><td>D</td><td>C</td><td>A</td><td>19.82</td></tr> </tbody> </table> Можно ли как-то уникально идентифицировать ячейку кроме 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 18:04:24

tabsort1.3a.js проверен: можно сохранять состояние сортировки. Пример в http://ir2.ru/static/thead.htm (с многострочным заголовком)

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

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

MrTopa 23.09.2010 00:29:38

Пробую использовать ваш скрипт, все отлично работает если гапка таблицы состоит из одной строки. Мне же нужно сделать сортировку по сложной шапке. Проще объяснить на примере. Есть таблица: <table> <thead> <tr> <td colspan="3">Company</td><th rowspan="2">Ticker</th><th rowspan="2">Price Open</th></tr> <tr> <th>id</th><th>Name</th><th>Name1</th></tr> </thead> <tbody> <tr><td>3</td><td>A</td><td>B</td><td>D</td><td>126.1</td></tr> <tr><td>2</td><td>B</td><td>A</td><td>C</td><td>512.59</td></tr> <tr><td>1</td><td>C</td><td>D</td><td>B</td><td>47.45</td></tr> <tr><td>4</td><td>D</td><td>C</td><td>A</td><td>19.82</td></tr> </tbody> </table В ней нужно сортировать по колонкам с тегом th, а td в шапке игнорировать. С JS толком не знаком, а сделать нужно. Не подскажите как такую сортировку можно реализовать?

лесник 26.08.2010 15:00:37

Clusta, кнопочку – нет, устал уже придумывать куда что пихать (и так громоздко получается). Можно по Esc. Ну, и, мне казалось, что достаточно очевиден "самосброс" фильтров при отсутствии результатов отбора (когда поле ввода покрывается красными буквами, ну, или хотя бы курсор в пустом поле краснеет).

p.s. В новой теме (JS фильтры) комментарии теперь добавляются.

лесник 26.08.2010 14:52:48

player0, так вроде есть такая возможность. Описана в секции «Использование», п.4 (стр. 11) скрипта tabsort.js:

«num|datru|daten ... следует вписывать в ячейку заголовка как атрибут: <th axis='num'>» (строковый тип можно вписать как 'str', а можно совсем не вписывать – он по умолчанию).

player0 26.08.2010 04:50:02

я нехочу ничего советовать, и так всё круто, спс ещё раз. но лично для себя я бы добавил возможность заранее определять тип стольбца – текст или цифры – а то писать кругом, чтобы зажимали ctrl – как то не очень. да и не все читают.

Clusta 26.08.2010 00:00:30

//отписался здесь, т.к. в той (новой) теме сообщения не отправляются :(

Clusta 25.08.2010 23:59:34

круто! всё ближе и ближе к "идеалу" (идеал без кавычек – не достижим) :)

ещё бы добавить кнопочку "сбросить фильтр" :)

Clusta 25.08.2010 03:07:59

не совсем то я имел в виду. допустим, в строках есть столбец "Цена" в соответствующих ячейках самые разные цены. Было бы удобно выбрать из выпадающего списка диапазон цен (от 100 до 1000 руб, от 1000 руб до 2000 руб, от ....., больше 10000 руб). как-то так.

лесник 24.08.2010 19:14:18

Всё, фильтры к таблицам прикручены. Только получилось много, поэтому написал новую статью с описанием и примерами: JS фильтры (отбор данных HTML).

лесник 21.08.2010 22:06:58

Clusta, вы намекаете, что в вашей HTML-таблице 1000 строк? Фильтры для таких больших таблиц обычно делают на сервере. В принципе, можно, конечно, прикрутить к HTML и ваши фильтры. Но это будет не универсально (почему именно 100-300? кому-то из разработчиков ведь может понадобится и 100-150).

Если вам интересно сортировать-фильтровать (искать) именно на Javascript, посмотрите нашу электронную газету :-) – dn.ir2.ru/dn6.zip. Архив небольшой, килобайт 200. Там файл CHM (его можно раскомпилировать, например, с помощью утилиты chmview); внутри система поиска товаров на javascript. Посмотреть онлайн (только в ИЕ!) можно на странице dn.ir2.ru/bd/, скрипт в файле dn.ir2.ru/bd/files/program.txt.

Как работать с базами данных на Javascript в других браузерах, я не знаю (вернее, подозреваю, но лень копаться).

лесник 21.08.2010 21:46:59

player0, похоже, это настоящий баг Оперы 10.61: она произвольно (от случая к случаю) удаляет объекты javascript, привязанные к DOM-элементам. Пришлось обрабатывать эту возможную ошибку. Теперь работает и в Опере (только может теряться состояние сортировки для отдельных таблиц).

В старых версиях tabsort.js, как вы помните, несколько таблиц перемешивались, именно потому что был один глобальный массив сортировки. Сейчас такой массив у каждой таблицы свой, привязанный к объекту table как свойство (table.rarr). Опера вдруг ни с того ни с хрена удаляет свойства .rarr у некоторых таблиц на странице (если таблиц больше одной).

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

Clusta 21.08.2010 03:42:25

А как прикрутить к таблице фильтр? например, к столбцу ID дропдаун-селект со значениями <100, 100-300, 300-1000, >1000

как-то так.

player0 20.08.2010 19:38:27

да уж странно.. проблема оказалась в опере – на всём остальном работает. у меня опера 10.61 (дальше не обновляет). старые версии tabsort.js шли норм; ir2.js обновлять не забывал. может это у меня опера криво стоит? вроде проблем не замечал. отпишитесь кто-нибудь, у кого работает.

лесник 20.08.2010 16:12:03

А на тестовой странице (http://ir2.ru/static/tabsort2.htm) тоже "после нескольких кликов перестают"? Ну, не знаю... Добавил сейчас туда ещё две таблицы (всего стало 4), пробую в ФФ, ИЕ8, Опере(9.50) – везде все 4 таблицы сортируются, пока рука не устанет. Может, у вас файл ir2.js старый? Попробуйте скачать оба файла .js заново и обновить кэш браузера.

player0 20.08.2010 00:26:56

к сожалению проблема так и осталась. выставил false – не помогло.

"нормально сортируется только первая таблица на странице. остальные после нескольких кликов перестают"

лесник 17.08.2010 20:35:35

Исправлено сохранение сортировки (имена куки для таблиц связаны с адресом страницы). Добавлена возможность удалять куки для текущей таблицы (очистить историю сортировки): для этого нужно щёлкнуть по заголовку таблицы, удерживая нажатыми клавиши Ctrl+Alt+Shift.

player0, ваша проблема тоже, надеюсь, исправлена (она возникала, скорее всего, из-за неупорядоченных куки). Если интересно точно выяснить это, попробуйте в своей версии в разделе "Настройки" поставить use_cookie = false. Ну, и в исправленной, сегодняшней (17.08.10) тоже можете ставить use_cookie = false, если сохранение параметров сортировки не нужно (а то ведь придётся пользователям давать инструкцию по удалению куки с помощью Ctrl+Alt+Shift+Click).

Не забывайте вместе со скриптом tabsort.js скачивать библиотеку ir2.js – иначе возможны ошибки!

player0 16.08.2010 21:22:00

заглянул сегодня за новой версией скрипта :)

в tabsort 1.2 нормально сортируется только первая таблица на странице. остальные после нескольких кликов перестают. поэтому пока – пользуюсь старой.

лесник 16.08.2010 19:48:30

therocksize, рано радуетесь! :-) Там есть подводная бомба. Имена куки создаются из номера (DOM, HTML-коллекция) таблицы. Если таблица с таким же номером встретится на другой странице, скрипт будет пытаться отсортировать её так же, как и на первой странице.

Через сегодня-завтра постараюсь исправить это (добавлю к именам куки адреса страниц).

therocksize 16.08.2010 03:34:25

D.M., admin , огромное спасибо!!! =)

лесник 14.08.2010 21:41:04

Готова очередная версия tabsort.js – 1.2. В файл скрипта добавлены (стр. 20) настройки параметров use_title (использование всплывающих подсказок) и use_cookie (сохранение-восстановление предыдущей сортировки). Сейчас use_cookie = true, поэтому в тестовых таблицах tabsort2.htm при перезагрузке страницы состояние сортировки сохраняется. Можно отключить, если поставить use_cookie = false. Операция восстановления (как указывалось в тексте статьи) может оказаться затратной (она просто повторяет все предшествующие действия пользователя по сортировке). Если кто-то придумает более оптимальный алгоритм, – поделитесь!

therocksize 13.08.2010 19:50:39

Все верно! Спасибо! Буду ждать :)

лесник 13.08.2010 16:46:04

therocksize,

вообще говоря, сортировку ДО открытия страницы лучше делать на сервере (средствами MySQL-PHP) и выводить данные сразу в нужном порядке.

Как «запоминать» пользовательскую сортировку, я уже говорил: если коротко – с помощью cookie (в комментариях скрипта tabsort.js даже указано место, где следует использовать информацию из cookie). Понятно, что перед этим в куки следует что-то записывать (в момент, когда происходит сортировка). Понятно также, что раз вы спрашиваете об этом, значит, самостоятельно сделать пока не можете :-). Ну, через день-другой, думаю, сумею уделить этому время (как сделаю, сообщу здесь).

therocksize 12.08.2010 22:48:23

Спасибо за материал! Можно узнать как задать сортировку по умолчанию? Мне надо чтоб при обновлении страницы сортировка уже произошла. Помогите пожалуйста !

Clusta 12.08.2010 17:39:52

3) ...у неактивных title "Отсортировано..." оставил не намеренно, а по недосмотру ;) в IE показывается всё как надо, поэтому даже не обратил внимания...

спасибо за советы по улучшению

лесник 12.08.2010 17:11:09

Ну что ж, очень даже неплохо для начинающего. Когда я был маленьким крокодилом, у меня не было столько терпения, чтобы разбираться в чужом коде.

1) В функции preptabs() выбрать все ячейки (TH или TD – всё равно) строки заголовка лучше, наверное, «родным» методом «интерфейса таблицы» – .cells:

2) В функции clicktab() можно выбрать ячейки строки заголовка ещё короче. Событие click ведь «навешено» именно на эту строку, значит, её можно вызвать из функции словом this:

3) Не знаю, намеренно ли вы оставляете title «Отсортировано...» у неактивных (по сортировке) колонок, но мне идея понравилась. Думал как раз, как бы отмечать колонки с предыдущей сортировкой: то ли удваивать стрелки, то ли уменьшать их, то ли цвет заголовка менять... Потому что это, конечно, имеет значение – в каком порядке щёлкать по заголовкам, и было бы удобно, чтобы пользователь видел, по каким столбцам он уже щёлкал.

title всё-таки как-то не очень. Добавляю курсив к отсортированным (addclass(th[jj], "i") - в файле .css должно быть соответствующее правило типа .i {font-style:italic;}), а вместо ваших className правильнее использовать какой-нибудь «ненужный», или даже несуществующий атрибут (изменил на rel).

Clusta 11.08.2010 18:50:51

в яваскрипте не силён, только начал изучать (с поиска готовых работающих решений) :)

и пока ждал ответа несколько по-своему решил задачу:

в function preptabs() дописал после определения стиля курсора и перед раскраской "зеброй"

далее, в function clicktab(e) перед закрывающей скобкой (после алерта с учетом времени):

ну и соответственно в CSS создал стили для заголовков таблицы с нарисованными стрелками (три состояния: обе стрелки неактивны, сотрировка "вниз" и сортировка "вверх")

буду рад, если будут подсказки, как улучшить моё решение :)

лесник 09.08.2010 20:13:29

Clusta,

1) что-то я сомневаюсь насчёт важности tbody. По-моему, это понятие вообще нигде явно в скрипте не используется. Есть только переменная tbody со значением "последний элемент таблицы" – обычно им как раз и является элемент tbody, хотя таких элементов в таблице может быть несколько. Ну, получается, после сортировки будет только один элемент.

2) насчёт darr – uarr... Ну, если коротко и грубо (навскидку), то после строки 79 скрипта http://tabsort.js ( if (!obj) return;) нужно вставить примерно такие строки:

А ещё нужно будет удалять стрелки у предыдущих колонок. Это можно сделать, например, вставив после строки, ставшей 100 ( var i=obj.cellIndex; /*номер сортируемой колонки*/), примерно следующее:

Вроде бы всё это я в скрипт tabsort.js сейчас добавил. Кажется, работает (на примере в этой странице).

P.S. Ну, там в коде, разумеется, должны быть не стрелки, а "сущности": &darr; и &uarr;

P.P.S. Спасибо за подсказку (идею со стрелками)!

Clusta 07.08.2010 00:12:28

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

Clusta 05.08.2010 21:52:54

сорри за невнимательность. не в том месте прописывал <tbody> оказывается, это важно ;)

Clusta 05.08.2010 19:31:25

обнаружил в таблице с реальными данными, что если сортировать, допустим, по цене, то сортировка выглядит примерно так: 300 350 400 4000 450 500 5000 и т.д.

как это исправить?

D.M. 06.07.2010 01:34:34

Да, блин. Но оно работало! Честное слово, работало! :-) Проблема возникла из-за постоянных переделок библиотеки ir2.js (по потребностям других проектов). Сейчас исправил, тестовая работает, на стр. http://dn.ir2.ru/mmedit/ тоже работает.

лесник 18.06.2010 03:27:48

Ну, всё. Готова новая версия Simple Table Sorter – 1.1 (файл тот же – tabsort.js). Можно указывать тип сортировки всего поля в ячейке заголовка, в атрибуте axis: <th axis="num">. Типы могут быть следующие: num|datru|daten – число, русская дата (день.месяц.год), буржуйская дата (месяц.день.год). Тип "строка" используется по умолчанию (если ничего не назначено).

Пользователь может сам назначать тип данных, игнорируя axis (или его отсутствие):

щелчок с удержанным Shift – Number,

щелчок с удержанным Ctrl – Date (RU),

щелчок с удержанным Alt – Date (EN) – по остаточному принципу, поскольку удержание одинокого Alt в ФФ, например, не работает.

Заодно найдена-таки и исправлена ошибка "иногда не сортируемой последней строки". Там проблема была тупая: в массив для сортировки строки заносились с номера 1 (0 – заголовок), и заносились под такими же номерами (начиная с 1), вот массив и получался кривой.

Тестировать новую версию можно на странице tabsort2.htm

player0 29.06.2010 01:16:19

ещё раз благодарю)

1 05.07.2010 00:24:33

a вот кстати новая не работает (даже в тесте)

лесник 16.06.2010 18:08:17

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

Вопрос о числах не так прост. Сейчас используется такой алгоритм: проверяются все значения столбца, и если все они – числа, тип сортировки «число»; иначе – строка («как винда»).

А как надо делать? Видел на одном форуме другую схему: в заголовочной ячейке явно прописывается тип в атрибуте (style="sort:string"). Но мне не очень нравится. Можно проверять тип значения только у первой ячейки; но тогда где гарантия, что все остальные ячейки не будут другого типа?

Больше всего склоняюсь к мысли «произвольного управления» сортировкой: например, при нажатой клавише SHIFT рассматривать тип столбца как «число», а при нажатой «ALT» – русская дата... Ну, в спецификацию можно добавить также и (необязательное!) условие обозначать тип данных в заголовке (только, конечно, не в атрибуте style – для этого существует атрибут axis). Приоритет будет у клавиш пользователя. Да, пожалуй, так. Завтра же и займусь.

player0 16.06.2010 05:59:20

рас уж вы такие ответственные, и отвечаете на комментарии :)) то вот ещё пара моментов из личного опыта, с которыми возникают неудобства: я заметил, что скрипт может сортировать числа 2-мя способами – тупо по алфавиту (как винда)т.е.: 1 14 15 18 193 2 23 271 и т.д

и как "числа" (по нормальному) 1 2 3 11 24 136 .... для второго способа в сортируемой колонке не должно быть никаких символов, кроме цифр. но очень часто нужно , например, поставить знак – , так как значение отсутствует, или например знак ?, так как значение неизвестно, ну и т.д. и тут то нужная сортировка перестаёт работать :(( приходится хитрить и ставить либо 0 либо просто ничего.

лесник 14.06.2010 18:28:31

Да, действительно оказалось перемешивание двух таблиц. Раньше не попадал в такие ситуации, потому что, видимо, не так активно тестировал (не очень нужно было несколько таблиц на странице сортировать).

Происходило потому, что сортировочный массив rarr был глобальным (для всего окна). Сейчас исправил: сделал у каждой сортируемой таблицы новое свойство – свой массив rarr.

лесник 14.06.2010 00:20:55

player0,

спасибо за отзыв! Да, чтобы скрипт заработал надо всё сделать именно так:

1) подключить к странице два скрипта (библиотеку ir2.js и tabsort.js) (лучше вставлять их в элемент HEAD);

2) к тегу TABLE сортируемой таблицы (их может быть несколько) добавить атрибут class='sortable'.

Непонятно, правда, что там с последней строкой и почему не сортируются две таблицы. Вообще скрипты уже немного изменились. Проверяю их работоспособность (почти каждый день) на стр. http://dn.ir2.ru/mmedit/. Можете проверить сами – например, введя в окно запросов такой текст:

select * from ir2_dn.`msgs` order by id desc limit 5;

select * from ir2_dn.`msgs` order by id limit 5;

- полученные на экран две таблицы сортируются нормально и независимо друг от друга.

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

player0 10.06.2010 05:52:54

отличный скрипт+ простота в использовании. пользуюсь им у себя на сайте http://minifaq.clan.su/publ/front_mission_3/front_mission_3_navyki_po_alfavitu/2-1-0-38. иногда возникают проблемы с последней строкой, которая не сортируется. так же не получилось сделать 2 таблицы на одной странице – при сортировке они смешивались. в остальном же – огромное спасибо! ps – из текста немного непонятно, как его установить, поэтому для тех, кому надо (установлено методом тыка :)) <table class="sortable".......... – вставить в начало. прописать 2! скрипта в конце (второй я тупо из сохранённой страницы взял) <script type="text/javascript" src="ir2.js"></script> <script type="text/javascript" src="tabsort.js"></script>