javascript UI accordion без JQuery UI

javascript UI accordion без JQuery UI

Статья для тех, кто хочет использовать такой элемент пользовательского интерфейса, как javascript UI accordion (разъезжающиеся вкладки) содержащие контент, но не хочет при этом подключать громоздкие JQuery + JQuery UI. Работает во всех "нужных" браузерах, включая IE линейку, начиная с 6-ой версии. Имеет настройки и собственные события.

Размеры:

  • Source с комментами 6,55 КБ
  • Packed 1.38 КБ

Архив скрипта с примерами accordion.v.1.1: javascript UI accordion v.1.1 скачать.

Архив скрипта с примерами accordion.v.1.2: javascript UI accordion v.1.2 скачать.
(Добавлена возможность программно управлять открытием/закрытием панелек)

javascript UI accordion демо:

Панель 1

javascript UI accordion dolor sit amet, consectetur adipiscing elit. Integer venenatis rhoncus ligula ac ultrices. Donec rhoncus nunc et dolor laoreet pharetra. Curabitur tempus justo id nibh elementum sed rutrum risus adipiscing. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse nec malesuada urna.

Панель 2

javascript UI accordion consectetur odio, quis suscipit turpis faucibus id. Aliquam mollis consequat venenatis. Fusce at convallis dui. Etiam blandit magna non dolor dignissim et commodo odio euismod. Pellentesque lobortis semper ullamcorper. Praesent tristique euismod enim ut adipiscing. Maecenas rhoncus nisl et mi eleifend at venenatis velit adipiscing.

javascript UI accordion consectetur odio, quis suscipit turpis faucibus id. Aliquam mollis consequat venenatis. Fusce at convallis dui. Etiam blandit magna non dolor dignissim et commodo odio euismod. Pellentesque lobortis semper ullamcorper. Praesent tristique euismod enim ut adipiscing. Maecenas rhoncus nisl et mi eleifend at venenatis velit adipiscing.

Панель 3

javascript UI accordion, libero non porta hendrerit, enim nisi imperdiet ligula, ac aliquet ipsum nisl in nisl. Aliquam vitae est ante. Suspendisse lacinia urna non nisi ultrices nec lobortis tellus tristique. Mauris eros mauris, pharetra ut luctus sit amet, molestie eu orci. Aliquam erat volutpat. Proin non tortor ligula. Vestibulum rhoncus ipsum nec arcu fringilla nec convallis lectus dignissim.

javascript UI accordion подготовка и подключение.

Для работы скрипта требуется следующий HTML - каркас (использовано в демо):

<!-- Контейнер  -->
<dl id="pane">
	<dt><!-- Заголовок 1  --></dt>
	<dd>
    	<!-- Контент 1 -->
    </dd>

    ...
    
	<dt><!-- Заголовок N  --></dt>
	<dd>
    	<!-- Контент N -->
    </dd>    
</dl>

Или такой:

<!-- Контейнер  -->
<div id="pane">
    <div><!-- Заголовок 1  --></div>
    <div>
        <!-- Контент 1 -->
    </div>

    ...
    
    <div><!-- Заголовок N  --></div>
    <div>
        <!-- Контент N -->
    </div>
</div>

Или такой:

<!-- Контейнер  -->
<div id="pane">
    <h3><!-- Заголовок 1  --></h3>
    <div>
        <!-- Контент 1 -->
    </div>
    ...
    
    <h3><!-- Заголовок N  --></h3>
    <div>
        <!-- Контент N -->
    </div>
</div>

В общем ему абсолютно пофиг, javascript UI accordion работает по следующему принципу: все не чётные элементы (прямые DOM - потомки) внутри контейнера с id="pane" станут как бы заголовками - элементами над которыми нужно произвести действие, а чётные элементы (прямые DOM - потомки) станут собственно свёрнутым содержимым - телами которые будут раскрываться/сворачиваться когда над заголовком совершается действие. Таким макаром семантику каркаса вы строите как вам нужно!

Для правильной работы javascript UI accordion нужно что бы каждый элемент - тело имел свой элемент - заголовок, т.е. количество элементов в контейнере должно быть чётным.

Пример CSS (использовано в демо):

#pane dt{
	border: solid 1px #666;
	padding: 10px 5px 10px 25px;
	background: #AAA url("/images/down.png") no-repeat 5px center;
	color  : #fff;
	cursor : pointer;
	font-weight: bold;
	margin: 1px auto;
}
#pane dt:hover{
	color : #666;
}
#pane dt#pane-current{
	background: #DDD url("/images/up.png") no-repeat 5px center;
}
#pane dd{
	margin : 0px;
	padding: 0px;
	background : #EEE;
}
#pane dd p {
	padding: 10px 20px;
	color : #666;
}

