Парочка аккордеонов

Несколько вариантов реализации меню-аккордеона с использованием библиотеки Prototype

Недавно в одном из проектов появилась необходимость использовать (подобный используемому на Desert Ridge Marketplace). Любят заказчики дешевые эффекты, ничего здесь не поделаешь :-)

Чем мне не понравился аккордеон на Desert Ridge — это полной зависимостью от : если выключен, навигация по сайту переставала работать. Это мне не понравилось, и я решил написать свой.

Представляю два решения:

  1. Подменю сворачивается/разворачивается по щелчку, элементы меню не зависят друг от друга;
  2. Подменю сворачивается/разворачивается по щелчку, при этом не может быть более одного развёрнутого подменю.

При выключенном JavaScript оба аккордеона трансформируются в двухуровневый список.

Поехали. Будем использовать следующую разметку:

[-]
View Code HTML
    <ul id="accordion">
        <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 активно (то есть должно быть развёрнуто).

Оформим аккордеон при помощи :

[-]
View Code CSS
a { text-decoration: none; color: #00F; }
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:

[-]
View Code CSS
* html #accordion li {
    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. В данных примерах я использую библиотеки и Effects (всё очень легко переводится на jQuery).
Вариант 1: независимые элементы меню

[-]
View Code Javascript
$$('#accordion > li:not([class="active"]) ul').invoke('setStyle', { display : 'none' }).invoke('addClassName', 'collapsed');
$$('#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: развёрнуто не более одного меню

[-]
View Code Javascript
$$('#accordion > li:not([class="active"]) ul').invoke('setStyle', { display : 'none' }).invoke('addClassName', 'collapsed');
$$('#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'));
        }
    }
);

Рабочий пример для второго варианта.

Всё очень просто!

Автор: ; опубликовано в: CSS, JavaScript; метки: CSS, JavaScript, Prototype, Scriptaculous, WCAG, XHTML, аккордеон
13
Апр
2008

RSS Комментарии к статье «Парочка аккордеонов» (22)  »

  1. Забыл сказать: код (для любого из примера) должен находиться внутри функции, вызываемой либо по событию onload, либо (более предпочтительный вариант) по событию DOMContentLoaded.

    С использованием Prototype это будет выглядеть следующим образом:

    [-]
    View Code Javascript
    Event.observe(window, 'load', function() { /* Code goes here */ });

    или

    [-]
    View Code Javascript
    document.observe('dom:loaded', function() { /* Code goes here */ });
  2. [...] статье «парочка аккордеонов» я рассказывал, как сделать простой аккордеон с [...]

  3. cadet

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

    • Лечится. Можно использовать, например, очереди эффектов

      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' } });

  4. Роман

    что у меня не работает первый вариант.
    код такой же как в примере.

    висит менюшка и все. ява поключилась, стили тоже. что проверить?

    строки из сомментария Vladimir
    Апрель 13, 2008 в 21:31 – тоже не забыл. но что с ними что без них – не работает.

  5. bonnadea

    Владимир, спасибо за варианты меню.Вроде как все работает)

    ЗЫ вот чем интенсивнее изучаю что-то новое, тем больше чувствую себя ламером((

    А вопрос можно? Сколько времени и сделанных проектов нужно для превращения из новичка в гуру?))

    • Терпение и труд всё перетрут.

      Главное — желание учиться, опыт и профессионализм дело наживное. Я программированием в разных его ипостасях около 20 лет занимаюсь.

  6. Виталий

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

    • Виталий, так и было задумано. Красный цвет — это признак того, что данное меню было активно при загрузке страницы. Как в навигации: жирным обычно выделяется текущая страница. Так и здесь.

  7. Евгений

    Владимир, а не подскажете – куда копать чтобы по клику по первому уровню раскрывался список + открывалась целевая страница? Спасибо!

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

      Либо, если Вы вдруг делаете все через фреймы, добавить всем тэгам .

  8. Artem

    Добрый день! установил я Ваше меню, выглядит красиво, но есть проблема.
    Когда я перехожу на страницу, аналог любого SubItem в примере, я не могу потом перейти на другой пункт меню, который не содержит подпунктов SubItem.
    К примеру, есть у меня первая строка меню Главная, потом пошло само меню. И вот при клике на Главная у меня меню сворачивается, но переход не осуществляется.
    я так понял оно принимает мою строку Главная аналогично другой строке Item, а как мне решишить эту проблемку?
    пробовал делать в разных дивах, эффект тот же, оно как-то наследуется.

  9. сайт http://foodtek.com.ua, посмотрите, пожалуйста, и спасибо за оперативный ответ!

    • Например, так:

      [-]
      View Code Javascript
      $$('#accordion > li:not([class="active"]) ul').invoke('setStyle', { display : 'none' }).invoke('addClassName', 'collapsed');
      $$('#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 какой-либо другой, будет осуществляться переход.

  10. Виктория

    Спасибо большое за простое и удобное меню!

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

Оставить комментарий к записи «Парочка аккордеонов»

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя

गते गते पारगते पारसंगते बोधि स्वाहा