Создаем простое, умное выпадающее меню с помощью библиотек Prototype и Scriptaculous

javascript

«Мы все когда-нибудь видели меню в виде аккордеона, эффектно смотрящееся на сайтах Web 2.0, однако, множество скриптов-«аккордеонов» тяжелы, малополезны для библиотек, на которых основаны, и не обеспечивают подбора высоты.»

Перед тем, как мы приступим к практическим шагам, я Вам рекомендую скачать исходный код, чтоб он у Вас был под рукой.

Также результат того, что мы будем делать, Вы можете посмотреть здесь.

Шаг 1 – Цель

Наша задача – создать простой скрипт-«аккордеон» на основе javascript-библиотек Prototype и Scriptaculous. «Аккордеон» должен:

Поддерживать неограниченное количество выпадающих панелей

Быть полностью настраиваемым с помощью CSS

Быть ненавязчивым – пользователи с выключенным javascript должны видеть все содержимое Вашего «аккордеона»

Быть легковесным – со сравнительно небольшим кодом, использовать делегирование для ограничения потребления памяти

Поддерживать любые виды содержимого внутри

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

Это — сравнительно новое учебное пособие, которое предполагает, что читатель уже имеет определенный объем знаний Javascript, CSS, HTML, объектно-ориентированного программирования и знания основ библиотек Prototype и Scriptaculous. Однако полный source-код доступен и прост для чтения и изучения, даже если вы не знакомы с используемой специфической библиотекой. Перед началом можно просмотреть рабочую версию «аккордеона» в действии.

javascript

Шаг 2 — Начнем с основной разметки

Для начала мы создадим простую HTML-разметку для нашего «аккордеона».

<div id="test-accordion" class="accordion">    
     <div class="accordion-toggle">Toggle 1</div>    
     <div class="accordion-content">Content 1</div>    
     <div class="accordion-toggle">Toggle 2</div>    
     <div class="accordion-content">Content 2</div>    
     <div class="accordion-toggle">Toggle 3</div>    
     <div class="accordion-content">Content 3</div>    
     <div class="accordion-toggle">Toggle 4</div>    
     <div class="accordion-content">Content 4</div>
</div>

Шаг 3 – Добавим чуть-чуть стилей

Далее нам нужно добавить немного стилей к нашему выпадающему меню, чтобы оно смотрелось именно как аккордеон. Для начала мы определим основные стили, работу над которыми продолжим в процессе работы. Для пущей уверенности в том, что «аккордеон» будет работать именно так, как задумано, следует включить сюда некоторые дополнительные стили.

div#test-accordion{
    margin: 10px;
    border: 1px solid #aaa;}

div.accordion {
    position: relative; /* required for bounding--works around a "peculiarity" in Prototype */
}

div.accordion-toggle{
    position: relative; /* required for effect */
    z-index: 10;		/* required for effect */
    background: #eee;   /* required for effect--can be anything except "transparent" */
    cursor: pointer;
}

div.accordion-toggle-active{
    background: #fff;
}

div.accordion-content{
    overflow: hidden;	/* required for effect */
    background: #aaa;
}

Шаг 4 – Создаем «аккордеон» с помощью Javascript

Prototype предлагает чудесную основу для построения категорий в Javascript и мы используем эту функциональность для построения нашего выпадающего меню. Этот класс будет содержать все свойства и методы выпадающего меню типа «аккордеон»: текущую видимую панель, содержимое «аккордеона», методы распахивания и сжатия панелей и методы обработчики событий для определения того, что происходит, когда пользователи кликают на «аккордеон». Теперь опишем основную структуру класса, свойства и методы, которые нам понадобятся:

var Accordion = Class.create({
initialize: function(){
this.accordion = null;           /* Stores a pointer to the the accordion element */
this.contents = null;            /* Array of pointers to the headings and content panes of the accordion */
this.options = null;             /* Allows user to define the names of the css classes */
this.maxHeight = 0;              /* Stores the height of the tallest content pane */
this.current = null;             /* Stores a pointer to the currently expanded content pane */
this.toExpand = null;            /* Stores a pointer to the content pane to expand when a user clicks */
this.isAnimating = false;        /* Keeps track of whether or not animation is currently running */
    },
checkMaxHeight: function(){},         /* Determines the height of the tallest content pane */
initialHide: function(){},            /* Hides the panes which are not displayed by default */
attachInitialMaxHeight: function(){}, /* Ensures that the height of the first content pane matches the tallest */
expand: function(el){},               /* Tells the animation function which elements to animate */
animate: function(){},                /* Performs the actual animation of the accordion effect */
handleClick: function(e){}            /* Determine where a user has clicked and act based on that click */
});

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

Шаг 5 – Инициализируем: начало

Классы Prototype имеют специальный метод-конструктор initialize() — это значит, что он действует, когда пользователь создает новый запрос такого класса. Для любого «аккордеона» нужно знать две вещи перед началом:

ID элемента «аккордеона»

Начальную позицию «аккордеона» по умолчанию (если есть что-то кроме первой позиции)

