Отправить заявку

Производительность css селекторов

Сегодня, во времена готовых решений, библиотек, фреймворков, препроцессоров и множества других, несомненно полезных в умелых руках, инструментов, для большого числа разработчиков механизмы технологий остаются чёрным ящиком. Я не буду занудой и не скажу, что каждый должен понять технологию изнутри, поскольку индустрия также требует низкоквалифицированных рабочих рук, в частности «нарезчиков макетов». Я так же не буду играть в Капитана Очевидность — понятно, что это принципиально важно, если вы планируете стать профессионалом. Делайте ваш выбор сами.

В работе над интерфейсами часто встают вопросы производительности. Существует множество различных инструментов и сервисов для определения узких мест. Один из них — Google Page Speed, который приводит рекомендации по повышению быстродействия JavaScript и скорости загрузки страницы. Перед тем как рассмотреть эти советы, нужно хорошо разобраться с тем, как браузеры работают с CSS.

Как обрабатывается CSS
Стиль элемента применяется при его создании
Мы воспринимаем страницу как единую «картинку», с оформлением и контентом. Но браузеры обрабатывают документы постепенно, потоком. Как только браузер начинает получать документ с сервера, он начинает его обрабатывать, не дожидаясь полной загрузки. Каждый узел обрабатывается и отрисовывается в окне браузера после получения. Рассмотрим простой документ.

<body>
   <div id="content">
      <div class="module intro">
         <p>Lorem Ipsum</p>
      </div>
      <div class="module">
         <p>Lorem Ipsum</p>
         <p>Lorem Ipsum</p>
         <p>Lorem Ipsum <span>Test</span></p>
      </div>
   </div>
</body>

Браузер начинает читать документ сверху и встречает элемент body. В этот самый момент браузер считает, что он пустой, поскольку ничего более пока не обработал. Браузер вычисляет стили (фон, шрифт, цвет и т.д.) для элемента и применяет их. После элемент отрисовывается на экране.

Затем браузер встречает элемент div с id="content". И снова, в этот момент он считает элемент пустым. Браузер вычисляет стили и элемент отрисовывается на экране. Браузер затем определяет, нужно ли перерисовать body – например, стал ли он шире или длиннее (изменение геометрических размеров — наиболее частый эффект воздействия потомка на родителя)?

Процесс продолжается до достижения конца документа.

CSS селекторы читаются справа налево

Чтобы определить, нужно ли применять правило к некоторому элементу, селектор читается браузером справа налево.

Если у вас есть, скажем, правило div#content p { color: #003366; }, тогда для каждого элемента, в момент его обработки браузером, сначала будет проверяться, является ли элемент параграфом. Если он является таковым, тогда браузер пойдёт вверх по DOM дереву и проверит, есть ли div с id="content". Если условие верно, тогда браузер продолжит поиск body.

Такой принцип работы выбран не случайно. Браузер определяет стоит ли применять стилевое правило к элементу на момент прорисовки значительно быстрее. Чем меньше узлов нужно обойти, тем быстрее обрабатывается правило.

Хорошие и плохие селекторы
Взгляните на рекомендации Google Page Speed. Они выделили четыре, на их взгляд, наиболее медленных селектора:

Контекстные селекторы, например #content h3
Дочерние селекторы, например #content > h3
Избыточно уточнённые селекторы, например div#content > h3
IE7-8: Псевдокласс :hover, применяемый не к ссылкам, например div#content:hover

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

. Безусловно, на проектах вроде Gmail это полностью оправдано, однако для куда меньших сайтов следует искать баланс между этими рекомендациями и здравым смыслом.

Универсальный селектор * {…}

Говорят, универсальный селектор нельзя использовать, потому что он очень медленный. Чушь собачья! Он медленный только в составе контекстного селектора, например #content p *. Сам по себе не хуже селектора элемента, класса или id.

Как быть?
Чаще всего в моей практике такая проблема решается сама собой. Поскольку обычно наши проекты подвергаются постоянным изменениям, в подавляющем большинстве случаев мы используем классы. Блокам, внутри которых выводится контент, отредактированный через WYSIWYG редакторы, присваивается определённый класс, от которого посредством контекстных селекторов задаются стили некоторых элементов (параграфы, списки, таблицы и др.)

Вместо заключения
А вообще говоря, так ли важно на практике следовать этим, далеко не очевидным, правилам? Зачем тогда вся оставшаяся армия селекторов, если тру-селекторы можно пересчитать по пальцам одной руки? Как я уже упоминал, во всём должен быть баланс, основанный на здравом смысле. Почему бы не сэкономить пару-тройку сотен миллисекунд, если это ничего не стоит! Однако будет безумием тратить на это часы разработки и тестирования.
В то же время, замечали ли вы когда-нибудь на страницах с новомодными приклеивающимися шапками и колонками тормоза? Это результат большого времени, которое необходимо на перерисовку при скроллинге страницы.

Другие публикации