По своей интерактивности сайты часто различаются,
в зависимости от того, на какой технологии они построены (тяжелые
флэш-сайты или стандартоориентированные сайты). Флэш-интерфейсы всегда
выглядят более живыми — динамически отвечают на действия пользователя,
в отличие от стандартоориентированных сайтов, которые на такое
не способны.
В последнее время ситуация меняется, конечно, благодаря появлению
динамических эффектов, основанных на таких библиотеках JavaScript, как Prototype, Scriptaculous, Moo, YUI, MochiKit. И сейчас самое время еще раз посмотреть четырехлетнюю статью про технику CSS спрайтов, и подумать, а не можем ли мы добавить туда немного движения.
Примеры ниже демонстрируют новые возможности CSS спрайтов, технику, которую мы рассмотрим в этой статье:
Введение в jQuery
Итак, для достижения поставленной задачи мы будем использовать jQuery.
Это проверенная JavaScript-библиотека, которая позволяет создать
такие же изящные решения, как и другие библиотеки JavaScript, плюс
обладает дополнительным преимуществом при создании CSS спрайтов: jQuery
позволяет выбирать элементы на странице, используя синтаксис CSS,
который мы уже знаем.
Мы должны отметить дополнительные килобайты, которые библиотека
добавит к первоначальной загрузке страницы. Внешний файл JavaScript,
конечно же, кэшируется, так что это произойдет лишь один раз, когда
пользователь зайдет на ваш сайт. Наиболее компактная версия jQuery весит
около 15 k. Это неизбежные издержки, которые могут стать причиной для
беспокойства. Если вы уже используете jQuery на своем сайте для других
целей, то дополнительный трафик не является проблемой. Если
вы собираетесь использовать jQuery лишь для данной техники, то надо
рассмотреть вопрос о размере файла и решить для себя стоит ли этот
эффект того. (Так как Google теперь имеет на своем хостинге jQuery,
вы можете просто поставить ссылку на их версию библиотеки, и, думаю,
что у многих из ваших посетителей этот URL уже будет кэширован
браузером.)
Что касается других библиотек JavaScript? Нет никаких причин, почему
вы не можете или не должны использовать их, считайте эту статью открытым
приглашением к переносу этой техники на любую другую библиотеку, —
и если получится, то дайте ссылку на ваш вариант в комментариях.
Базовый HTML и CSS
Первое, что мы делаем, это создаем вариант для пользователей с отключенным Javascript. (Мы читали несколько лет назад статью Джереми Кейса, и с тех пор, естественно, большие поклонники ненавязчивых DOM сценариев.)
Мы уже знаем, как добиться rollover-эффекта только с помощью CSS, так
что давайте начнем строить нашу панель навигации с создания простых CSS
спрайтов. И, потому что мы ленивые, мы не будем повторно их делать,
а просто возьмем из предыдущей статьи, а затем добавим jQuery.
По поводу всех «как» и «почему» при создании CSS спрайтов обращайтесь к оригинальной статье,
но есть несколько моментов, на которых я остановлюсь. Давайте начнем
с 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>
Каждый класс служит своей цели: контейнер ul имеет класс nav, который
позволяет нам задавать параметры в CSS (а позже и использовать
в JavaScript), также как и класс .current-about, который мы используем,
чтобы выделить пункт навигации той станицы, которую мы просматриваем
в данный момент. Каждый элемент li имеет свой уникальный класс, который
мы также будем использовать.
Итак, пока все хорошо. Наша разметка навигации является простым
и доступным HTML-списком, и мы задали достаточно классов, чтобы
приступить к CSS:
.nav { width: 401px; height: 48px; background: url(../i/blue-nav.gif) no-repeat; position: absolute; top: 100px; left: 100px; }
Мы установили значение position на absolute, чтобы изменить
позиционирование li. Мы также можем использовать relative, для этого
есть свои причины, но мы остановимся на absolute. Для изучения проблемы
absolute/relative обратитесь к статье Дугласа Боумена(Douglas Bowman).
Суть нашей техники создания спрайтов заключается в фоновом
изображении для каждого nav элемента, и абсолютном позиционировании
их в родительском контейнере 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.Также прописали overflow: hidden,
чтобы в некоторых браузерах не появлялась точечный контур, тянущийся
от элемента до левой границы экрана.
Пример 1: базовые CSS спрайты.
Итак, мы имеем работающее навигационное меню со спрайт-эффектами. Посмотрим как его можно усовершенствовать.
Использование jQuery
Обратите внимание, что все, описанное ниже, будет располагаться
внутри функции jQuery, которая гарантирует, что код будет обработан лишь
после того, как документ полностью загрузиться. Фрагменты кода, которые
вы видите ниже, должны работать внутри функции, так что, если
вы столкнетесь с ошибкой, проверьте их расположение:
$(document).ready(function(){
// everything goes here
});
Так как уже созданное нами спрайт-меню — это запасной вариант
на случай, если JavaScript отключен, мы лучше откажемся от прописанных
в CSS фоновых изображений, и после зададим новые:
$(".nav").children("li").each(function() { $(this).children("a").css({backgroundImage:"none"}); });
В первой строчке мы запрашиваем элемент с классом nav и присоединяем
новую функцию для каждого вложенного элемента li. Эта функция
располагается на второй строчке, и она запрашивает объект this для
каждого вложенного a элемента. Если она находит их, то устанавливает
значение для CSS background-image на none. В нашем случае, this означает
элемент li.
Пример 2: отключение с помощью jQuery изменения средствами CSS фонового изображения при наведении мыши.
Это работает... но мы также потеряли наш текущий выделенный пункт
меню. То есть мы должны пройти по всему списку вложенных в ul элементов
и найти тот, которому соответствует класс .current-(что-либо), а затем
убрать его из функции по изменению background-image. Предыдущий фрагмент
кода требуется немного доработать:
$(".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"}); } });
Вторая строчка кода задает переменную current, которая берет класс
каждого элемента li по-порядку и создает класс для ul, как если бы
данный li был текущим выделенным пунктом меню. Третья строчка создает
вторую переменную, которая берет название класса непосредственно
от ul. Наконец, четвертая строчка сравнивает две переменные. Если они
не равны, только тогда, мы меняем background-image для элемента a. Все
это позволяет нам избежать смены фонового изображения у текущего
выбранного элемента, к чему мы и стремились.
Прикрепление событий
Теперь мы должны прикрепить функцию к каждому элементу li для каждого
интерактивного события, которое мы хотим создать. Давайте создадим
функцию для этого и назовем ее attachNavEvents:
function attachNavEvents(parent, myClass) { $(parent + " ." + myClass).mouseover(function() { // do things here }).mouseout(function() { // do things here }).mousedown(function() { // do things here }).mouseup(function() { // do things here }); }
Эта функция имеет два аргумента. Первый — это строка содержащая
неизменяемый класс родительского элемента, дополненная предшествующим
периодом, как вы увидите ниже. Второй — строка содержащая класс
непосредственно li, к которому мы прикрепляем событие. Мы соединим оба
в первой строке функции, чтобы создать селектор jQuery, который
указывает на тот же элемент, что и селекторы в CSS, например, .nav
.home.
Так как jQuery позволяет соединить множество функций в один объект,
мы можем создать все вызывающие события функции за один раз. Такое
соединение — это уникальная особенность jQuery. Это довольно мудрено
понять, поэтому, если не ясно, как это работает, просто примите как
должное.
Теперь мы прикрепим эти функции к каждому элементу нашей навигации.
Ниже приводится подробный способ — мы его оптимизируем позже, —
но сейчас, давайте напишем функцию для каждого li. Что касается
аргументов, мы используем как родительский класс, так и собственный
класс каждого li:
attachNavEvents(".nav", "home"); attachNavEvents(".nav", "about"); attachNavEvents(".nav", "services"); attachNavEvents(".nav", "contact");
Это отнюдь немного, но мы постараемся это исправить.
Пример 3: основной сценарий для событий.
Теория
Я собираюсь объяснить, что происходит дальше. Оставайтесь со мной —
важно понять, что будет дальше, потому что вам надо будет преобразовать
элементы, которыми мы манипулировали.
Для каждой ссылки, мы создадим новый div внутри li, который будет
использоваться в jQuery эффектах. Мы зададим картинки для новых дивов,
используя то background-image правило, что и для a элементов
в родительских li. Мы также абсолютно позиционируем div в родительском
элементе. Он должен более или менее копировать элемент a из нашего CSS.
Путем проб и ошибок я пришел к тому, что создание нового div избавляет
от ряда багов, которые появляются, если просто применить jQuery эффекты
к уже имеющимся элементам — так что это необходимый шаг.
Стили к этому div должны уже быть написаны. Мы создадим новый класс
для этого li (.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 и присваиваем ему вышеуказанный класс. Нам
надо сделать так, чтобы до своего плавного появления он был невидимым,
для этого используем функцию jQuery изменяющую css свойство на display:
none. И наконец, мы используем функцию jQuery fadeIn, чтобы совершить
плавный переход из невидимого состояния в видимое, и устанавливаем
аргумент 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. Когда он исчезнет, мы почистим
за собой, удалив его из 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() { // fade out & destroy pseudo-link $("div.nav-" + myClass).fadeOut(200, function() { $(this).remove(); }); }); }
А вот как это все симпатично выглядит:
Пример 4: Выполнение сценария при наведении курсор
Неплохо бы что-нибудь тоже сделать и для событий mousedown и mouseup,
если мы изначально установили изменения для псевдокласса :active в CSS.
Нам требуется класс отличный от :hover, поэтому мы создаем новый класс
в CSS, и добавляем событие mousedown. Мы также хотим создать обратное
событие, чтобы восстановить :hover, если пользователь, отпустив кнопку
мыши, не переместил курсор. Вот как наша функция выглядит теперь:
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); }); }
Для создания нового класса надо лишь чуть-чуть подправить имеющийся
CSS, изменив положение фонового изображения для показа при клике:
.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: все в собранном виде.
Другие соображения
Мы неограниченны только эффектом постепенного изменения фона. jQuery
имеет встроенную функцию slideUp/slideDown, которую мы также можем
использовать (как показано во втором примере в начале статьи). Или
мы можем очень захотеть и создать в обычном CSS анимационный эффект,
используя функцию animate (как показано в третьем примере). Только сразу
надо оговориться по поводу animate — результаты могут быть самыми
непредсказуемыми, что вы и можете увидеть в нашем примере.
Кросс-браузерная совместимость этой техники сильно облегчает нам
жизнь; jQuery поддерживается большинством современных браузеров, таким
образом, все, что описано здесь, будет работать в IE6+, Firefox, Safari,
Opera и др. Мы также создали несколько изящных вариантов с пониженным
функционалом. Если у пользователя отключен JavaScript, он увидит
изначальные CSS спрайты. Если у него отключены и JavaScript, и CSS,
он увидит базовый список HTML. И мы получили также другие преимущества
от CSS спрайтов, так как мы до сих пор используем одну картинку для всех
вариантов и эффектов нашей навигации.
Хотя этого и не требуется, но настоятельно рекомендуется учесть
следующие тонкости; скорость анимации более нескольких сотен миллисекунд
поначалу может показаться забавной, но через некоторое время она
начинает сильно действовать на нервы тех, кто просматривает сайт. Так
что, лучше использовать быструю анимацию.
Еще одна потенциальная проблема связана с текстом на странице который
начинает «мерцать» во время действия анимации. Это запутанная проблема,
связанная с рендерингом суб-пикселей,
характерна для современных операционных систем, и лучшим ее решением
оказывается установка чуть-менее-чем-непрозрачного значения для opacity.
Если вы добавите следующий код в CSS, мерцание прекратится, так как
текст будет сглаживаться стандартным образом, а не с помощью
суб-пикселей:
p { opacity 0.9999; }
В демо этот способ применяется к p, что вызывает конфликт с CMS. Но это правило можно применить к любому элементу на странице.
В итоге
Вам не надо запоминать ни один скрипт из этой статьи, так как в последнем примере вас ожидает уже существующая функция,
действие которой и будет сейчас продемонстрировано. Используя
JavaScript в HTML-файле, вам нужно будет отредактировать лишь одну
строчку, чтобы CSS спрайты 2 появились на вашем сайте:
$(document).ready(function(){ generateSprites(".nav", "current-", true, 150, "slide"); });
Функция generateSprites имеет пять аргументов:
- Первый класс родительского ul, включая период.
- Префикс, используемый для выбранных элементов, например, для
выбранного класса selected-about, используйте «selected-» как префикс.
- Переключатель, показывающий, используются ли стили для :active.
Установите значение true, если вы определяете состояние :active и его
эквиваленты в jQuery в CSS, в противном случае установите false.
- Скорость анимации, миллисекундах, например, 300 = 0,3 секунды.
- Предпочитаемый вид анимации в виде строки. Установите «slide» или «fade», по умолчанию стоит «fade».
Пример 6: Всего лишь одна простая строчка кода, спасибо предварительно собранной функции.
Вам все еще необходимо позиционировать и прописать стили к различным
элементам CSS, так что чувствуйте себя свободно и пользуйтесь CSS
примерами из этой статьи как ориентиром.
Примечания
Во время написания данной статьи появилась схожая техника, правда без наших симпатичных CSS спрайтов. Мы также открыли совершенно другой вариант анимированного меню на jQuery, который вы можете найти интересным.
|