Простой аккордеон

30 Марта, 2009

Это те самые, странно разворачивающиеся и очень редко нужные менюшки. ;)
Так как jquery стал почти стандартом в индустрии, дальше я буду говорить только о нем.

Проблема

На сайте понадобился такой элемент. Выглядят они обычно как-то так. Я буду считать что один из его блоков должен быть всегда открыт, но на деле, это не очень важно.
Ну и предполагаем, что по невероятному совпадению на сайте уже есть jquery. Потому что подключать его только ради меню глупо, согласитесь. Но, если очень хочется, то, как всегда, можно.
HTML — обычный dl, в котором dt будут кликаться, а dd раскрываться, вот такой:

  1. <dl>
  2. <dt class="b-acc">Заголовок</dt>
  3. <dd>
  4. Контент
  5. </dd>

И все видимые блоки имеют фиксированную высоту 150px.

#1: Своими силами

Сделать такой эффект своими силами вроде бы совсем несложно. Всего-то нужно схлопывать-расхлопывать два блока одновременно.

  1. $(“.b-acc”).click(function(){
  2. if($(this).hasClass(‘b-acc-active’)) {
  3. // сделать что-то если блок уже открыт
  4. }
  5. else{
  6. $(“dd:visible”).slideUp();// закрыть «видимый» блок
  7. $(this).next().slideDown();// открыть новый блок
  8. // и переключить класс b-acc-active
  9. }
  10. return;
  11. });

Можно посмотреть в работе. На первый взгляд все отлично.
Однако, если присмотреться, во время анимаций нижняя граница аккордеона скачет. В некоторых проектах на это и можно закрыть глаза, но когда под аккордеоном куча контента, который будет прыгать на 1-2 пикселя, это очень неприятно.
Пришлось искать решение.

#2: Плагин

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

Я сделал простой пример работы плагина. Нижняя кромка больше не прыгает.
Однако добавилось 8 Кбайтов плагина.

#3: Улучшенный вариант

Но так хотелось все оставить простым, так не хотелось подключать кучу лишнего кода, который, я знал, никому на сайте уже не понадобится. И я решил раскопать проблему, почему же нижняя граница дергается, и как это все исправить.
В конце концов оказалось, что функции slideUp и slideDown меняют высоту блоков не по целым значениям. То есть, в процессе анимации высоте блока присваиваются значения вроде 2.157305232px. При этом, округление в браузерах работает очень по-разному, что и приводило к «вибрации» нижней кромки аккордеона.

Наконец забравшись в недра плагина, я нашел решение, которое также познакомило меня с малоизвестными возможностями jquery.
Идея примерно такая: мы начинаем «схлопывать» видимый блок, при этом, на каждом шаге анимации, рассчитывать высоту расхлопываемого блока так, чтобы сумма их оставалась константой. То есть, если на каком-то шаге высота закрываемого 50 пикселей, то высота открываемого будет 100 (в сумме 150px).

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

  1. toShow = $(this).next();
  2. toHide = $(“.accordion dd:visible”);
  3.  
  4. staticheight = toHide.height(); //определить высоту блоков, 150px
  5. toShow.css({ height: 0,display: ‘block’ }); // открываемый делаем “видимым” с высотой 0, теперь он готов к анимации
  6. toHide.animate({height:”hide”},{ // начинаем скрывать видимый
  7. step: function(now) { // на каждом шаге выполняем, now – значение height в каждый момент
  8. var current = staticheight – now; // собственно рассчет
  9. if ($.browser.msie || $.browser.opera || $.browser.safari) { // все округляют по-своему
  10. current = Math.ceil(current);
  11. }
  12. toShow.height(current); // присваиваем высоту
  13. },
  14. duration: 500
  15. });

Работающий пример. Вуаля, нижняя граница опять зафиксирована, и мы сэкономили 7 Кбайт несжатого кода.

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

В конце

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

Разумеется, только ради аккордеона не стоит подключать jquery, но, обычно, сайты с такими навороченными меню содержат еще кучу всяких анимаций и действий. И потому, в результате, все равно удобнее использовать фреймворк для разработки.

Ссылки

XHTML/CSS, Полезности

29 комментариев к “Простой аккордеон”

1. Марта 30th, 2009 | cr_

всегда удивлялся, почему именно jquery?

2. Марта 30th, 2009 | DiS

» cr_: всегда удивлялся, почему именно jquery?

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

3. Марта 30th, 2009 | Mourner

Хорошо. :) Только у меня какие-то глюки в FF3/Win со всеми вариантами выскакивают – рисуются странные синие полоски в некоторых случаях и пропадают при перерисовке окна браузера (например при ресайзе).

4. Марта 30th, 2009 | ScorpAL

