Простой аккордеон
30 Марта, 2009Это те самые, странно разворачивающиеся и очень редко нужные менюшки. ;)
Так как jquery стал почти стандартом в индустрии, дальше я буду говорить только о нем.
Проблема
На сайте понадобился такой элемент. Выглядят они обычно как-то так. Я буду считать что один из его блоков должен быть всегда открыт, но на деле, это не очень важно.
Ну и предполагаем, что по невероятному совпадению на сайте уже есть jquery. Потому что подключать его только ради меню глупо, согласитесь. Но, если очень хочется, то, как всегда, можно.
HTML — обычный dl, в котором dt будут кликаться, а dd раскрываться, вот такой:
- <dl>
- <dt class="b-acc">Заголовок</dt>
- <dd>
- Контент
- </dd>
- …
И все видимые блоки имеют фиксированную высоту 150px.
#1: Своими силами
Сделать такой эффект своими силами вроде бы совсем несложно. Всего-то нужно схлопывать-расхлопывать два блока одновременно.
- $(“.b-acc”).click(function(){
- if($(this).hasClass(‘b-acc-active’)) {
- // сделать что-то если блок уже открыт
- }
- else{
- $(“dd:visible”).slideUp();// закрыть «видимый» блок
- $(this).next().slideDown();// открыть новый блок
- // и переключить класс b-acc-active
- …
- }
- return;
- });
Можно посмотреть в работе. На первый взгляд все отлично.
Однако, если присмотреться, во время анимаций нижняя граница аккордеона скачет. В некоторых проектах на это и можно закрыть глаза, но когда под аккордеоном куча контента, который будет прыгать на 1-2 пикселя, это очень неприятно.
Пришлось искать решение.
#2: Плагин
Это будет первое что вы нагуглите. Проще всего его подключить и вызвать. И если всем плевать на размер файла, а также на то, что вы подключили кучу мусора (разве что ваш сайт будет увешан всеми видами аккордеонов). На этом можно и закончить уже разработку.
Я сделал простой пример работы плагина. Нижняя кромка больше не прыгает.
Однако добавилось 8 Кбайтов плагина.
#3: Улучшенный вариант
Но так хотелось все оставить простым, так не хотелось подключать кучу лишнего кода, который, я знал, никому на сайте уже не понадобится. И я решил раскопать проблему, почему же нижняя граница дергается, и как это все исправить.
В конце концов оказалось, что функции slideUp и slideDown меняют высоту блоков не по целым значениям. То есть, в процессе анимации высоте блока присваиваются значения вроде 2.157305232px. При этом, округление в браузерах работает очень по-разному, что и приводило к «вибрации» нижней кромки аккордеона.
Наконец забравшись в недра плагина, я нашел решение, которое также познакомило меня с малоизвестными возможностями jquery.
Идея примерно такая: мы начинаем «схлопывать» видимый блок, при этом, на каждом шаге анимации, рассчитывать высоту расхлопываемого блока так, чтобы сумма их оставалась константой. То есть, если на каком-то шаге высота закрываемого 50 пикселей, то высота открываемого будет 100 (в сумме 150px).
Для этого в jquery есть специальный механизм. В функции animate, есть возможность исполнять что-то на каждом шаге анимации. Называется этот коллбэк — step.
Финальное работающее решение выглядит примерно так:
- toShow = $(this).next();
- toHide = $(“.accordion dd:visible”);
- staticheight = toHide.height(); //определить высоту блоков, 150px
- toShow.css({ height: 0,display: ‘block’ }); // открываемый делаем “видимым” с высотой 0, теперь он готов к анимации
- toHide.animate({height:”hide”},{ // начинаем скрывать видимый
- step: function(now) { // на каждом шаге выполняем, now – значение height в каждый момент
- var current = staticheight – now; // собственно рассчет
- if ($.browser.msie || $.browser.opera || $.browser.safari) { // все округляют по-своему
- current = Math.ceil(current);
- }
- toShow.height(current); // присваиваем высоту
- },
- duration: 500
- });
Работающий пример. Вуаля, нижняя граница опять зафиксирована, и мы сэкономили 7 Кбайт несжатого кода.
Понятно что конкретно этот аккордеон не универсален, но зная как работает step можно легко применить его к любому другому способу.
В конце
Все это далеко не rocket science, но мои долгие попытки найти легкое решение этой проблемы приводили только к плагину. Который я, ну никак не хотел подключать. Потому надеюсь кому-то это сэкономит усилия и код. И буду рад услышать ваше мнение об этом.
Разумеется, только ради аккордеона не стоит подключать jquery, но, обычно, сайты с такими навороченными меню содержат еще кучу всяких анимаций и действий. И потому, в результате, все равно удобнее использовать фреймворк для разработки.
всегда удивлялся, почему именно jquery?
» cr_: всегда удивлялся, почему именно jquery?
да хотя бы потому, что он активно развивается, а также поддерживается огромным коммьюнити.
Хорошо. :) Только у меня какие-то глюки в FF3/Win со всеми вариантами выскакивают – рисуются странные синие полоски в некоторых случаях и пропадают при перерисовке окна браузера (например при ресайзе).
Нашел ошибочку.
Попробуйте быстро покликать по менюшке.
Нажмите на один пунк меню, и пока происходить эффект слайдинга тут же нажмите на другой пункт меню. Попробуйте проделать так несколько раз.
Для меня jquery, потому что когда стали появляться фреймворки, о нем мне первом рассказали. Да и такой понятный синтаксис, а это всегда приятно, взять и сразу что-то делать. Примерно как с HTML.
@ScorpAL да, в живом скрипте я еще делал защиту, чтобы во время анимации события не обрабатывались, но тут решил не усложнять. Я в целом хотел рассказать именно про
step, про который нигде так и не нашел документации (но где-то по слухам есть)@Mourner – дадада, когда сделал фон для разворачиваемых пунктов, мой фф/mac тоже стал глючно прорисовывать :) Но я решил оставить, всегда забавно видеть столь редкие баги в ФФ =)
В примере> в FF 3.0.8 при переключении пунктов на блоках остаются артефакты…
Убрал оттуда фоновую картинку, чтобы артефакты никого не смущали, но надо бы отсабмитить им багрепорт по этому поводу.
Ну и противное название «аккордеон».
Спасибо! Недавно сам искал похожее…
Если jQerry не используется, а аккордеон нужен поможет вот этот замечательный js: http://www.dezinerfolio.com/2007/07/19/simple-javascript-accordions
1kb кода)
Однако ты азартен, Akella. Этож надо в наше время 7 килобайт экономить. Школа!
Абсолютно во всех примерах в Safari (mac) скачет низ. Для последнего работающего примера это можно увидеть, нажав сразу же на пункт «Подписаться». Safari 3.2.1.
Скачет, но по-разному, в одном варианте сильно и все время, в другом чуть-чуть и иногда ;) я считаю это выгодной сделкой с браузером у которого пара процентов.
хорошая штучка.. вот только реально при быстром кликанье в итоге все закроется навсегда…
совсем недавно реализовал свой слайдер.. правда в моем случае все слайдеры работают независимо друг от друга, а сделано это на лысом javascript без всяких фреймворков и занимает всего 2.1 кб.. но получилось довольно универсально..
При начале новой анимации нужно останавливать все предыдущие.
http://docs.jquery.com/Effects/stop.
Спасибо!
А еще лучше имхо создать флаг, чтобы никакая анимация не могла начаться в процессе другой.
Так сделает нормальный вариант или стоит пользоваться плагином? Я ноль в JavaScript, поэтому стоп не могу сделать..
последний работающий линк, не работает.
Когда закрываются все пункты, открыть их потом не получается
вот так
http://floomby.ru/content/dZyVUFVfFE/
Пример из пункта 3 почти хорош, только если быстро нажимать он схлопывается и уже не разворачивается.
Akella, а высоту контейнерам обязательно задавать принудительно или возможно не задавать?
Высота должна быть одинаковая у всех блоков, иначе ж будет двигаться нижняя кромка. Можно просто джаваскриптом считать максимальную из всех будущих расхлопывающихся блоков и задавать её всем остальным
А как же чтобы со ссылками.
кошерный вариант
http://moofx.mad4milk.net/
эмм, а в чем его кошерность? там вроде обычный банальный вариант, и отделения там разной высоты…
?
Прикольно, но:
пример с дегающимся нижним краем работает чисто (ну кроме дергающегося края).
А вот готовый вариант немного бажит – если кликать быстро, то с каждым кликом гармошка становится все меньше и меньше пока почти совсем не сойдет на нет.
Конечно этим можно пренебречь – вряд ли кто-то будет с такой скоростью кликать по меню… Но все же…
А так возможность интерестная.
Главное чтобы индексировались поисковиками такие вот менюшки. Полезная штука когда в меню очень много категорий и вертикальное нужно прокручивать. Хотя судя по кода проблем быть не должно.
А как сделать чтобы они были свернуты первоночально?
А подскажите плиз, что означает это выражение: toShow = $(this).next();
toShow – это функция или переменная? Как растолковать?
Оставить комментарий