Создаем навигационную панель-липучку с помощью Waypoints от jQuery

навигационная панель-липучка с помощью Waypoints от jQuery

От автора: в этом учебнике мы создадим панель навигации, которая при прокрутке всегда остается с вами — а еще отполируем ее одним-двумя хитрыми приемами «до блеска».

«Ленточки нравятся всем», говорит Крис Койер (Chris Coyier) при обсуждении преимуществ псевдоэлементов :before и :after. Я видел, как эти стилизованные, с треугольными краями ленты так и бросаются в глаза по всему Интернету (один из видных примеров – страница Facebook’а Introducing Timeline) и, хотя они обладают некоторой привлекательностью, должен заметить, что мне не очень нравится создаваемый ими объемный эффект.

скачать исходникидемо

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

Что мы сделаем

В этом учебнике мы применим один из новых элементов HTML5, тэг nav, в качестве контейнера для горизонтального списка ссылок. Я вкратце объясню, как сделать его привлекательным с помощью CSS.

Гораздо важнее то, что вы познакомитесь с основами плагина jQuery под названием Waypoints, который обеспечит улучшение функциональности: когда пользователь делает прокрутку вниз, панель навигации «прилипает» к верху демонстрационного окна, а также меняется, обозначая текущий раздел. Как дополнительный штрих, мы применим другой плагин, ScrollTo, чтобы гарантировать легкую прокрутку и удобное расположение при щелчках пользователя на ссылки навигации.

Шаг 1: Блок

Уверен, что вы уже знакомы с различными новыми элементами, представленными в HTML5. В этом примере мы воспользуемся двумя из них: <nav> и <section>. Начнем со следующего:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class=wrapper>
      <h1>A reasonably large heading.</h1>
      <nav><!-- Navigation. --></nav>
      <section id="section-1">Lorem ipsum ...</section>
      <!-- Type in some longer sections of sample text here. -->
    </div>
    <!-- Scripts will go here. -->
  </body>
</html>

Мы собираемся назначить своей навигационной панели точную ширину. Сделайте ее на 28px шире основного div-a и задайте отрицательный левый отступ. Еще давайте с помощью border-*-radius сделаем слегка закругленные верхние края, а также небольшой произвольный отступ.

nav {
  position: relative;
  width: 928px;
  padding: 2em;
  margin-left: -14px;
  border-top-left-radius: 14px 7px;
  border-top-right-radius: 14px 7px;
}

Далее нам нужно добавить внутрь навигационной панели неупорядоченный список ссылок и установить его элементы на display: inline-block для того, чтобы выстроить их все в одну линию. Маркеры нам не нужны, поэтому внесем сюда еще list-style: none.

<nav>
  <ul>
    <li><a class=nav-link href=#section-1>Section 1</a></li>
    <li><a class=nav-link href=#section-2>Section 2</a></li>
    ...some more list items...
  </ul>
</nav>
nav li {
  display: inline-block;
  list-style: none;
}

К этому моменту у вас должно получиться нечто такое:

Шаг 2: Края