Далее подключаем скрипт и вызываем ф-цию Accordion(), передав ей ссылку на HTMLElement - контейнер:

Accordion(document.getElementById("pane"));

Вообще Функция Accordion() принимает два параметра:

  1. container - HTMLElement контейнер, содержащий заголовки и тела элементов аккордеона
  2. config - Object объект конфигурации, который может содержать свойства перечисленные ниже:

javascript UI accordion конфигурация

Конфигурировать можно следующие параметры (их можно увидеть в строке 58 листинга скрипта):

int : openIdx - Заранее открытая вкладка
Вкладка с этим номером будет открыта при загрузке скрипта, отсчёт начинается с 1, если указать не существующий номер вкладки или 0 - все вкладки на момент инициализации скрипта будут закрыты.
int : speed - Скорость открывания вкладок
Скорость - чем больше тем быстрее будут открываться и закрываться элементы. По умолчанию 5
string : event - Название события: click или mouseover (без приставки "on")
События по которому будет срабатывать свёртывание/развёртывание. По умолчанию click. Вы можете поэксперементировать с событиями, но я не вижу смысла в использовании других :)
Function : onBeforeOpen - ссылка на callback функцию
Это должна быть ссылка на callback функцию - обработчик, которую вы хотите вызывать каждый раз ПЕРЕД открытием вкладок.
Function : onAfterOpen - ссылка на callback функцию
Это должна быть ссылка на callback функцию - обработчик, которую вы хотите вызывать каждый раз ПОСЛЕ открытия вкладок.
Function : onBeforeCollapse - ссылка на callback функцию
Это должна быть ссылка на callback функцию - обработчик, которую вы хотите вызывать каждый раз ПЕРЕД закрытием вкладок.
Function : onAfterCollapse - ссылка на callback функцию
Это должна быть ссылка на callback функцию - обработчик, которую вы хотите вызывать каждый раз ПОСЛЕ закрытия вкладок.

Callback функции принимают как единственный параметр - объект состояие описанный в листинге в 129 строке :

        // Этот объект будет представлять текущее 
        // состояние и иметь следующие свойства:
        cur = {            
            dt : 	// Элемент - заголовок            
            dd :	// Элемент - тело            
            num :	// Номер вкладки           
            state : // Состояние (закрыто 0, открыто 1)
        };
        

Используя функции обратного вызова вы можете например подгружать контент при помощи AJAX

Пример вызова javascript UI accordion со всеми параметрами:

Accordion( 
    document.getElementById("pane"),
    {   
        openIdx : 1,
        speed   : 7,
        event   : "click",
        onBeforeOpen : function(p){alert("Pane № " + p.num + ": I`am beginning to open !")},
        onAfterOpen  : function(p){alert("Pane № " + p.num + ": I`am opened !")},
        onBeforeCollapse : function(p){alert("Pane № " + p.num + ": I`m beginning to close!")},
        onAfterCollapse  : function(p){alert("Pane № " + p.num + ": I`am closed !")}
    }
);

javascript UI accordion листинг

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