Нашел ошибочку.

Попробуйте быстро покликать по менюшке.

Нажмите на один пунк меню, и пока происходить эффект слайдинга тут же нажмите на другой пункт меню. Попробуйте проделать так несколько раз.

5. Марта 30th, 2009 | akella

Для меня jquery, потому что когда стали появляться фреймворки, о нем мне первом рассказали. Да и такой понятный синтаксис, а это всегда приятно, взять и сразу что-то делать. Примерно как с HTML.

@ScorpAL да, в живом скрипте я еще делал защиту, чтобы во время анимации события не обрабатывались, но тут решил не усложнять. Я в целом хотел рассказать именно про step, про который нигде так и не нашел документации (но где-то по слухам есть)

@Mourner – дадада, когда сделал фон для разворачиваемых пунктов, мой фф/mac тоже стал глючно прорисовывать :) Но я решил оставить, всегда забавно видеть столь редкие баги в ФФ =)

6. Марта 30th, 2009 | Арнольд

В примере> в FF 3.0.8 при переключении пунктов на блоках остаются артефакты…

7. Марта 30th, 2009 | akella

Убрал оттуда фоновую картинку, чтобы артефакты никого не смущали, но надо бы отсабмитить им багрепорт по этому поводу.

8. Марта 30th, 2009 | Genn

Ну и противное название «аккордеон».

9. Марта 31st, 2009 | nice

Спасибо! Недавно сам искал похожее…

10. Марта 31st, 2009 | Del'ka

Если jQerry не используется, а аккордеон нужен поможет вот этот замечательный js: http://www.dezinerfolio.com/2007/07/19/simple-javascript-accordions
1kb кода)

11. Апреля 3rd, 2009 | птюч

Однако ты азартен, Akella. Этож надо в наше время 7 килобайт экономить. Школа!

12. Апреля 3rd, 2009 | Oracle

Абсолютно во всех примерах в Safari (mac) скачет низ. Для последнего работающего примера это можно увидеть, нажав сразу же на пункт «Подписаться». Safari 3.2.1.

13. Апреля 3rd, 2009 | akella

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

14. Апреля 6th, 2009 | Razor

хорошая штучка.. вот только реально при быстром кликанье в итоге все закроется навсегда…
совсем недавно реализовал свой слайдер.. правда в моем случае все слайдеры работают независимо друг от друга, а сделано это на лысом javascript без всяких фреймворков и занимает всего 2.1 кб.. но получилось довольно универсально..

15. Апреля 8th, 2009 | Parkim

При начале новой анимации нужно останавливать все предыдущие.
http://docs.jquery.com/Effects/stop.

16. Апреля 10th, 2009 | akella

Спасибо!
А еще лучше имхо создать флаг, чтобы никакая анимация не могла начаться в процессе другой.

17. Апреля 10th, 2009 | WebMast

Так сделает нормальный вариант или стоит пользоваться плагином? Я ноль в JavaScript, поэтому стоп не могу сделать..

18. Апреля 17th, 2009 | delaf

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

19. Апреля 17th, 2009 | delaf

вот так
http://floomby.ru/content/dZyVUFVfFE/

20. Апреля 24th, 2009 | homm

Пример из пункта 3 почти хорош, только если быстро нажимать он схлопывается и уже не разворачивается.

21. Апреля 28th, 2009 | cinic

Akella, а высоту контейнерам обязательно задавать принудительно или возможно не задавать?

22. Апреля 28th, 2009 | akella

Высота должна быть одинаковая у всех блоков, иначе ж будет двигаться нижняя кромка. Можно просто джаваскриптом считать максимальную из всех будущих расхлопывающихся блоков и задавать её всем остальным

23. Августа 11th, 2009 | cuba

А как же чтобы со ссылками.

24. Августа 16th, 2009 | barbuza

кошерный вариант
http://moofx.mad4milk.net/

25. Августа 18th, 2009 | akella

эмм, а в чем его кошерность? там вроде обычный банальный вариант, и отделения там разной высоты…
?

26. Октября 2nd, 2009 | MadDorris

Прикольно, но:

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

А так возможность интерестная.

27. Апреля 17th, 2010 | Алматинский полубомж

Главное чтобы индексировались поисковиками такие вот менюшки. Полезная штука когда в меню очень много категорий и вертикальное нужно прокручивать. Хотя судя по кода проблем быть не должно.

28. Сентября 22nd, 2010 | Вдюпель

А как сделать чтобы они были свернуты первоночально?

29. Декабря 7th, 2011 | Дмитрий

А подскажите плиз, что означает это выражение: toShow = $(this).next();
toShow – это функция или переменная? Как растолковать?

Оставить комментарий