С помощью appendChild совсем несложно сделать аккуратный аналог innerHTML - если "собрать" новый элемент вне дерева DOM, а потом выложить на страницу в готовом виде

Как в большом списке найти слово с очень высокой скоростью?

Простой список. Прямой алгоритм

Начнём с нашего любимого примера – простого (в одну колонку) списка рубрик из справочника предприятий vostsibspravka.ru: listru1.htm. Наиболее очевидный алгоритм отбора слов из списка такой:

  1. Перед началом поиска присваиваем списку (HTML элементу div) класс «условного форматирования» CSS – hidchild (этот класс делает невидимыми все дочерние элементы a списка с помощью правила CSS .hidchild a {display:none;}).
  2. Перебираем дочерние узлы списка (у нас это ссылки), сравнивая их тексты с искомым словом.
  3. Если совпадение найдено, присваиваем совпавшему элементу класс «видимости» (vis) – элементы с этим классом будут отображаться даже если списку присвоен класс hidchild (благодаря правилу CSS .hidchild a.vis {display:block;}).
  4. Если элемент не содержит искомый текст, удаляем у него класс видимости vis.
  5. Если не найдено ни одного совпадения, удаляем у списка класс hidchild (и все ссылки списка становятся видимыми).

На нашем списке (430 элементов) такая схема работает нормально, однако уже видно глазом, что при убирании класса hidchild отрисовка полного списка «подтормаживает» (пусть и на долю секунды, но заметно). Интересно, что измерение времени внутри javascript эту задержку показать не может (так как она возникает внутри другой подсистемы – CSS).

Возникает соблазн скрывать-отображать элементы каким-то другим способом. Например, не «условным», а прямым скрытием элементов. Делаем второй пример, в котором элемент списка не меняет своего поведения по отношению к дочерним (класс hidchild присвоен навсегда): listru2.htm. Алгоритм усложняется:

Перед началом работы предварительно (при открытии страницы) копируем весь список, присваиваем ему навсегда класс hidchild и в невидимом режиме (display:none) вставляем на страницу:

(Где invis_class = 'disnone', invis_child = 'hidchild')

  1. При поиске перебираем дочерние узлы вспомогательного списка, сравнивая их тексты с искомым словом.
  2. Если совпадение найдено, присваиваем совпавшему элементу класс «видимости» (vis) (эти элементы будут отображаться, а все остальные – нет, так как у вспомогательного списка класс hidchild).
  3. Если элемент не содержит искомый текст, удаляем у него класс видимости vis.
  4. Если было найдено хотя бы одно совпадение (результат поиска успешный), удаляем у вспомогательного списка класс невидимости disnone, а основному списку добавляем этот класс.
  5. Если не найдено ни одного совпадения, делаем наоборот: прячем вспомогательный список и отображаем основной.

Глазом видно, что при неуспешном поиске отображение всего списка происходит быстрее, чем в Примере 1. Хотя логирование времени работы показывает, что ищет второй скрипт медленнее, чем первый.

Всё, это предел. Быстрее напрямую работать с DOM (HTML-элементами) невозможно. Да и не нужно, если список маленький. А если большой, необходимо так называемое «кэширование»: например, помещение списка в пямять компьютера в виде javascript-массива (а не в виде DOM-дерева).

Простое кэширование списка (первый уровень)

В первом приближении создать из HTML-элементов «виртуальный» список для поиска можно так:

Поиск в цикле по полученному массиву (warr) будет идти в разы (а то и в десятки раз) быстрее, чем по реальным HTML-элементам. Но что делать с найденными значениями? Они ведь никак не связаны с самими элементами списка. Значит, надо создавать как минимум два массива с одинаковыми индексами: один для поиска, а второй для выдачи элементов по найденным индексам.

В нашем усовершенствованном скрипте будет всё-таки один массив, но каждый элемент в нём является объектом с двумя значениями: текст для поиска (value) и элемент списка (html_el):

Теперь мы можем гораздо быстрее найти все элементы, в которых есть нужный текст.

Вторая часть кэширования (только полезные + innerHTML)

Ещё одна проблема возникает, оттого что в цикле поиска мы обрабатываем все HTML-элементы: и полезные (найденные) и ненужные. Если в элементе есть искомый текст, назначаем элементу один класс, если нет текста – другой (точнее, добавляем и удаляем класснэйм vis):

И всё это в режиме реального времени, прямо с существующими на странице элементами.

Быстрее будет 1) обрабатывать только полезные элементы (а к ненужным вообще не обращаться), 2) собрать все найденные (полезные) элементы в один объект не на странице, а в новом, ещё не присоединённом к DOM элементе, и потом отобразить этот элемент сразу весь (а не по кусочкам).

Для этого в HTML-код страницы вставляем перед списком пустой (почти:-) элемент div (id = links2) – в этот элемент будем вставлять сгенерированный список элементов; перед началом поиска создаём новый элемент-контейнер для накопления найденных:

В цикле поиска складываем в контейнер полезные (содержащие искомый текст) элементы списка:

По окончании цикла присоединяем наполненный (или нет) контейнер к существующему на странице links2 и скрываем (или отображаем – в зависимости от успеха поиска) основной список links:

Как это всё вместе работает, можно посмотреть в примере listru3.htm (javascript – в файле listru3.js). Особенно заметны отличия (в скорости) от первого варианта listru1.htm на медленном компьютере, в каком-нибудь IE6. Да, и это ещё не предел. Список ведь может быть и не 400 строк, а 1400, или даже 14000; и не список, а целая таблица. Как ускорить поиск в этом случае, читайте в следующей статье.

D.M., admin

Комментарии