Россия, Сочи, РГПУ им. А.И.Герцена, 1997 |
CSS-оптимизация
Во-первых, для IE и Firefox (наиболее популярных браузеров) функция эта работает некорректно (в общем случае возвращает неверные данные). Во-вторых, работает она чуть ли не медленнее, чем offsetHeight.
Вообще говоря, рекомендуется не пользоваться такими универсальными функциями ( getStyle есть практически в каждой JavaScript-библиотеке), а реализовывать необходимую функциональность в каждом конкретном случае. Ведь если мы договоримся, что скрытые элементы должны иметь класс hide, то все сведется к определению наличия этого класса у элемента или его родителей.
Оптимизация: определение класса hide
Давайте подробнее остановимся на предложенном мной решении. Предлагаю следующую реализацию:
function isHidden(el) { var p=el; var b=document.body; var re=/(^|\s)hide($|\s)/; while(p && p!=b && !re.test(p.className)) p=p.parentNode; return !!p && p!=b; }
Предполагается, что корневые элементы DOM скрывать не имеет смысла и поэтому проверки ведутся только до document.body.
Предложенное решение явно не спустит лавину reflow, так как никаких вычислений и измерений не проводится. Однако немного смущает проход до корня документа: что же будет при большой вложенности элементов? Давайте проверим. Тест isHidden проводится для вложенности 2 ( document.body / test_div ), а тест isHidden2 — для вложенности 10 ( document.body / div * 8 / test_div ).
IE sp62 | Firefox 2.0.0.12 | Opera 9.22 | Safari 3.04b | |
---|---|---|---|---|
offsetHeight | 23500 | 10624 | 4453 | 5140 |
isHidden | 231 | 351 | 70 | 71 |
isHidden2 | 370 | 792 | 212 | 118 |
offsetHeight vs. isHidden | 102 раза | 30 раз | 73 раза | 92 раза |
Как показывают тесты, даже при большой вложенности падение скорости невелико. Таким образом, мы получили универсальное решение, которое быстрее доступа к offsetHeight в 30–100 раз.
Заключение
Все вышеприведенные мысли предназначены не столько для решения проблемы выяснения видимости элемента в общем случае, сколько для объяснения одного из наиболее часто встречающихся узких мест взаимодействия с DOM и детального разбора методов оптимизации. В ходе тестов был намеренно воспроизведен наихудший случай. В реальных ситуациях такой прирост скорости получится только при использовании в анимации. Однако понимание причин и механизма reflow позволяет писать более оптимальный код.
В качестве послесловия: стили или классы?
В заключении давайте затронем еще несколько оптимизационных моментов, связанных с отображением HTML-страницы на экране браузера. Пусть в нашем документе есть элементы, у которых нужно поменять цвет, фон или что-нибудь еще, относящееся к стилям. Например, подсветить строки таблицы при наведении мыши или пометить их, если выбрана соответствующая галочка в форме.
Существует два способа это сделать: при помощи стилей или установив цвет (или фон) напрямую из JavaScript. Для начала немного кода — с помощью класса:
var items = el.getElementsByTagName('li'); for (var i = 0; i < 1000; i++) { items[i].className = 'selected' }
И с помошью стилей:
var items = el.getElementsByTagName('li'); for (var i = 0; i < 1000; i++) { items[i].style.backgroundColor = '#007f00'; items[i].style.color = '#ff0000'; }
Результаты простые и понятные:
Метод | IE 6 | IE 7 | Firefox 1.5 | Firefox 2.0 | Opera 9 |
---|---|---|---|---|---|
element.className | 512 | 187 | 291 | 203 | 47 |
element.style.color | 1709 | 422 | 725 | 547 | 282 |
Перерисовка страницы
Однако когда мы изменяем класс элемента, код отрабатывает значительно быстрее, но вот страница обновляется медленно. Это все из-за того, что изменение свойства className не перерисовывает страницу мгновенно, вместо этого браузер просто помещает событие обновления в очередь reflow. Отсюда и огромная скорость, казалось бы, более сложной процедуры. А что по поводу :hover? К сожалению, :hover работает только для ссылок в Internet Explorer 6. Поэтому в любом случае придется пользоваться какой-то его эмуляцией.
Из всего вышеперечисленного можно сделать два ключевых вывода
- Используйте className везде, где это возможно. Это дает больше гибкости и контроля над внешним видом сайта.
- Если на странице много элементов в контейнере и необходимо построить очень быстрый интерфейс, стоит устанавливать стили напрямую через свойство style.
Групповое изменение стилей
Если мы уже задумались над максимально быстрым изменением интерфейса нашего веб-приложения (отрисовке) через свойство style, то стоит иметь в виду следующий момент. Мы можем изменять свойство cssText, которое отвечает за компилируемые стили элемента:
element.style.cssText = "display:block;width:auto;height:100px;...";
Таким образом, мы можем дополнительно ускорить наше единовременное обновление стилей у элемента, потому что произойдет всего одно присвоение свойств и всего один reflow (и он случится сразу же после изменения этого свойства, а не в отложенном режиме).
Два слова о таблицах
Таблицы замечательно подходят для организации информации. Однако если в HTML-документе встречается таблица, то браузеру приходится пробежаться по ней дважды: в первый раз — чтобы выбрать все элементы, рассчитать их взаимные размеры, и чтобы отрисовать их все — во второй раз. Если на странице выводятся большие массивы данных (например, параметры товаров или статистические данные), то гораздо быстрее будет визуализировать такие таблицы в один проход.
Давайте рассмотрим, как можно помочь браузерам в их нелегком труде. Следующие действия позволят начать отображение таблицы еще до того, как будет получена вся информация о ней.
- Необходимо установить для table CSS-атрибут table-layout в значение fixed.
- Затем явно определить объекты col для каждого столбца.
- И установить для каждого элемента col атрибут width.
В качестве примера можно привести такой фрагмент кода:
<table style="table-layout: fixed"> <!-- первый столбец имеет ширину 100 пикселей --> <col width="100"></col> <!-- второй — 200 --> <col width="200"></col> <!-- третий и четвертый — по 250 --> <col width="250"></col><col width="250"></col> <thead>...</thead> <tfoot>...</tfoot> <tbody>...</tbody> </table>