CSS Sprites и data:URI

Давайте проведем сравнение двух освещенных выше методов для кардинального уменьшения числа запрашиваемых файлов с сервера: CSS Sprites и data:URI.

Проблемы при верстке

С какими проблемами сталкивается верстальщик, когда использует CSS Sprites? Это, в первую очередь, проблемы изменения каждой конкретной картинки в общем массиве. Для этого нужно открыть ресурсную картинку, найти в ней область, соответствующую данному небольшому изображению (которое меняется), и заменить ее, не потеряв палитру при всех изменениях. Также при изменении расположения картинок в ресурсном файле (например, перераспределили свободное место в связи с очередными дизайнерскими изменениями) нужно заново пересчитать все координаты и внести соответствующие изменения в CSS-файл.

При небольшом количестве спрайтов или их равномерном распределении (например, иконок для пунктов меню) это будет не очень сложно. Но если ресурсный файл представляет собой набор картинок разных размеров, например, как у Google? Тогда любое изменение может превратиться в ночной кошмар. В любом случае на использование спрайтов тратится дополнительное время при разработке сайтов.

SpeedUpYourWebsite.v1.2_img_22

Рис. 22. Пример CSS Sprites со страницы поиска Google. Источник: www.google.com

Также у IE возникают проблемы с позиционированием полупрозрачных PNG-картинок (которые нужно вставлять через AlpaImageLoader). Таким образом, больше одной такой картинки в спрайт не добавить (в левый верхний угол). Это можно обойти при помощи IE- фильтра crop или абсолютного позиционирования и дополнительной разметки (когда в контейнер с относительным позиционированием вставляем изображение с абсолютным позиционированием и накладываем поверх этого изображения все остальное содержимое контейнера — оно будет располагаться в нем в обычном порядке).

Проблемы при загрузке

Казалось бы, CSS Sprites призваны уменьшить задержку при загрузке страницы, однако, на практике так происходит только при правильном подходе. Обычно в ресурсную картинку объединяется все подряд, картинка многократно увеличивается, а визуальная задержка при загрузке сайта только растет: пользователь может ждать 1 большую картинку дольше, чем половину входящих в нее маленьких, а последние обеспечат ему «почти» загрузку сайта.

Если спрайтов у нас немного (1-2 картинки), то общее время загрузки сайта, скорее всего, возрастет. Это связано с тем, что браузер не сможет открыть, как минимум, 4-8 дополнительных соединений (к хостам, где расположена статика) и загрузить все исходные картинки параллельно, а не последовательно. При небольшой сетевой задержке это может оказать решающее воздействие.

Проблемы при использовании

Даже если положиться на то, что спрайты поддерживаются (почти) всеми браузерами на данный момент, все равно остается достаточно много вопросов, которые они не только не решают, а скорее, сами создают. Во-первых, это проблемы при использовании иконок для списка. В таком случае необходимо располагать маленькие картинки «лесенкой», но, в общем случае, это увеличивает размер получившейся картинки на 20-30%.

Во-вторых, при использовании спрайтов, когда пытаются обойти обрисованную проблему с «выползанием» фона для ненужных элементов, в структуре страницы появляются несемантические элементы (дополнительная разметка), которые, по сути, заменяют тег img, только через фоновое изображение. Такое засорение структуры страницы вредно отражается на всех уровнях разработки и использования сайта.

В-третьих, если логотип склеен с другими картинками (как у вышеуказанного примера), то становится невозможным его использование как обычной картинки (например, в версии для печати или на внешних ресурсах).

Шаг за шагом

С развитием техник data:URI наиболее логичным выходом из сложившейся ситуации будет следующий характер разработки.

  • Верстальщик создает рабочую версию макета сайта, не прибегая к помощи CSS Sprites (на каждый элемент одна фоновая картинки).
  • Веб-технолог внедряет кодирование картинок в base64 (+mhtml) в CSS-файл(ы) на этапе их загрузки на боевой сайт, создавая автоматизированное решение. На этом этапе могут использоваться и CSS Sprites, однако, их внедрять сложнее из-за перерасчета позиционирования фона и обновления соответствующих стилевых правил.
  • Веб-программист обеспечивает для ряда «старых» браузеров загрузку версии без использования data:URI.