Итак, нам необходимо разрешить нашему конструктору принять эти два параметра. К тому же, наш конструктор должен:

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

Устанавливать опции, выбранные пользователем

Устанавливать текущий распахнутый элемент

Определять максимальную высоту, которую мы будем использовать, для всех панелей содержимого и применять ее

Прятать панели содержимого, которые не показываются по умолчанию

Добавить обработчик событий к «аккордеону», чтобы отслеживать клики пользователей

Вот код для метода инициализации:

initialize: function(id, defaultExpandedCount) {
    if(!$(id)) throw("Attempted to initalize accordion with id: "+ id + " which was not found.");
    this.accordion = $(id);
    this.options = {
        toggleClass: "accordion-toggle",
        toggleActive: "accordion-toggle-active",
        contentClass: "accordion-content"
    }
    this.contents = this.accordion.select('div.'+this.options.contentClass);
    this.isAnimating = false;
    this.maxHeight = 0;
    this.current = defaultExpandedCount ? this.contents[defaultExpandedCount-1] : this.contents[0];
    this.toExpand = null;

    this.checkMaxHeight();
    this.initialHide();
    this.attachInitialMaxHeight();

    var clickHandler =  this.clickHandler.bindAsEventListener(this);
    this.accordion.observe('click', clickHandler);
}

Видите, мы разумно установили все значения по умолчанию и применили три метода, чтобы все упорядочить. Наконец, мы прикрепили к «аккордеону» обработчик событий. Давайте создадим эти три метода и обработчик событий.

Шаг 6 – Ограничиваем самый длинный элемент

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

checkMaxHeight: function() {
    for(var i=0; i<this.contents.length; i++) {
        if(this.contents.getHeight() > this.maxHeight) {
            this.maxHeight = this.contents.getHeight();
        }
    }
}

Шаг 7 – Прячем все остальное

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

initialHide: function(){
    for(var i=0; i<this.contents.length; i++){
        if(this.contents != this.current) {
            this.contents.hide();
            this.contents.setStyle({height: 0});
        }
    }
}

Шаг 8 – Показываем панель содержимого по умолчанию

Теперь, когда мы спрятали все панели, кроме панели по умолчанию, нам нужно убедиться, что панель содержимого по умолчанию отражается правильно – ее название должно иметь «активный» вид, а ее высота должна соответствовать установленной максимальной высоте:

attachInitialMaxHeight: function() {
this.current.previous('div.'+this.options.toggleClass).addClassName(this.options.toggleActive);
if(this.current.getHeight() != this.maxHeight) this.current.setStyle({height: this.maxHeight+"px"});
}

Шаг 9 – Создаем обработчик событий

Если вы придерживаетесь традиционного взгляда на размещение обработчиков событий, в соответствии с которым обработчики присоединяются к той области, где возникает событие, то вас может сбить с толку, что мы присоединяем обработчик событий к одному-единственному элементу. Мы используем делегирование событий.

clickHandler: function(e) {
    var el = e.element();
    if(el.hasClassName(this.options.toggleClass) && !this.isAnimating) {
        this.expand(el);
    }
}

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

Шаг 10 – Начинаем процесс

Теперь уже можно начать создавать эффект «аккордеона». Мы уже знаем, что метод expand() должен принимать параметр – ссылку на элемент, на который кликнули. Используя этот параметр, метод определяет, которую из панелей содержимого развернуть, и если она еще не развернута, то применить анимацию, чтобы показать это действие в процессе.

expand: function(el) {
    this.toExpand = el.next('div.'+this.options.contentClass);
    if(this.current != this.toExpand){
	    this.toExpand.show();
        this.animate();
    }
},

Шаг 11 – Делаем черновую работу

Сейчас все составляющие уже находятся на своих местах. Мы знаем, какая из панелей показывается, знаем, который из заголовков кликнул пользователь, а также знаем, которую из панелей содержимого вызвал пользователь. Теперь нужно создать анимацию «аккордеона». Для этого мы создадим метод анимации, использующий класс Scriptaculous Effect.Parallel, чтобы нарисовать две анимации совместно, и Effect.Scale, чтобы изменить размер каждой панели содержимого. Метод анимации должен выполнить следующие шаги:

Создать массив, который будет использоваться для хранения объектов Effect.Scale.

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

Добавить этот объект в массив.

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

Добавить этот объект в массив.

Создать объект Effect.Parallel , который будет синхронно запускать объекты Effect.Scale.

Уведомить объект о начале операции

Запустить анимацию.

Очистить все затрагивавшиеся стили заголовков.

Уведомить объект о завершении операции.