var Accordion = (function (GLOB) {
	"use strict";
	// Анимация свёртывания панели:
	function collapse(element, ctx) {
		var i       = element.dd.scrollHeight,
			intId   = GLOB.setInterval(
				function () {
					if (i <= 0) {
						element.dd.style.height = "0px";
						GLOB.clearInterval(intId);
                        // Изменяем состояние объекта:                        
                        // Вызываем обработчик события "после свёртывангия панели"
                        ctx.cfg.onAfterCollapse(element);
						return true;
					}
					element.dd.style.height = (i -= ctx.cfg.speed) + "px";
					return true;
				},
				10
			);
        // Вызываем обработчик события "до свёртывангия панели"
        ctx.cfg.onBeforeCollapse(element);
        element.state = 0;
		return true;
	}
	// Анимация развёртывания панели:
	function expand(element, ctx) {
		var i       = 0,
            height  = element.dd.scrollHeight,
			intId   = GLOB.setInterval(
				function () {
					if (i >= height) {
						element.dd.style.height = height + "px";
						GLOB.clearInterval(intId);
                        // Изменяем состояние объекта:
                        
                        // Вызываем обработчик события "после открытия панели"
                        ctx.cfg.onAfterOpen(element);
						return true;
					}
					element.dd.style.height = (i += ctx.cfg.speed) + "px";
					return true;
				},
				10
			);
        // Вызываем обработчик события "до открытия панели"
        ctx.cfg.onBeforeOpen(element);
        element.state = 1;
		return true;
	}
    /**
     * Возвращаемая ф-ция
     * @param HTMLElement element - элемент для создания вкладок,
     * @param Object config - объект конфигурации.
     */
	return function (element, config) {
		return {
            // Объект конфигурации:
			cfg			: {
                // Заранее открытая вкладка:
				openIdx	: 0,
                // Скорость открытия/закрытия
				speed	: 5,
                // Тип события на которое будем реагировать.
				event	: "click",
                // Обработчик вызывается ПЕРЕД ОТКРЫТИЕМ вкладки el-HTMLElement - текущая вкладка
                onBeforeOpen:   function (el) {return true; },
                // Обработчик вызывается ПОСЛЕ ОТКРЫТИЯ вкладки el-HTMLElement - текущая вкладка
                onAfterOpen:    function (el) {return true; },
                // Обработчик вызывается ПЕРЕД ЗАКРЫТИЕМ вкладки el-HTMLElement - текущая вкладка
                onBeforeCollapse:   function (el) {return true; },
                // Обработчик вызывается ПОСЛЕ ЗАКРЫТИЯ вкладки el-HTMLElement - текущая вкладка
                onAfterCollapse:    function (el) {return true; }
			},
			// Метод для "перегона" свойств из объекта - параметра конфигурации во внутренний объект
			option		: function (config) {
				var p;
				for (p in config) {
					// Здесь JSLint будет брехать - игнорим!
					if (this.cfg.hasOwnProperty(p)) {
						this.cfg[p] = typeof config[p] === "undefined" ? this.cfg[p] : config[p];
					}
				}
				return true;
			},
            // Инициализация объекта - вызывается автоматом сразу.
			init	: function (element, config) {
				var prefix	= element.id,
					panes	= element.children,
					length	= panes.length,
					openObj = null,
					ctx = this,
					cur,
					i;
                // Задаём конфигурацию:
				ctx.option(config);
                // Обработчик вынесен в эту ф-цию, что бы не навешивать его внутри цикла:
				function actionHandler(current) {
                    // Вешаем обработчики (помним: событие задаётся в настройках ctx.cfg.event):
					current.dt["on" + ctx.cfg.event] = function () {
						// Если кликнули на новой панельке - Меняем ссылку на текущий объект:
						if (openObj !== current) {
							// Если openObj назначен вообще:	
							if (openObj && openObj.state === 1) {
								openObj.dt.removeAttribute("id");
								collapse(openObj, ctx);

							}
							openObj = current;
							openObj.dt.id = prefix + "-current";
							expand(openObj, ctx);
							return true;
						}
						// Текущий объект в состоянии "ЗАКРЫТ" - Открываем:
						if (current.state === 0) {
							openObj.dt.id = prefix + "-current";
							expand(openObj, ctx);
							return true;
						}
						// Текущий объект в состоянии "ОТКРЫТ" - Закрываем:
						openObj.dt.removeAttribute("id");
						collapse(openObj, ctx);
						return true;
					};
					return true;
				}
                // Пройдёмся по всем четным! вкладкам, развесим на них обработчики:
				for (i = 0; i < length; i += 2) {
                    // Этот объект будет представлять текущее состояние
                    // на момент прохода цикла.
					cur = {
                        // Элемент - заголовок
						dt : panes[i],
                        // Элемент - тело
						dd : panes[i + 1],
                        // Номер вкладки:
                        num : (i / 2) + 1,
                        // Состояние (учитывая настройки ставим закрыто 0/открыто 1)
						state : (i === ((ctx.cfg.openIdx - 1) * 2)) ? 1 : 0
					};
                    // Если сотояние текущего объекта открыто:
					if (cur.state === 1) {
						openObj			= cur;
						openObj.dt.id	= prefix + "-current";
					} else {
						cur.dd.style.height	= "0px";
					}
					cur.dd.style.overflow	= "hidden";
					actionHandler(cur);
				}
				return true;
			}
		}.init(element, config);
	};
}(this));

Вот собственно и всё. Пользуйтесь на здоровье и пишите свои впечатления и пожелания. :)

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


Защитный код
Обновить



Кто на сайте
Сейчас 179 гостей онлайн