Чем это хорошо? Верстальщик не думает лишний раз, что и как ему расположить и нарезать: эти операции уже включены в процесс публикации сайта, автоматизированы и максимально адаптированы под пользователей.

Чем это плохо? В общем случае, загрузка страницы не ускорится, а даже может замедлиться, потому что фоновые картинки (включенные через data:URI) будут грузиться в один поток, а не в несколько, как при обычном использовании спрайтов. Если фоновых картинок достаточно много (несколько десятков килобайтов), то это окажется существенным. При небольшом их объеме (до 10 Кб) будет заметно явное ускорение.

Как распределить загрузку фоновых картинок между предзагрузкой и пост-загрузкой (фактически, ускорив первую за счет замедления второй) рассказывается чуть ниже.

Выносим CSS-файлы в пост-загрузку

При использовании data:URI итоговый CSS-файл занимает довольно большой объем (фактически, равный 110-120% от размера всех картинок и набор базовых CSS-правил). И это в виде архива. Если файл не заархивирован, то его дополнительный размер увеличивается многократно (в 2,5-3 раза относительно размера всех фоновых изображений), но это не так существенно, ибо пользователей с отключенным сжатием для CSS-файлов сейчас единицы (обычно доли процента).

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

Фактически, используя такой подход, мы создаем другой контейнер для фоновых изображений (не ресурсное изображение, а CSS-файл), который удобнее использовать в большинстве случаев. Мы объединяем все фоновые картинки не через CSS Sprites, а через data:URI, и можем загрузить их все одним файлом (в котором каждая картинка будет храниться полностью независимо). При этом избегаем любых проблем с позиционированием фона (все ранее заявленные проблемы с (полу)прозрачными картинками для прошлых версий IE сохраняются, однако, их решение также остается прежним).

Теоретическое решение

Все гениальное просто, поэтому мы можем загружать в самом начале страницы достаточно небольшой CSS-файл (без фоновых изображений, только базовые стили, чтобы только отобразить страницу корректно), потом по событию загрузки страницы (подробнее о методе можно прочитать в седьмой главе) через JavaScript подгрузить в один или несколько потоков динамические файлы стилей.

Тут есть и возможные минусы: после загрузки каждого дополнительного CSS-файла будет происходить перерисовка страницы. Однако, если таких файлов всего 1 или 2, то отображение страницы произойдет значительно быстрее.

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

На практике

На практике все оказалось не сильно сложнее. Мы загружаем в head страницы (до вызовов любых внешних файлов) наш «легкий» CSS:

<link href=»light-light.css» rel=»stylesheet» type=»text/css» media=»all»/>

А затем добавляем в комбинированный обработчик window.onload (подробнее о нем рассказывается в седьмой главе) создание нового файла стилей, который дополняет уже загрузившуюся страницу фоновыми изображениями:

function combinedWindowOnload() {
load_dynamic_css(«background-images.css»);

}

В результате мы имеем максимально быстрое отображение страницы, а затем стадию пост-загрузки, которая вытянет с сервера все дополнительные картинки (тут уже сам браузер постарается), стилевые правила и скрипты.

А доступность?

Внимательные читатели уже заготовили вопрос: а что, если у пользователя отключен JavaScript? Тут вс. должно быть просто: мы добавляем соответствующий <noscript> для поддержки таких пользователей. С маленьким нюансом: <noscript> не может находиться в <head>, а <link> не может находиться в <body>. Если мы соблюдаем стандарты (все же иногда лучше довериться профессионалам и не ставить браузеры в неудобное положение, когда они встретятся с очередным отклонением от спецификации), то стоит искать обходные пути.

После небольших экспериментов было выделено следующее изящное решение, обеспечивающее работу схемы во всех браузерах (замечание: после многочисленных экспериментов было решено остановиться на HTML-комментариях — они оказались наилучшим способом запретить загрузку указанного CSS-файла):

