Адаптивный вертикальный аккордеон

В данном уроке мы создадим простой адаптивный аккордеон, который будет открывать разделы вниз во вертикали с плавным появлением содержания в них. Идея заключается в том, чтобы скомпоновать содержание с минимальной необходимостью для вертикальной прокрутки. Также мы будем использовать трансформации CSS3 для формирования стрелки рядом с разделом, которая будет появляться при наведении курсора мыши на название раздела и переворачиваться при открытии/закрытии. Аккордеон будет изменять размер вместе с окном браузера. 

Разметка HTML

Структура HTML состоит из контейнера с идентификатором и классом st-accordion, в котором располагается неупорядоченный список. Пункты списка имеют ссылки, работающие как название пункта, и область содержания, которая изначально скрыта. Элемент span с классом st-arrow будет работать как индикатор с правой стороны от названия раздела. Он будет появляться при наведении курсора мыши на родительский элемент, то есть на ссылку.

Code
<div id="st-accordion" class="st-accordion">
  <ul>
  <li>
  <a href="#">
  Item Title
  <span class="st-arrow">Open or Close</span>
  </a>
  <div class="st-content">
  <p>Some content</p>
  </div>
  </li>
  <li> ... </li>
  </ul>
</div>


CSS

Зададим стили для контейнера. Установим для него ширину 100%, чтобы она изменялась при изменении внешнего элемента. Вы можете использовать здесь то значение, которое нужно. Внешний элемент в демонстрации имеет ширину 90% при ограниченном максимуме 800 px. Элемент st-accordion будет иметь минимальную ширину 270 px:

Code
.st-accordion{
  width:100%;
  min-width:270px;
  margin: 0 auto;
}


Предположим, что мы используем сброс CSS, который удаляет отступы и поля у неупорядоченного списка. И в таких условиях определим стили для каждого элемента списка. Установим начальную высоту равную 100 px и скроем все что выходит за границу элемента. Таким образом мы не будем видеть содержание пункта. Когда пункт открывается, изменение высоты будет анимироваться для вывода содержания. Для разделения пунктов используется верхняя и нижняя части рамки.

Code
.st-accordion ul li{
  height: 100px;
  border-bottom: 1px solid #c7deef;
  border-top:1px solid #fff;
  overflow: hidden;
}


У первого пункта не должно быть верхней разделительной линии.

Code
.st-accordion ul li:first-child{
  border-top:none;
}


Для элемента ссылки добавляем трансформацию цвета, которая будет формировать визуальный эффект при наведении курсора мыши:

Code
.st-accordion ul li > a{
  font-family: 'Josefin Slab',Georgia, serif;
  text-shadow: 1px 1px 1px #fff;
  font-size: 46px;
  display: block;
  position: relative;
  line-height: 100px;
  outline:none;
  -webkit-transition: color 0.2s ease-in-out;
  -moz-transition: color 0.2s ease-in-out;
  -o-transition: color 0.2s ease-in-out;
  -ms-transition: color 0.2s ease-in-out;
  transition: color 0.2s ease-in-out;
}

.st-accordion ul li > a:hover{
  color: #1693eb;
}


Элемент span для стрелки позиционируется абсолютно и скрывается за счет
установки вне элемента ссылки и полной прозрачности. Трансформация
передвинет стрелку в область видимости и плавно проявит её:
Code
.st-accordion ul li > a span{
  background: transparent url(../images/down.png) no-repeat center center;
  text-indent:-9000px;
  width: 26px;
  height: 14px;
  position: absolute;
  top: 50%;
  right: -26px;
  margin-top: -7px;
  opacity:0;
  -webkit-transition: all 0.2s ease-in-out;
  -moz-transition: all 0.2s ease-in-out;
  -o-transition: all 0.2s ease-in-out;
  -ms-transition: all 0.2s ease-in-out;
  transition: all 0.2s ease-in-out;
}

.st-accordion ul li > a:hover span{
  opacity:1;
  right: 10px;
}


Когда открывается пункт, мы устанавливаем ему класс st-open, а элемент ссылки будет иметь такой же цвет, как и при наведении курсора мыши. Элемент span будет повернут, чтобы стрелка указывала вверх:

Code
.st-accordion ul li.st-open > a{
  color: #1693eb;
}
.st-accordion ul li.st-open > a span{
  -webkit-transform:rotate(180deg);
  -moz-transform:rotate(180deg);
  transform:rotate(180deg);
  right:10px;
  opacity:1;
}


Стили для элементов содержания:

Code
.st-content{
  padding: 5px 0px 30px 0px;
}
.st-content p{
  font-size: 16px;
  font-family: Georgia, serif;
  font-style: italic;
  line-height: 28px;
  padding: 0px 4px 15px 4px;  
}
.st-content img{
  width:125px;
  border-right:1px solid #fff;
  border-bottom:1px solid #fff;
}


