Pull to refresh

CSS Sprites 2: время Javascript

Reading time 11 min
Views 4.4K
Original author: Dave Shea
Ощущение динамики часто являлось тем, что отличало насыщенные Flash-ем сайты от сайтов, основанных на стандартах html. До недавнего времени флэш-интерфейсы всегда казались более живыми, они взаимодействовали с пользователем динамично, и это тот функционал, который остальные сайты не могли просто взять и скопировать.

Конечно, позже состояние дел изменилось — появились эффекты для динамических интерфейсов, поддерживаемые такими JS-библиотеками, как Prototype, Scriptaculous, Moo, YUI, MochiKit (и этот список можно продолжить). Сейчас самое время (через 4 года) вспомнить технику CSS Sprites и посмотреть, сможем ли мы добавить в неё «немного динамики».

Знакомство с jQuery


Тут мы сталкиваемся с первым условием: нам нужно использовать jQuery, чтобы все получилось. jQuery – это хорошо продуманная JS-библиотека, которая предоставляет нам тот же нужный функционал, что и другие библиотеки, но обладает, помимо этого, дополнительным преимуществом, которое позволяет упростить расширение CSS Sprites: для выбора элементов страницы мы можем применять те же конструкции, что используются в CSS.

Нужно отметить, что эта библиотека требует загрузки несколько дополнительных килобайт скриптов. Конечно, внешние JS-библиотеки кэшируются и загружаются только один раз – когда посетитель сайта открывает страницу в первый раз. Самая компактная версия jQuery весит 15 кб. Это неизбежное увеличение размера страницы, и оно может стать проблемой. Если вы уже используете jQuery для других целей, тогда все нормально. Но если вы заинтересованы в добавлении только этой техники, то посчитайте размер загружаемой страницы и решите для себя, стоит ли эффект этого. (С тех пор, как jQuery хостится у Google, вы можете сделать ссылку на версию используемой библиотеки, как и мы в приведенных примерах, и надеятся, что браузеры большинства посетителей вашего сайта уже закэшировали URL библиотеки) (Пер.: На мой взгляд, автор тут немного преувеличивает проблему: jQuery одна из самых маленьких JS-библиотек и 15 лишних килобайт за динамику при первой загрузке — это нестрашно)

Как насчет других JS-библиотек? Вам абсолютно ничего не мешает использовать их. Рассматривайте эту статью как открытое приглашение портировать эту технику к той библиотеке, которой вы пользуетесь.

HTML и CSS


Первая вещь, которую мы хотим сделать – это создание основы навигационного меню для посетителей сайта с отключенным JavaScript.

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


Картинка для CSS Sprites

Если есть какие-то вопросы по технике CSS Sprites, вы можете взглянуть на оригинальную статью, но есть несколько моментов, которые стоит разъяснить ниже. Начнем с HTML. Уделите особое внимание этой структуре, мы будем часто на неё ссылаться в дальнейшем:

<ul class='nav current-about'>
  <li class='home'><a href='#'>Home</a></li>
  <li class='about'><a href='#'>About</a></li>
  <li class='services'><a href='#'>Services</a></li>
  <li class='contact'><a href='#'>Contact</a></li>
</ul>

Каждый класс служит определенной цели: класс nav тэга ul позволяет указывать на этот элемент с помощью CSS (и позже Javascript), в то время, как второй класс current-about используется для выделения просматриваемой страницы или раздела сайта. Каждый li-элемент имеет свой уникальный класс, который также используется для указания на этот тэг в CSS или JS.

Итак, наша разметка для навигации – это простой и доступный HTML список, и у нас достаточно классов чтобы заработала техника Sprites:

.nav {
width: 401px;
height: 48px;
background: url(../i/blue-nav.gif) no-repeat;
position: absolute;
top: 100px;
left: 100px;
}


Мы установили свойство position равным absolute, чтобы изменить точку отсчета для позиционных сдвигов тэгов li. Мы могли бы также использовать значение relative, чтобы достичь того же эффекта и, в то же время, оставляя элемент .nav в нормальном потоке. Есть некоторые причины использовать значение relative, но в этой статье мы будем применять значение absolute. Больше информации по этому вопросу вы можете получить в статье Дугласа Боумана.

Основа самой техники Sprites в нашем случае – это использование background-изображений для каждого элемента навигации и их абсолютное позиционирование внутри родительского тэга ul:

.nav li a:link, .nav li a:visited {
position: absolute;
top: 0;
height: 48px;
text-indent: -9000px;
overflow: hidden;
}

.nav .home a:link, .nav .home a:visited {
left: 23px;
width: 76px;
}

.nav .home a:hover, .nav .home a:focus {
background: url(../i/blue-nav.gif) no-repeat -23px -49px;
}

.nav .home a:active {
background: url(../i/blue-nav.gif) no-repeat -23px -98px;
}


Мы собираемся продвинуться немножко дальше, чем в оригинальной статье, и определим состояния focus и active. Первое состояние – это небольшое добавление к переключателю изображений, когда ссылка является субъектом состояния hover или focus, состояние active добавляет новое свойство – при клике на элемент. Они необязательны, но это хорошая идея — определить стили для обоих состояний. Правило “overflow: hidden” также новое в наших стилях, оно служит для того, чтобы предотвратить появление визуального бага в некоторых браузерах.

Пример 1: Настройка CSS Sprites для меню.

Итак, мы подошли к стартовой точке – у нас теперь есть работающее навигационное меню, основанное на технике CSS Sprites, с возможностью выбора активного элемента. Разовьем нашу идею дальше.

Начальная настройка jQuery


Заметьте, что весь код, написанный ниже, будет расположен внутри этой jQuery функции, она гарантирует запуск кода только один раз и только после того, как полностью прогрузится вся страница. Все куски кода ниже предполагают, что они запускаются внутри этой функции, поэтому если вы столкнетесь с ошибками, не забудьте проверить месторасположение кода:

$(document).ready(function(){
  // весь код здесь
});


Поскольку спрайтовое меню – это наше «меню по умолчанию» для отключенного Javascript (если мы все сделали правильно), то нам лучше избавиться от всех определенных в стилях фоновых изображений для элементов при наведении, поэтому мы создадим новые фоновые изображения с помощью скрипта:

$(".nav").children("li").each(function() {
  $(this).children("a").css({backgroundImage:"none"});
});


В первой строчке запрашиваются элемент(-ы) класса nav и после применяется функция к каждому дочернему элементу li, который они содержат. Эта функция показана во второй строчке кода, она опрашивает объект this (под this подразумеваются найденные тэги li) на наличие дочерних тэгов a внутри него. Если такие существуют, то для свойства CSS background-image этих элементов устанавливается значение none.

Пример 2: Отключение стилей при наведении с помощью jQuery.

Это работает… но мы также потеряли выделение выбранного пункта меню в процессе. Поэтому нам нужно проверять название нашего current-(выбранный пункт меню) класса для родительского тэга ul, чтобы сохранить фоновое изображение для выбранного элемента. Предыдущий код необходимо немного расширить:

$(".nav").children("li").each(function() {
  var current = "nav current-" + ($(this).attr("class"));
  var parentClass = $(".nav").attr("class");
  if (parentClass != current) {
    $(this).children("a").css({backgroundImage:"none"});
  }
});


(Пер.: В следующем абзаце подробно объясняется, что делает вышеприведенный код. Я посчитал это излишним – это очевидно даже тем, кто не знаком с jQuery. :))

Привязка событий


Теперь нам нужно привязать функцию к каждому из элементов li для каждого события-взаимодействия, к которому применяются стили. Давайте создадим функцию для этого и назовем её attachNavEvents:

function attachNavEvents(parent, myClass) {
  $(parent + " ." + myClass).mouseover(function() {
    // какой-то код
  }).mouseout(function() {
    // какой-то код
  }).mousedown(function() {
    // какой-то код
  }).mouseup(function() {
    // какой-то код
  });
}