<script type=»text/javascript»>
/* если мы сможем создать динамический файл стилей */
if (document.getElementsByTagName) {
/* то добавляем в загрузку облегченную версию */
document.write(‘\x3clink href=»light-light.css»
rel=»stylesheet» type=»text/css» media=»all»/>’);
/* после этого начинаем HTML-комментарий */
document.write(‘\x3c—‘);
}
</script>
<link href=»full.css» rel=»stylesheet» type=»text/css» media=»all»/>
<!—[if IE]><![endif]—>

В результате браузер с включенным JavaScript запишет начало комментария, а закроет его только после <link> (комментарии не могут быть вложенными). При выключенном JavaScript <script> не отработает, <link> обработается и добавится в очередь загрузки, а последний комментарий будет просто комментарием.

Делаем решение кроссбраузерным

В ходе тестирования в Internet Explorer обнаружилось, что если добавлять файл стилей сразу параллельно со скриптами (в функции, которая для него срабатывает по onreadystatechange), то IE «морозит» первоначальную отрисовку страницы (т.е. показывает белый экран), пока не получит «свеженький» файл стилей. Для того чтобы Internet Explorer не занимался «замораживанием», нужно вставить фиктивную задержку следующим образом:

setTimeout(‘load_dynamic_css(«background-images.css»)’,0);

В Safari же логика отображения страницы в зависимости от загружаемых файлов отличается ото всех браузеров. Если в двух словах, то можно жестко определить начальный набор файлов, необходимых для отображения страницы на экране (HTML/CSS/JavaScript). А можно начать загружать все файлы в порядке приоритетности (и выполняя все их зависимости) и проверять время от времени, можно ли уже отобразить страницу (выполняя все вычисления в фоновом режиме без обновления экрана).

У Safari второй подход, поэтому ничего лучше выноса загрузки динамического CSS-файла с фоновыми картинками после срабатывания window.onload для этого браузера пока не существует. Зато первоначальная картинка в браузере появляется значительно быстрее (при большом объеме фоновых изображений).

Итак, давайте объявим функцию для создания динамического файла стилей:

/* Объявляем функцию по динамической загрузке стилей и скриптов. */
function load_dynamic_css (src){
var node = document.createElement(«link»);
node = document.getElementsByTagName(«head»)[0].appendChild(node); node.setAttribute(«rel», «stylesheet»);
node.setAttribute(«media», «all»);
node.setAttribute(«type», «text/css»);
node.setAttribute(«href», src);
} …
/* Далее определяем для window обработчик по событию onload. Используем условную компиляцию для выделения IE */
window[/*@cc_on !@*/0 ? ‘attachEvent’ : ‘addEventListener’]
(/*@cc_on ‘on’ + @*/’load’, function(){
setTimeout(‘load_dynamic_css(«background-images.css»)’,0);
}
,false);

Выигрыш

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

Описанная техника (кроссбраузерный data:URL плюс динамическая загрузка файлов стилей) позволяет добиться всех преимуществ технологии CSS Sprites, не затягивая загрузку страницы. При этом обладает очевидными преимуществами: не нужно лепить все картинки в один файл (их можно объединять на этапе публикации, а не на этапе разработки), можно работать с каждой совершенно отдельно, что позволяет добиться большей семантичности кода и большего удобства использования сайтов. К тому же это несколько сократит CSS-код за счет уничтожения необходимости применения background-position.

Таким образом, data:URI (в смысле влияния на скорость загрузки) равносильны CSS Sprites (или даже предпочтительнее последней, если учесть, что для повторяющихся и полупрозрачных CSS Sprites придется создавать отдельные ресурсные файлы). В смысле же простоты внедрения и разработки они отличаются в выгодную сторону: нужно лишь настроить использование общей схемы один раз в шаблонах (с учетом динамической загрузки JavaScript-файлов, которая описана в седьмой главе, это все равно придется делать) и при публикации изменения применять base64-кодирование к фоновым изображениям.

Posted in Разгони свой сайт.