CSS-селекторы

Вопросы производительности

Спецификации

Особенности обработки веб-страниц

Как браузер обрабатывает загружаемую страницу?

Основания для пересчета и возможной перерисовки

Отрисовка страницы при первоначальной загрузке

<body>
	<div id="page">
		<header id="intro">
			<h1>…</h1>
			<p>…</p>
		</header>

	</div>
</body>

Вначале нет ничего, кроме «пустого» элемента <body>

<body>
	<div id="page">
		<header id="intro">
			<h1>…</h1>
			<p>…</p>
		</header>

	</div>
</body>

Затем появляется «пустой» элемент <div>

<body>
	<div id="page">
		<header id="intro">
			<h1>…</h1>
			<p>…</p>
		</header>

	</div>
</body>

После этого — «пустой» элемент <header>

<body>
	<div id="page">
		<header id="intro">
			<h1>…</h1>
			<p>…</p>
		</header>

	</div>
</body>

Получили контент элемента <h1>, перерисовали требуемое

<body>
	<div id="page">
		<header id="intro">
			<h1>…</h1>
			<p>…</p>
		</header>

	</div>
</body>

Получили контент элемента <p>, вновь перерисовали требуемое

<body>
	<div id="page">
		<header id="intro">
			<h1>…</h1>
			<p>…</p>
		</header>

	</div>
</body>

Как анализируются CSS-селекторы?

.content ul li {…}

Обычно справа налево!

القاعدة

Является ли текущий элемент пунктом списка?

.content ul li {…}

Если да, следуем вверх по DOM, ищем элемент <ul>

.content ul li {…}

Нашли? Отлично! Следуем еще выше в поисках элемента с классом content

.content ul li {…}

Быстрые и медленные

Эффективность простых селекторов

Самые быстрые — такие селекторы, которые требуют меньшего количества проверок:

#intro {…}
.content {…}

Более медленные простые селекторы

В порядке снижения производительности, обусловленного возрастанием количества или усложнением проверок:

div {…}
* {…}
[title] {…}
:hover {…}

Комбинированные селекторы

В порядке снижения производительности, обусловленного возрастанием количества проверок:

h1 + p {…}
ul > li {…}
pre code {…}

Внешность обманчива

#intro * {…}
Slowpoke is too slow

Избыточность не нужна!

header#intro {…}
#intro {…}

Это почти полностью справедливо и для селекторов по классу

Еще один примерчик устранения избыточности

body ul.navigation li a {…}
.navigation a {…}

Общие рекомендации

См. Google Page Speed

Впрочем, чувство меры не помешает

Google Page Speed рекомендует:

ul li {color: blue;}
ol li {color: red;}
.unordered-list-item {color: blue;}
.ordered-list-item {color: red;}

Семантика?.. Объем HTML-кода?..

Занятные истории

И последние станут первыми?

Странное, казалось бы, дело:

Второй при честной реализации требует больше ресурсов

E:last-child не везде реализован по-честному

Можно сравнить отрисовку меню навигации в замедленном режиме в различных браузерах

Все лучшее — детям?

Давно и успешно реализованы:

Рассчитывать ли нам на селектор предка или селектор родителя?

Как это могло бы реализоваться в CSS? Код разметки

<section class="content">
	<h1>Заголовок</h1>
	<p>Текст первого абзаца.</p>

	<p>Текст еще какого-то абзаца с <code>фрагментом кода</code>.</p>

</section>

Как это могло бы реализоваться в CSS? Код таблицы стилей

.content:has(code) {
	line-height: 1.5;
 }

Условия установления применимости правила

Дополнительные проблемы

Целевые объекты селектора

По умолчанию subjects определяются крайней справа частью селектора:

.content pre code {…}

Принудительное переопределение целевых объектов в CSS4

ol > li:only-child {…}
ol! > li:only-child {…}

Одна и та же структура DOM-дерева, но различные целевые объекты

С цифрами на руках

Как выяснить порядок различий?

Устройство тестовых файлов

<div id="d1" class="d1">
	<section id="s1" class="s1">
		<article id="a1" class="a1">
			<p id="p1" class="p1"><span>Текст абзаца 1.</span></p>
			… еще 9
		</article>
		… еще 9
	</section>
	… еще 5
</div>
… еще 4

Методика измерения

Перед таблицей стилей, внутри <head>…</head>:

var $startTime = (new Date()).getTime();

Перед закрывающим тегом </body>:

window.onload = function() {
	var $processDuration = (new Date()).getTime() - $startTime;
}

Более тонкий инструмент — профайлеры

Обыденные случаи

Вид теста Время загрузки Время обработки CSS
00 Без стилей 100% 100%
01 #foo 108% 116%
02 .foo 108% 121%
03 .foo > span 129% 641%
04 .foo span 133% 655%
05 .foo * 164% 1081%

Красивости CSS3

Вид теста Время загрузки Время обработки CSS
06 .bar p:last-child 116% 541%
07 .bar p:nth-child(-11n+10) 118% 602%
08 .bar p:nth-of-type(-11n+10) 117% 546%

Из серии «За гранью добра…»

Вид теста Время загрузки
09 body.page div section article[class*="number"] :last-child * 238%
10 * * * [class*="number"] :last-child * 307%

Поведение избранных тестов в разных браузерах

Вид теста Opera 12 FF 12 FF 13 Chrome 19
00 Без стилей 100% 100% 100% 100%
01 #foo 108% 109% 107% 104%
02 .foo 108% 111% 109% 102%
05 .foo * 164% 271% 116% 112%
09 body.page div section article[class*="number"] :last-child * 238% 489% 446% 897%
10 * * * [class*="number"] :last-child * 307% 662% 593% 1135%

Краткие выводы

С одной стороны…

С другой стороны…

Пара частностей

Идентификаторы или классы?

Что именно использовать? Решайте сами в зависимости от задачи!

Вполне нормальная практика

.content h1 {

	}
.content p {

	}

Ссылки

Спасибо за внимание!