Эта функция принимает 2 аргумента. Первый аргумент – строка, содержащая название класса родительского элемента. Второй аргумент – строка, содержащая название название класса тэга li, к которому привязываются события. Мы скомбинировали оба аргумента в следующей строке функции, чтобы создать селектор, аналогичным применяемому в CSS, например такой .nav .home.

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

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

attachNavEvents(".nav", "home");
attachNavEvents(".nav", "about");
attachNavEvents(".nav", "services");
attachNavEvents(".nav", "contact");


Пока мы почти ничего не сделали, но у нас все впереди.

Пример 3: Начальная настройка скрипта для событий.

Теория


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

Для каждой из ссылок мы создадим новый тэг div внутри тэга li. Этот новый тэг мы и будем использовать для эффектов jQuery. Фоновое изображение для тэга div будет то же самое, что мы использовали ранее для тэга a (и оно зависит также от родительского тэга li). Мы абсолютно позиционируем тэг div (он будет позиционироваться относительно элемента .nav). Таким образом, мы почти полностью скопировали существующий тэг a из нашей начальной конструкции CSS Sprites. Путем проб и ошибок я выяснил, что создание нового тэга div позволяет с меньшими трудностями создать динамические эффекты на jQuery, чем если мы будем обходиться только существующими тэгами, так что это необходимый шаг.

Стили для тэга div должны быть заранее прописаны в CSS. Мы создадим новый уникальный класс для div (например, .nav-home), зависящий от названия родительского класса li, и добавим к нему стили:

.nav-home {
position: absolute;
top: 0;
left: 23px;
width: 76px;
height: 48px;
background: url(../i/blue-nav.gif) no-repeat -23px -49px;
}


Практика


Настало время добавления эффектов. Когда срабатывает событие mouseover, мы создаем элемент div c соответствующим названием класса. Нам нужно, чтобы этот тэг был невидимым перед тем, как он появится, поэтому используем jQuery-функцию css для задания ему свойства display: none. Наконец, мы используем jQuery-функцию fadeIn для плавного появления div и передаем ей в качестве аргумента значение 200, чтобы определить длительность анимации в миллисекундах:

function attachNavEvents(parent, myClass) {
  $(parent + " ." + myClass).mouseover(function() {
    $(this).before('<div class="nav-' + myClass + '"></div>');
    $("div.nav-" + myClass).css({display:"none"}).fadeIn(200);
  });
}


Затем мы делаем то же самое, только наоборот, для события mouseout – наш div должен плавно исчезнуть. После того, как он исчезнет, мы удаляем div из DOM. Вот как наша функция attachNavEvents должна работать:

function attachNavEvents(parent, myClass) {
  $(parent + " ." + myClass).mouseover(function() {
    $(this).before('<div class="nav-' + myClass + '"></div>');
    $("div.nav-" + myClass).css({display:"none"}).fadeIn(200);
  }).mouseout(function() {
    // псевдо-ссылка плавно исчезает и уничтожается
    $("div.nav-" + myClass).fadeOut(200, function() {
      $(this).remove();
    });
  });
}


И этого достаточно для эффектов при наведении.

Пример 4: Добавление эффектов при наведении.

Нам следует подумать об обработке событий mousedown и mouseup, поскольку у нас в стилях было прописано состояние active для ссылок. Создадим другой класс, чтобы мы могли ссылаться только на него в CSS и менять название класса при возникновении события mousedown. При возникновении события mouseup, будет возвращаться прежнее название класса для тэга div. Вот как будет выглядеть функция attachNavEvents после этих изменений:

function attachNavEvents(parent, myClass) {
  $(parent + " ." + myClass).mouseover(function() {
    $(this).before('<div class="nav-' + myClass + '"></div>');
    $("div.nav-" + myClass).css({display:"none"}).fadeIn(200);
  }).mouseout(function() {
    $("div.nav-" + myClass).fadeOut(200, function() {
      $(this).remove();
    });
  }).mousedown(function() {
    $("div.nav-" + myClass).attr("class", "nav-" + myClass + "-click");
  }).mouseup(function() {
    $("div.nav-" + myClass + "-click").attr("class", "nav-" + myClass);
  });
}


Мы можем повторно использовать стили при наведении на тэг div, всего лишь немного изменив положение фонового спрайтового изображения для создания стилей при клике:

.nav-home, .nav-home-click {
position: absolute;
top: 0;
left: 23px;
width: 76px;
height: 48px;
background: url(../i/blue-nav.gif) no-repeat -23px -49px;
}

.nav-home-click {
background: url(../i/blue-nav.gif) no-repeat -23px -98px;
}


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

Пример 5: Собираем все вместе.

Другие размышления


Мы не ограничены только эффектом «fade». jQuery имеет встроенные функции «slideUp/slideDown», которые тоже можно использовать (второй пример в статье). Или мы даже можем проявить фантазию и задействовать собственные анимационные эффекты, основанные на стилях и использующие jQuery-функцию animate (третий пример). Сразу предупреждаем насчет функции animate — анимация может получиться не очень плавной, как вы могли заметить в нашем примере.

Кроссбраузерная совместимость – это как своеобразный небольшой подарок для вас, jQuery прекрасно работает во всех современных браузерах, и все, что вы видите здесь, работает в ИЕ6, ИЕ7, ФФ2, ФФ3, Сафари, Опере и т.д. Мы даже предусмотрели множество грациозных способов обхода ограничения функционала. Так, если у посетителя сайта отключен Javascript, он увидит динамику, реализованную с помощью CSS Sprites. (Пер.: Это не работает в ИЕ6) Если же отключены CSS и JS, посетитель увидит простой ul-список. Мы получаем и другие преимущества CSS Sprites помимо этого, так как можем использовать одну картинку для различных навигационных состояний и эффектов.

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

Еще одна небольшая проблема, с которой вы можете столкнуться – это когда текст на сайте начинает «моргать» во время анимации. Это сложное явление связано с рендерингом суб-пикселей, присущим современным операционным системам. Лучшее решение этой проблемы – задать почти непрозрачное значение opacity, чтобы рендеринг текста проводился особым образом. Если вы добавите эту строчку в свои стили, то «моргание текста» должно прекратиться, только текст будет сглаживаться обычным образом, а не с помощью суб-пикселей:

p {
  opacity 0.9999;
}


Пакет функций для Sprites2


Нет необходимости запоминать какой-либо код в этой статье, поскольку мы подготовили функцию, используемую в нашем окончательном примере. Используя код JavaScript в HTML-файле, как помощь, вам нужно поменять только одну строчку в коде, чтобы применить Sprites2 на вашем сайте:

$(document).ready(function(){
  generateSprites(".nav", "current-", true, 150, "slide");
});


Функция generateSprites принимает 5 аргументов:
1. Название класса родительского ul, включая точку.
2. Префикс, используемый для выбранных элементов (например, для выбранного класса selected-about, используйте "selected-" в качестве значения префикса).
3. Переключатель, показывающий, используются ли стили для состояния active. Если вы определяете состояние :active и его эквиваленты в jQuery в вашей таблице стилей, установите значение true. В противном случае нужно установить false.
4. Скорость анимации, в миллисекундах (300 = 0.3 секунды).
5. Тип анимации, строковая переменная. Установите «slide» или «fade», последнее стоит по умолчанию.

Пример 6: Всего одна простая строчка кода для изменения благодаря нашей функции.

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

Заметки


Во время написания этой статьи в интернете распространилась схожая техника, хотя и без дополнительной поддержки CSS Sprites. Мы также обнаружили совсем другое анимированное jQuery меню, которое вы можете найти полезным.
Tags:
Hubs:
+36
Comments 46
Comments Comments 46

Articles