Сортировка в таблице html.
Зачем перегружать страницу при сортировке HTML-таблицы? Нельзя ли перегруппировать HTML элементы таблицы прямо в браузере, с помощью Javascript?.js (55 строк).
Сортировка таблицы средствами JavaScript - DOMВопрос когда-то с неизбежностью возникает перед любым добросовестным веб-мастером: зачем перегружать HTML страницу при сортировке таблицы? Нельзя ли перегруппировать HTML элементы таблицы прямо в браузере, без серверных скриптов, с помощью одного только 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 , а вот результат его работы:
Общая постановка задачи (приблизительный алгоритм)Следуя хорошей практике ненавязчивого 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
Рано обрадовался. В таком варианте таблицы неправильно определяется реальный индекс колонки второй строки шапки.
Company | TickerPrice Open|||||
5 | 3 | A | B | D | 126.1 |
7 | 2 | A | A | C | 512.59 |
6 | 1 | A | D | B | 47.45 |
8 | 4 | D | C | A | 19.82 |
Лесник 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
Пробую использовать ваш скрипт, все отлично работает если гапка таблицы состоит из одной строки. Мне же нужно сделать сортировку по сложной шапке. Проще объяснить на примере. Есть таблица:
Company | TickerPrice Open||||
3 | A | B | D | 126.1 |
2 | B | A | C | 512.59 |
1 | C | D | B | 47.45 |
4 | D | C | A | 19.82 |
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