Теперь, если бы CSS позволил нам применить множественные псевдоэлементы (напр. ::after::after), можно было бы легко выполнить закругленные края панели навигации семантически «чистым» способом. Но мы не можем этого сделать, поэтому в конец nav’а нам понадобится добавить два несемантических div’а. Назначьте им классы nav-left и nav-right (или назвали бы их с фантазией, типа Кастор и Поллукс (Castor and Pollux). Они в 14px шириной и 14px высотой и позиционированы абсолютно (absolute) в 14px от нижнего края nav’а.

Как видно из вышеприведенного, семейство свойств border-radius может принимать по два значения для каждого угла. Они могут быть в процентах от ширины элемента, что очень удобно — такой подход позволяет радиусу угла автоматически подстраиваться к изменению размеров блока.

Маленькие «тени», дополняющие ленту панели навигации, выглядят созданными с помощью псевдоэлементов ::after. Их ширина и высота, как и радиусы углов, также установлены в процентах.

/* swap ‘left’ with ‘right’ for the other two */
 
.nav-left {
  position: absolute;
  left: 0;  bottom: -14px;
  width: 14px;  height: 14px;
  background: #848a6a;
  border-bottom-left-radius: 100% 50%;
}
 
.nav-left::after {
  content: '';
  position: absolute;
  right: 0;
  width: 66%;  height: 66%;
  background: #000;
  border-top-left-radius: 100% 50%;
  border-bottom-left-radius: 100% 50%;
}

И здесь мы уже закончили!

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

Шаг 3: Скрипт

Для достижения эффекта «плавающего» заголовка мы воспользуемся плагином jQuery под названием Waypoints от Калеба Тротона (Caleb Troughton). Его единственная задача – запускать события, когда пользователь прокручивает до определенного элемента. Как вы увидите, он чрезвычайно прост, однако предполагает большую гибкость — на домашней странице можно взглянуть на несколько примеров.

Подключите на свою страницу jQuery и Waypoints и давайте начнем!

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

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

$(function() {                     // When the page has loaded,
  $('nav').waypoint(               // create a waypoint
    function() {
      alert("Waypoint reached.");
    }
  )
});

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

<div class="nav-container">
  <nav>
    ...
  </nav>
</div>

Создайте в CSS следующее правило.

.sticky {
  position: fixed;
  top: 0;
}

Все уже подготовлено к самому приятному! Измените содержимое своего скрипта на следующее:

$(function() {
 
  // Do our DOM lookups beforehand
  var nav_container = $(".nav-container");
  var nav = $("nav");
 
  nav_container.waypoint({
    handler: function(event, direction) {
        nav.toggleClass('sticky', direction=='down');
    }
  });
 
});

Ага, какого черта, откуда все это взялось, задаете вы правомерный вопрос. Возможно, вы догадались, что мы прикрепляем вейпойнт к nav-container; только теперь делаем это по-другому. Вместо прямой передачи функции обработчика к .waypoint(), мы заключаем его в объект. Само по себе это роли не играет: оба способа – это полностью надежные методы. Передаваемый нами объект, однако, может содержать несколько прочих значений опции — так что применение его сейчас позже приводит к более последовательному коду.

Назначенная нами функция обработчика получает два параметра: первый – это стандартный объект jQuery event, который нам здесь неинтересен. Второй – особый Waypoints’а: это строка, чье значение равно ‘down’ либо ‘up’, в зависимости от направления, в котором пользователь делал прокрутку в момент достижения вейпойнта.

Сейчас знать, куда движется пользователь, очень важно, просто оттого, что это позволяет нам осуществить в каждом из направлений разную модель поведения. В теле функции обработчика мы применяем сравнительно малоизвестный вариант метода jQuery .toggleClass(), который ведет к удобной стенографии: в этом синтаксисе второй параметр определяет, будет ли класс добавлен к нужному элементу или удален из него. Когда пользователь делает прокрутку вниз, выражение direction===’down’ вычисляется до true, и так наша навигационная панель получает класс sticky и «прилипает» к верху демонстрационного окна. Как только пользователь снова прокручивает вверх, класс удаляется из панели навигации, которая возвращается на свое место. Сейчас попробуйте это сделать.

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

if (direction == 'down')
  nav_container.css({ 'height':nav.outerHeight() });
else
  nav_container.css({ 'height':'auto' });

Происходящее здесь весьма очевидно: мы используем nav-container в качестве «заполнителя», как упоминалось выше. При прокрутке вниз мы растягиваем его высоту, а содержимое ниже остается на месте. Хотя здесь есть подвох – для его нормальной работы любые вертикальные поля, которые вы можете пожелать вокруг панели навигации, нужно применять к nav-container, а не к nav.

Вот так вот! У нас получилась красивая фиксированная навигационная панель, как на многих других сайтах. Представление закончено, ребята…

…или нет? Ну, вы, может быть, захотите увидеть еще парочку приемов, которые приведут вас в авангард масс. Если это ваш случай, то читаем дальше.

Шаг 4: Вертикальные смещения

Если подумать, существует множество случаев, где запуск события при достижении элементом края демонстрационного окна браузера не совсем то, что нужно. К счастью, Waypoints обеспечивает для этого удобную опцию: offset. Вот как это выглядит:

nav.waypoint( {
  handler: …,
  offset: 50
} )

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

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

Первое, что приходит в голову – добавление пространства над элементом-»липучкой». Это легко сделать с помощью переменной offset: для создания сверху офсета в 15px добавьте offset:15px в опции .waypoint(), и измените top:0px на top:15px в правиле CSS .sticky.

Если того требует дизайн проекта, то хорошо выйдет, если создать над панелью навигации небольшой градиент. Это легко выполнимо путем добавления внутрь nav еще одного div’а и небольшого количества CSS:

.sticky .nav-above {
  position: absolute;
  top:-15px;
  left:1em;
  right:1em;
  height:15px;
  background: linear-gradient(top, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
  /* add cross-browser gradient code as needed */
}

В минималистских дизайнах этот лакомый визуальный кусочек будет отлично работать.

Шаг 5: Функции офсета

Калеб предусмотрительно добавил в Waypoints способность генерировать офсет вейпойнта динамически, как тут:

nav.waypoint( {
  handler: …,
  offset: function() {
    return —(nav.outerHeight()+50);
  }
} )

Это дает нам возможность получить обработчик, который станет запускаться, когда пользователь уже прокрутил 50px мимо низа элемента, без необходимости заранее знать его высоту.

Примечание: такие процедурно генерируемые офсеты (а также те, которые определяются в процентном соотношении) пересчитываются каждый раз при изменении размера окна, добавляются новые вейпойнты или модифицируются опции вейпойнта. Если делать что-нибудь еще, что может повлиять на расположение вейпойнтов (такое, как изменение DOM’а или разметки страницы),обязательно впоследствии вызывайте $.waypoints(‘refresh’) для тех позиций, которые нужно пересчитать.

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

var top_spacing = 15;
var waypoint_offset = 50;
 
nav_container.waypoint({
 
  handler: function(event, direction) {
 
    if (direction == 'down') {
 
      nav_container.css({ 'height' : nav.outerHeight() });
      nav.addClass("sticky")
         .stop()
         .css("top", -nav.outerHeight())
         .animate({"top" : top_spacing});
 
    } else {
 
      nav_container.css({ 'height' : 'auto' });
      nav.removeClass("sticky")
         .stop()
         .css("top", nav.outerHeight() + waypoint_offset)
         .animate({"top" : ""});
 
    }
 
  },
 
  offset: function() {
    return &mdash;(nav.outerHeight() + waypoint_offset);
  }
 
});

Не слишком-то плохо! Довольно стандартная раскладка jQuery: как только мы добавляем или удаляем класс sticky в nav, то подменяем вертикальное расположение элемента с помощью .css(), а затем как следует анимируем .animate() его. .stop() служит для предотвращения возможных ошибок путем очистки очереди события jQuery.

Здесь, однако, имеется небольшой побочный эффект — так как код при фиксировании эффективно контролирует вертикальную позицию элемента навигации, вы могли бы выпустить top:15px из своего CSS. Если вы участвуете в большом проекте, где над дизайном и сценарием внешнего интерфейса работают отдельные люди, это может вызвать проблему, так как следы подобного хака легко теряются. Просто чтобы вы знали – существуют такие плагины, как великолепный jQuery.Rule Эриела Флеслера (Ariel Flesler), которые можно применять для покрытия разрыва между скриптами и таблицами стилей. Вам нужно самому решить, нужно ли вам что-то подобное.

Скорее всего, еще подобного эффекта можно достичь с помощью @keyframes из CSS, но они гораздо меньше поддерживаются (и требуется множество префиксов производителей) и менее гибкие, и анимация подъема верх может оказаться большим разочарованием. Так как мы не сходим с пути прогрессивного улучшения, то у нас отсутствуют причины не придерживаться солидной функциональности jQuery.

Шаг 6: Выделение и гладкая прокрутка

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

$(function() {
  …
  // copy from here…
 
  var sections = $('section');
  var navigation_links = $('nav a');
 
  sections.waypoint({
    handler: function(event, direction) {
      // handler code
    },
    offset: '35%'
  });
 
  // …to here
});

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

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

var active_section;
active_section = $(this);
if (direction === "up") active_section = active_section.prev();
 
var active_link = $('nav a[href="#' + active_section.attr("id") + '"]');
navigation_links.removeClass("selected");
active_link.addClass("selected");

Во-первых, нам нужно знать, какой раздел в данный момент просматривается. Если прокрутить вниз, то раздел, к которому принадлежит вейпойнт – тот же, который активируется. Прокрутка вверх мимо вейпойнта, тем не менее, означает, что в поле зрения оказался предыдущий раздел — так что для его выбора мы применим .prev(). Затем удаляем класс selected из всех ссылок навигационной панели до его применения заново к тому классу, чей атрибут href соответствует id текущего активного раздела.

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

На каком-то этапе вы, может быть, заметили, что щелчок по ссылкам панели навигации помещает верх раздела на самую верхушку демонстрационного окна браузера. Весьма неожиданно, когда ничто не заслоняет эту часть экрана; теперь, когда у нас там находится навигационная панель, она становится сильным раздражителем. Вот где приходит на помощь ScrollTo от Эриела Флеслера (Ariel Flesler). Включите его в свою страницу, а затем вставьте следующий код:

navigation_links.click( function(event) {
 
  $.scrollTo(
    $(this).attr("href"),
    {
      duration: 200,
      offset: { 'left':0, 'top':-0.15*$(window).height() }
    }
  );
 
});

(конечно, этот код ставится над последней фигурной скобкой!)

Хотя существуют лучшие способы привязать функцию к событиям щелчка, мы собираемся придерживаться самого простого: .click(). Метод .scrollTo() вызывается способом, очень похожим на .waypoint(). Он принимает два параметра — цель прокрутки и объект, содержащие различные опции, которые в данном случае говорят сами за себя. Атрибут href нажатой ссылки отлично работает в качестве адресата прокрутки, а выражение, использованное как верхний офсет, размещает цель на 15% от высоты демонстрационного окна.

Заключение

Похоже, мы закончили. Я представил вам удобный небольшой плагин Waypoints, мы пробежались по нескольким случаям его применения, который должны были дать вам представление о множестве вещей, которые можно сделать с его помощью. Мы также выполнили более интуитивное поведение прокрутки, подходящее к навигации. Добавьте в смесь немного Ajax’а и окажетесь на пути к созданию некоего плавного, создающего эффект присутствия, впечатления от Сети, за которым будущее… ну, скорее всего это станет на какое-то время модной тенденцией, а затем будет общеупотребительным, заставляющим ветеранов Сети ностальгировать о прошлом. Эй, мир так устроен!

Что касается ленточек, их основной недостаток вот в чем: они просто иллюзия. Края ленты на самом деле не загибаются за края контейнера; они просто таковыми кажутся, что становится вполне ясно, когда лента проходит над элементом, выступающим за край страницы.

По причине того, как работает z-index, похоже, простых путей разрешения этого конфликта не существует, кроме избегания его. Однако с помощью воображения, а также знания основ jQuery можно спроектировать для этих элементов способ сдвигаться с пути ленты по мере ее приближения. Хотя этот прием сильно выходит за грань этого учебника; надеюсь, что рано или поздно смогу показать вам его в виде небольшой подсказки, здесь или на Nettuts+. Оставайтесь на связи!

Автор: Adam Avramov

Источник: http://webdesign.tutsplus.com/

Редакция: Команда webformyself.

JavaScript&jQuery с нуля до профи

Пройдите пошаговый видеокурс по JavaScript&jQuery

Научиться

Метки: , ,

Комментарии Вконтакте:

Комментарии Facebook:

Комментарии (15)

  1. Александр

    Всё бы хорошо, но в IE8 не работает… Об этом необходимо указать в материале.

  2. Нина

    Здравствуйте. Все замечательно, но есть вопрос, как это будет выглядеть в старых браузерах? Ведь и более простые элементы седьмой IE ломает.

  3. romapad

    У меня и в ie9 не захотел работать (windows7 64 bit), вывел список меню столбиком. А в последнем файрфокс работает, но верстка поехала. Жалко, а сама идея очень хорошая, давно ищу какой-нибудь кроссбраузерный вариант, можно конечно через position:fixed привязать просто панельку к верху страницы, но хочется именно так — чтоб панелька изначально шла под хедером, а при прокрутке закреплялась в top. Может кто-то сможет допилить это решение и сделать его кроссбраузерным? Тогда у нас будет информация, которой даже на http://webdesigntutsplus.s3.amazonaws.com еще нет)))

    • Андрей Кудлай

      Странно, у меня в Firefox 11.0 и 12.0 все ок… только Chapter IV с переносом (не критично). В Google Chrome 18.0.1025.162 также все ок. В Опере пока не проверял, на работе гляну.

      UPD. В Опере 11.62 тоже все ок.

  4. Dmitry

    Спасибо за урок. Жалко, что в самом популярном браузере, я имею ввиду IE, пока отсутствует поддержка HTML5 и CSS3. У меня, например, IE8. Также хотелось бы знать: вот фрагмент кода
    nav {
    2.
    position: relative;
    3.
    width: 928px;
    4.
    padding: 2em;
    5.
    margin-left: -14px;
    6.
    border-top-left-radius: 14px 7px;
    7.
    border-top-right-radius: 14px 7px;
    8.
    }
    Можно ли сделать width в процентах? И что тогда изменится?

    • Андрей Кудлай

      А попробовать? :)
      Можно задать и в процентах… но все же размеры лучше задавать здесь не в относительных, а абсолютных величинах.

    • Виктор Рог

      Мне кажется на счет того, что IE — это самый популярный браузер — Вы очень погорячились:)

      • Dmitry

        Я имел ввиду, то что большинство пользователей, у которых XP или 7 пользуются IE. Даже могу предположить, что они просто не знают, как установить другой :-) Так, например мой сайт dmitry-goodwin.blogspot.com/ посетило за 2 года чуть больше 5000 (пяти тысяч) человек. По той же статистике, зашло с IE около 53% пользователей. Ну что ты тут будешь делать? Как не поверить в могущество Microsoft? А если вы посмотрите сайт моей работы turgenev.ru (там есть раздел статистики), то там и того больше. Так что приходится мириться с властью IE, и подгонять все свои проекты и под этот дрянной браузер.

        • Виктор Рог

          Да не согласен я с Вами.:) У нас по статистике, ВНИМАНИЕ! только (3-5)% использует IE. Конечно это статистика только нашего сайта, но, как Вы могли заметить, у нас на сайте даже стоит заглушка на IE ниже 8 версии.
          Хотя, как видим, все еще зависит от тематики сайта.

          • Дмитрий

            Согласен, зависит от тематики сайта. Только вот какая штука. «Простых смертных» гораздо больше, чем программистов (верстальщиков, Web-мастеров и Web-дизайнеров). Отсюда, делаем вывод, что у них посещаемость выше. А то, что доступность на сайт или блог должна быть обеспеченна любым не устаревшим браузером — это знает любой продвинутый школьник. А вот, «интеллектуальная доступность», должна зависеть от тематической направленности сайта. По-моему — это верно и правильно.

  5. samadhi

    интересно, вполне возможно применю

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree