Парочка аккордеонов
Несколько вариантов реализации меню-аккордеона с использованием библиотеки Prototype
Недавно в одном из проектов появилась необходимость использовать аккордеон (подобный используемому на Desert Ridge Marketplace). Любят заказчики дешевые эффекты, ничего здесь не поделаешь
Чем мне не понравился аккордеон на Desert Ridge — это полной зависимостью от JavaScript: если JavaScript выключен, навигация по сайту переставала работать. Это мне не понравилось, и я решил написать свой.
Представляю два решения:
- Подменю сворачивается/разворачивается по щелчку, элементы меню не зависят друг от друга;
- Подменю сворачивается/разворачивается по щелчку, при этом не может быть более одного развёрнутого подменю.
При выключенном JavaScript оба аккордеона трансформируются в двухуровневый список.
Поехали. Будем использовать следующую разметку:
<li>
<a href="#">Item 1</a>
<ul>
<li><a href="#">SubItem 1.1</a></li>
<li><a href="#">SubItem 1.2</a></li>
<li><a href="#">SubItem 1.3</a></li>
<li><a href="#">SubItem 1.4</a></li>
</ul>
</li>
<li class="active">
<a href="#">Item 2</a>
<ul>
<li><a href="#">SubItem 2.1</a></li>
<li><a href="#">SubItem 2.2</a></li>
<li><a href="#">SubItem 2.3</a></li>
<li><a href="#">SubItem 2.4</a></li>
</ul>
</li>
<li>
<a href="#">Item 3</a>
<ul>
<li><a href="#">SubItem 3.1</a></li>
<li><a href="#">SubItem 3.2</a></li>
</ul>
</li>
<li>
<a href="#">Item 4</a>
<ul>
<li><a href="#">SubItem 4.1</a></li>
<li><a href="#">SubItem 4.2</a></li>
</ul>
</li>
</ul>
С разметкой всё просто: обычный двухуровневый список; предполагается, что меню Item2 активно (то есть должно быть развёрнуто).
Оформим аккордеон при помощи CSS:
a:hover { text-decoration: underline; }
#accordion a { outline: 0; }
#accordion, #accordion ul, #accordion li {
list-style: none;
margin: 0;
padding: 0;
display: block;
}
#accordion {
border: 1px solid #CCC;
width: 150px;
}
#accordion ul {
margin-left: 40px;
}
#accordion > li {
border-bottom: 1px solid #CCC;
padding: 2px 5px;
}
#accordion > li:last-child {
border-bottom: 0;
}
#accordion li.active > a {
color: red;
}
Покажем уродство IE6, не понимающего CSS 2.1:
border-bottom: 1px solid #CCC;
padding: 2px 5px;
}
* html #accordion li li {
border-bottom: 0;
padding: 0;
}
* html #accordion {
border-bottom: 0;
}
* html #accordion li.active a {
color: red;
}
* html #accordion li.active ul a {
color: blue;
}
За использование пяти лишних правил я и не люблю IE6 (и это мы еще используем простую разметку).
Переходим к JavaScript. В данных примерах я использую библиотеки Prototype и Scriptaculous Effects (всё очень легко переводится на jQuery).
Вариант 1: независимые элементы меню
$$('#accordion > li[class="active"] ul').invoke('addClassName', 'expanded');
$$('#accordion > li > a').invoke('observe', 'click',
function(e)
{
e.stop();
var ul = e.findElement('a').up('li').down('ul');
if (ul) {
new Effect.toggle(ul.toggleClassName('collapsed').toggleClassName('expanded'), 'blind');
}
}
);
Рабочий пример для первого варианта.
Вариант 2: развёрнуто не более одного меню
$$('#accordion > li[class="active"] ul').invoke('addClassName', 'expanded');
$$('#accordion > li > a').invoke('observe', 'click',
function(e)
{
e.stop();
var ul = e.findElement('a').up('li').down('ul');
if (ul) {
if (ul.hasClassName('collapsed')) {
var c = $$('#accordion ul:not([class="collapsed"])')[0];
if (c) {
new Effect.BlindUp(c.toggleClassName('collapsed').toggleClassName('expanded'));
}
}
new Effect.BlindDown(ul.toggleClassName('collapsed').toggleClassName('expanded'));
}
}
);
Рабочий пример для второго варианта.
Всё очень просто!
Автор: Vladimir; опубликовано в: CSS, JavaScript; метки: CSS, JavaScript, Prototype, Scriptaculous, WCAG, XHTML, аккордеонАпр
2008
Комментарии к статье «Парочка аккордеонов» (22) »
Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.
Оставить комментарий к записи «Парочка аккордеонов»
गते गते पारगते पारसंगते बोधि स्वाहा
Меня зовут Владимир, я программист-фрилансер, специализирующийся на Web-программировании и програмировании под Linux.
По совместительству занимаюсь администрированием LAMP/LNMP-серверов и техническим переводом.


Забыл сказать: код (для любого из примера) должен находиться внутри функции, вызываемой либо по событию
onload, либо (более предпочтительный вариант) по событиюDOMContentLoaded.С использованием Prototype это будет выглядеть следующим образом:
или
[...] статье «парочка аккордеонов» я рассказывал, как сделать простой аккордеон с [...]
вариант второй «развернуто не более одного меню» можно развернуть все!!!
если дважды кликнуть по одному из меню, а затем кликать на соседнее. это лечится?
Лечится. Можно использовать, например, очереди эффектов
new Effect.BlindUp(c.toggleClassName('collapsed').toggleClassName('expanded'), { queue : { position: 'end', scope : 'accordion' } });и
new Effect.BlindDown(ul.toggleClassName('collapsed').toggleClassName('expanded'), { queue : { position: 'end', scope : 'accordion' } });Да, действительно с очередями эффектов данная проблема не наблюдается. И эффектней смотрится! Здорово! Владимир, Спасибо.
Привет! а можно этот код использовать под bbpress?
если да, то куда влеписть? в какой файл? =)
Заранее спасибо!
Можно.
Не знаю, зависит от темы.
тема стандартная)
«Какумею». =)
что у меня не работает первый вариант.
код такой же как в примере.
висит менюшка и все. ява поключилась, стили тоже. что проверить?
строки из сомментария Vladimir
Апрель 13, 2008 в 21:31 – тоже не забыл. но что с ними что без них – не работает.
Дайте, пожалуйста, ссылку на тестовую страницу.
Владимир, спасибо за варианты меню.Вроде как все работает)
ЗЫ вот чем интенсивнее изучаю что-то новое, тем больше чувствую себя ламером((
А вопрос можно? Сколько времени и сделанных проектов нужно для превращения из новичка в гуру?))
Терпение и труд всё перетрут.
Главное — желание учиться, опыт и профессионализм дело наживное. Я программированием в разных его ипостасях около 20 лет занимаюсь.
Владимир, спасибо за ваш «аккордеон», очень помог в решении проблем с ИЕ6, но есть недочет, который я прошу исправить. Данный скрипт не раздает классы основному меню, т.е.
. В примере можно заметить, что красный элемент списка, таким и остается, а не передается другому активному элементу.Виталий, так и было задумано. Красный цвет — это признак того, что данное меню было активно при загрузке страницы. Как в навигации: жирным обычно выделяется текущая страница. Так и здесь.
Владимир, а не подскажете – куда копать чтобы по клику по первому уровню раскрывался список + открывалась целевая страница? Спасибо!
Евгений, тут будет проще вообще все на сервере сделать: для того, чтобы открылась ссылка, JavaScript, ясен пень, не нужен, а на стороне сервера смотреть, какая страница открывается и рисовать соответствующее подменю открытым.
Либо, если Вы вдруг делаете все через фреймы, добавить всем тэгам
.Добрый день! установил я Ваше меню, выглядит красиво, но есть проблема.
Когда я перехожу на страницу, аналог любого SubItem в примере, я не могу потом перейти на другой пункт меню, который не содержит подпунктов SubItem.
К примеру, есть у меня первая строка меню Главная, потом пошло само меню. И вот при клике на Главная у меня меню сворачивается, но переход не осуществляется.
я так понял оно принимает мою строку Главная аналогично другой строке Item, а как мне решишить эту проблемку?
пробовал делать в разных дивах, эффект тот же, оно как-то наследуется.
Артём, если можно — дайте ссылку на тестовую страницу, я посмотрю и скажу, что и где исправить.
Vladimir, спасибо, я решил свою проблемку! можете даже не публиковать мои комментарии! все равно спасибо и за меню, и за помощь!
сайт http://foodtek.com.ua, посмотрите, пожалуйста, и спасибо за оперативный ответ!
Например, так:
$$('#accordion > li[class="active"] ul').invoke('addClassName', 'expanded');
$$('#accordion > li > a').invoke('observe', 'click',
function(e)
{
var a = e.findElement('a');
if (a.readAttribute('href') != '#') {
e.stop();
}
var ul = a.up('li').down('ul');
if (ul) {
if (ul.hasClassName('collapsed')) {
var c = $$('#accordion ul:not([class="collapsed"])')[0];
if (c) {
new Effect.BlindUp(c.toggleClassName('collapsed').toggleClassName('expanded'));
}
}
new Effect.BlindDown(ul.toggleClassName('collapsed').toggleClassName('expanded'));
}
}
);
Если у элемента
aатрибутhrefустановлен в#, элемент будет открываться/закрываться. Еслиhrefкакой-либо другой, будет осуществляться переход.Спасибо большое за простое и удобное меню!