animate: function() {
    var effects = new Array();
    var options = {
        sync: true,
        scaleFrom: 0,
        scaleContent: false,
        transition: Effect.Transitions.sinoidal,
        scaleMode: {
            originalHeight: this.maxHeight,
            originalWidth: this.accordion.getWidth()
        },
        scaleX: false,
        scaleY: true
    };

    effects.push(new Effect.Scale(this.toExpand, 100, options));

    options = {
        sync: true,
        scaleContent: false,
        transition: Effect.Transitions.sinoidal,
        scaleX: false,
        scaleY: true
    };

    effects.push(new Effect.Scale(this.current, 0, options));

    new Effect.Parallel(effects, {
        duration: 0.5,
        fps: 35,
        queue: {
            position: 'end',
            scope: 'accordion'
        },
        beforeStart: function() {
            this.isAnimating = true;
            this.current.previous('div.'+this.options.toggleClass).removeClassName(this.options.toggleActive);
            this.toExpand.previous('div.'+this.options.toggleClass).addClassName(this.options.toggleActive);
        }.bind(this),
        afterFinish: function() {
            this.current.hide();
            this.toExpand.setStyle({ height: this.maxHeight+"px" });
            this.current = this.toExpand;
            this.isAnimating = false;
        }.bind(this)
    });
}

Для полного описания параметров, которые мы передаем объектам Effect.Scale и Effect.Parallel, смотрите документацию к Scriptaculous. Важными аспектами метода являются методы beforStart и afterFinish в нашем Effect.Parallel. Метод beforStart информирует «аккордеон» о том, что он в данный момент анимируется. Этим предотвращается возможная попытка обработчика событий начать какие-либо изменения, пока проигрывается анимация. Таким же образом, заголовку, на который кликнули, устанавливается CSS-класс, соответствующий активному имени. Метод afterFinish полностью скрывает панель содержимого, которая была активной ранее (после того, как она полностью спрятана благодаря анимации). Он также обеспечивает то, что конечная высота текущей панели содержимого является правильной. Когда замена произошла, он информирует наш «аккордеон» о том, что разворачиваемая панель стала активной и анимация закончена.

Шаг 12 – Добавим еще стиля

В этот момент мы уже имеем пристойно выглядящий «аккордеон», который можно посмотреть в действии здесь. Но с небольшой помощью CSS мы можем заставить его выглядеть еще более зрелищно. Итак, во-первых, мы создаем быстрый рабочий образец Photoshop, так что можно примерно понять идею того, как все это будет выглядеть. Помня об этом, мы собираемся создать три картинки:

Картинку-логотип

javascript

Парочку симпатичных фоновых картинок

javascript
javascript

А вот обещанный CSS-код:

body {
	padding: 130px 50px 50px 50px;
	background: #252422 url(../img/logo.gif) no-repeat;
	background-position: 60px 40px;
	font-family: "Lucida Grande", "Lucida Sans Unicode", Arial, Sans-serif;
	font-size: 11px;
	line-height: 18px;
}

div#test-accordion{
	border: 1px solid #343230;
	background-color: #21201f;
	padding: 10px;
}

div.accordion {
	position: relative; /* required for bounding */http://nettuts.com/wp-admin/users.php
	width: 800px;
}

div.accordion-toggle{
	position: relative; /* required for effect */
	z-index: 10;		/* required for effect */
	background: #3f3c38 url(../img/off.jpg) repeat-x;
	background-position: bottom;
	color: #fff;
	cursor: pointer;
	margin-bottom: 1px;
	padding: 9px 14px 6px 14px;
	border-top: 1px solid #5d5852;
}

div.accordion-toggle:hover, div.accordion-toggle-active{
	background-image: url(../img/on.jpg);
	background-color: #6d493a;
	border-top: 1px solid #a06b55;
}

div.accordion-content{
	overflow: hidden;	/* required for effect */
	background: #302e2c;
	color: #c4bab1;
	border-bottom: 1px solid #000;
}

div.accordion-content p{
margin: 9px 24px 6px 24px;
}

Видно, что здесь мы:

Добавили немного фоновых стилей вокруг странички и «аккордеона».

Придали рамке «аккордеона» постоянный фоновый цвет.

Рамка «аккордеона» при движении мышью и активные заголовки внутри теперь используют один и тот же красноватый цвет.

Шаг 13 – Запускаем

Работающую модель можно видеть здесь. Вы можете также добавить свой собственный CSS и картинки, чтобы отточить вид сайта. Скачайте accordion.js&accordion.css.

Перевод и редакция: Рог Виктор и Андрей Бернацкий. Команда webformyself.

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

E-mail:contact@webformyself.com

Проект webformyself.com — Как создать свой сайт. Основы самостоятельного сайтостроения

P.S. Хотите опубликовать интересный тематический материал? Если ответ «Да», то жмите сюда.

Практика HTML5 и CSS3 с нуля до результата!

Получите бесплатный пошаговый видеокурс по основам адаптивной верстки с полного нуля на HTML5 и CSS3

Получить

Метки: ,

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

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

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

  1. сергей

    СПАСИБО ЗА ВАШ ТРУД!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  2. Георгий

    Однако, хлопотно это! Чегой-то я уже запутался, но очень интересно! Вы действительно большое дело делаете, спасибо!

  3. dak

    как настроить checkMaxHeight по высоте выпадающего окна, и сделать, чтобы при перезагрузке страницы оставалось выпадающим то окно, которое и было до перезагрузки,
    думаю, нужно не только мне :)

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

Ваш 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