С помощью медиа запроса мы изменяем размер шрифта на более мелкий при уменьшении ширины экрана:

Code
@media screen and (max-width: 320px){
  .st-accordion ul li > a{
  font-size:36px;
  }
}


JavaScript

Рассмотрим важные части плагина. Начнем с определения опций и их значений по умолчанию:
Code
$.Accordion.defaults = {
  // Индекс открытого пункта. -1 означает, что все пункты закрыты по умолчанию.
  open : -1,
  // Если данная опция имеет значение true, то только один пункт может быть открыт. При открытии пункта другой открытый пункт закрывается.
  oneOpenedItem : false,
  // Скорость анимации открытия/закрытия
  speed : 600,
  // Эффект анимации открытия/закрытия
  easing : 'easeInOutExpo',
  // Скорость прокрутки анимации действия
  scrollSpeed : 900,
  // Эффект прокрутки анимации действия
  scrollEasing : 'easeInOutExpo'
  };


Плагин инициализируется вызовом функции _init:

Code
_init : function( options ) {
   
  this.options = $.extend( true, {}, $.Accordion.defaults, options );
   
  // Проверка опций
  this._validate();
   
  // current - индекс открытого пункта
  this.current = this.options.open;
   
  // Скрываем содержание, чтобы вывести его потом
  this.$items.find('div.st-content').hide();
   
  // Сохраняем оригинальную высоту и координату по вертикали для каждого пункта
  this._saveDimValues();
   
  // Если нужно по умолчанию открыть пункт
  if( this.current != -1 )
  this._toggleItem( this.$items.eq( this.current ) );
   
  // Инициализируем события
  this._initEvents();
   
},


Функция _saveDimValues сохраняет оригинальные значения высоты и координаты пункта, так что мы знаем куда нужно прокручивать содержание, когда пункт открывается.

Если есть пункт, который должен быть открыт по умолчанию, то вызываем функцию _toggleItem. А затем инициализируем события.

Функция _toggleItem обрабатывает два варианта при нажатии кнопки мыши на пункте. Либо пункт уже открыт, то есть имеет класс st-open, либо он закрыт. Если он открыт, присваиваем переменной current значение -1 и скрываем содержание, изменяя высоту пункта до оригинального значения. Если пункт закрыт, то мы присваиваем переменной currrent индекс пункта, анимируем изменение высоты и прозрачности содержания для вывода на экран. Затем мы прокручиваем окно вниз с помощью функции _scroll, чтобы открытый пункт оказался вверху:

Code
// Открыть / закрыть пункт
_toggleItem : function( $item ) {
   
  var $content = $item.find('div.st-content');
   
  ( $item.hasClass( 'st-open' ) )  
   
  ? ( this.current = -1, $content.stop(true, true).fadeOut( this.options.speed ), $item.removeClass( 'st-open' ).stop().animate({
  height : $item.data( 'originalHeight' )
  }, this.options.speed, this.options.easing ) )
   
  : ( this.current = $item.index(), $content.stop(true, true).fadeIn( this.options.speed ), $item.addClass( 'st-open' ).stop().animate({
  height : $item.data( 'originalHeight' ) + $content.outerHeight( true )
  }, this.options.speed, this.options.easing ), this._scroll( this ) )

},


Функция _initEvents инициализирует два события - нажатие кнопки мыши на пункте и изменение размеров окна.

При нажатии кнопки мыши на пункте, он открывается или закрывается с помощью функции _toggleItem. Если опция oneOpenedItem имеет значение true, то сначала закрывается другой открытый пункт.

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

Code
_initEvents : function() {
   
  var instance = this;
   
  // Открыть / закрыть пункт
  this.$items.find('a:first').bind('click.accordion', function( event ) {
   
  var $item = $(this).parent();
   
  // Закрыть любой открытый пункт если опция oneOpenedItem имеет значнеие true
  if( instance.options.oneOpenedItem && instance._isOpened() && instance.current!== $item.index() ) {
   
  instance._toggleItem( instance.$items.eq( instance.current ) );
   
  }
   
  // Открыть / закрыть пункт
  instance._toggleItem( $item );
   
  return false;
   
  });
   
  $(window).bind('smartresize.accordion', function( event ) {
   
  // Сброс оригинальных занчений пункта
  instance._saveDimValues();
   
  // Сброс высоты содержания любого пункта, который открыт
  instance.$el.find('li.st-open').each( function() {
   
  var $this = $(this);
  $this.css( 'height', $this.data( 'originalHeight' ) + $this.find('div.st-content').outerHeight( true ) );
   
  });
   
  // Прокрутка до текущего пункта
  if( instance._isOpened() )
  instance._scroll();
   
  });
   
},


Выше описаны самые важные функции кода. Полностью текст JavaScript плагина вы можете посмотреть в исходниках.

  • FalleN

  • 4102

  • 1

  • 242

Ссылки на статью:

Похожие статьи: