jQuery

индекс
283,92

Пишем аккордеон-плагин в 618 байт

Очень часто приходится видеть варианты элемента управления «аккордеон» на различных сайтах. В этой заметке я хотел бы предложить свой вариант, который кроме того, что обладает некоторыми оригинальными свойствами, еще и весит в minified-виде всего 618 байт. Заодно, я покажу как быстро написать простейший плагин для jQuery.

Забегая в перед скажу, что плагин тестировался в Firefox 3.0.3, Internet Explorer 7 и 8b2, Opera 9.52 и Chrome 0.3.154.9. Во всех других браузерах работоспособность гарантируется настолько насколько в них работает jQuery.

Для любопытных приведу пример того, что будет в итоге (ссылки и кнопки в примере не работают).

Постановка задачи


Создать плагин для jQuery, минимальный по объему, который бы на базе html-элемента «dl» строил элемент управления «аккордеон». Обязательным условием является вывод пояснения для неактивных элементов «аккордеона». Активный элемент аккордеона определяется по наличию класса «active» у соответствующего элемента dt. dt так же содержит заголовок элемента «аккордеона». Элемент пояснения span должен иметь класс «remark» и находится в dd. Основное тело каждого элемента «аккордеона» должно содержаться в div, который также должен быть в dd.

Решение


Для начала определим функцию которая обновляет состояние элемента dl и приводит его к требуемому виду:
  function Update(dl) {
    $("#" + dl.id + " > dt:not(.active)").each(function() {
      $(this).css("cursor", "pointer");
    });
    $("#" + dl.id + " > dt.active").each(function() {
      $(this).css("cursor", "");
    });

    $("#" + dl.id + " > dd > div").hide();
    $("#" + dl.id + " > dd > span.remark").show();

    $("#" + dl.id + " > dt.active").next().children("div").show(300);
    $("#" + dl.id + " > dt.active").next().children("span.remark").hide();
  }


* This source code was highlighted with Source Code Highlighter.


Первые две операции обновляют вид указателей мыши при наведении на активный и неактивные элементы «аккордеона». Вторые две операции скрывают все содержимое «аккордеона» и показывают все примечания. Третьи две операции показывают с замедлением содержимое активной вкладки «аккордеона» и скрывают его примечание.

Далее напишем функционал, который будет выполняться в момент нажатия на вкладки «аккордеона»:
    $("#" + this.id + " > dt").click(function() {
      if ($(this).hasClass("active"))
        return; // выход если щелкнули по активному dt

      $("#" + dl.id + " > dt").removeClass("active");
      $(this).addClass("active");

      Update(dl);
    });


* This source code was highlighted with Source Code Highlighter.


Это «вырванный» кусок кода. Здесь $("#" + this.id + " > dt") — это выборка всех вкладок нашего «аккордеона». Средствами jQuery устанавливается обработчик нажатия на вкладку, где в самом начале идет проверка куда кликнули, если на пассивный элемент, то производится выход. Далее, у всех вкладок удаляется класс «acitve», в кликнутой, наоборот, присваивается.

Сведем все вместе и напишем плагин к jQuery:
jQuery.fn.accordion = function() {

  function Update(dl) {
    $("#" + dl.id + " > dt:not(.active)").each(function() {
      $(this).css("cursor", "pointer");
    });
    $("#" + dl.id + " > dt.active").each(function() {
      $(this).css("cursor", "");
    });

    $("#" + dl.id + " > dd > div").hide();
    $("#" + dl.id + " > dd > span.remark").show();

    $("#" + dl.id + " > dt.active").next().children("div").show(300);
    $("#" + dl.id + " > dt.active").next().children("span.remark").hide();
  }

  return this.each(function() {
    var dl = $(this)[0];

    Update(dl);

    $("#" + this.id + " > dt").click(function() {
      if ($(this).hasClass("active"))
        return; // выход если щелкнули по активному dt

      $("#" + dl.id + " > dt").removeClass("active");
      $(this).addClass("active");

      Update(dl);
    });

  });

};


* This source code was highlighted with Source Code Highlighter.


Мы расширяем jQuery нашей функцией «accordion», в которой для каждого заданного элемента произвдятся следующие шаги: получается экземпляр dl, обновляется состояние полученного dl, устанавливаются обработчики нажатия мышью для всех dt полученного dl.

Использование


Использовать написанный элемент крайне просто. Для этого просто включите в код следующий блок, который сделает из переданного элемента dl наш «аккордеон»:
  <script type="text/javascript">
    $(document).ready
    (
      function() {
        $("#LoginList").accordion();
      }
    );
  </script>  


* This source code was highlighted with Source Code Highlighter.


Заключение


Представленный вариант — самый простейший, он не претендует ни на что кроме примера. Его можно улучшить добавив параметры, которые влияли бы на скорость отображения активного элемента, на стартовый номер вкладки «аккордеона». Кроме того, уверен, что можно улучшить и сам javascript-код.

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

Ссылка на плагин (1192 байта), minified-версия (618 байт).

UPD: благодаря оптимизации DmitryBaranovskiy размер minified-версии уменьшен до 508 байт, полной версии до 909 байт

_________
Текст подготовлен в ХабраРедакторе
+48
5 ноября 2008, 08:23
150

комментарии (41)

–8
soltpain #
Вы уж извините, но зачем писать плагин в полкило к куче кода ~50Кб? Наболело. Предложите лучше аккордеон в чистом виде…
+10
XaocCPS #
к какой куче?
посмотрите через firebug, сколько весит ВСЯ страница с примером: 25кб вместе с картинками, html, css и двумя плагинами

иногда лучше промолчать
+7
soltpain #
Виноват, на страницу с демо не заходил, файлы не мерил. 50кб упомянуто как типичный размер jquery-файла встречаемого мною в сторонних проекта. Для того чтобы потом по клику взять и спрятать/показать див. Наболело, потому что устал смотреть ЯС-код так называемых JQuery-kiddies. Подключат библиотеку, нагородят быдлокод такой, что диву даешься… Сядешь, перепишешь на чистый ЯС — даже меньше получается.

За свой предыдущий пост прошу прощения, выстрелило, к вам это не имеет прямого отношения.
+6
XaocCPS #
ничего страшного
дело в том, что мы находимся в блоге jQuery и просить написать не jQuery «аккордеон» тут — по меньшей мере странно :)

а быдлокод — он языконезависим, имхо, и тут jQuery скорее средство, нежели причина
+7
Riz #
А вот стоит ли экономить эти килобайты? jQuery прекрасная библиотека, в первую очередь ценная своим уровнем абстракции от браузера. Подключил один файл и не нужно вспоминать как заставить работат AJAX в IE, или где как обзываются размеры блоков. Даже если эти 30-50кб служат только для того, чтобы скрыть\показать элемент на странице, ну и пусть. Ведь потом «внезапно» потребуется сделать это сокрытие с анимацией, или загрузить на этой-же странице что-то через AJAX.
+1
soltpain #
С Вами трудно не согласиться, все фреймворки подобного рода популярны именно из-за непохожести браузеров. Но моя личная паранойя — не использовать чужого. Либо возьми, раскрути, пойми как что делается и напиши свое. Но на последнее все меньше и меньше времени остается, так что и я когда-нибудь примкну в какой-нибудь стан пользователей фреймворков ((-8
–2
PSHKGRZN #
Тогда почему вы используете для своих нужд HTML язык? Не вы же его придумали!
+1
konfuze #
Круто и, безусловно, очень увлекательно писать на голом js когда у тебя куча времени. Но зачастую в процессе активной конвейерной разработки сайтов некогда этим заниматься.

Вот тогда фреймворки (js, css, php etc) и приходят на помощь.
+2
Riz #
Не использовать чужого = писать велосипеды. Это здорово, пока не приходится работать быстро и много. Как по мне — лучше пусть за меня пишут, отлаживают и тестируют другие люди. Я поработаю, а потом с радостью часть денюжки скину в качестве благоларности.
+2
ayavryk #
Использовать jquery, если не накручено пару десятков мулечек не имеет смысла. А если пара десятка мулечек есть, не имеет смысла без него обходиться. Имхо.

Хотя все идет к тому, что писать ui на голом js лет скоро уже никто не будет.
+1
PSHKGRZN #
Лично мой девиз по отношению к размерам страниц: «Чем меньше, — тем быстрее!»
— И не стоит рассуждать по поводу стоило писать плагин или нет! Стоило!

Автор — спасибо! Только маленький советик: сделай пожалуйста комментарии к каждой важной строке кода чтоб новички могли разобраться!
0
XaocCPS #
вроде, постарался после кода расписать что да как…

если есть у кого вопросы, прошу задавать в комментах, имхо, код простой и для тех кто немного знает jQuery труда не составляет
+1
denver #
По юзабилити немного: аккордеон это когда что-то сужается, другое расширяется. А у вас вместо сужения скачок, так что теряешься «ой, куда вдруг делась предыдущая форма».
+1
XaocCPS #
сделал .hide(300);
лучше?
0
denver #
Пёрфект :)
0
deerua #
Я уж голову начал ломать, что такой за бойан вы хотите показать ;)
Спасибо за работу и идею ;)
0
XaocCPS #
в каком-то смысле, действительно, бойан :)
+2
pepelsbey #
А обязательно использовать ID'шники и писать CSS внутри JS?
Мне кажется, что по коллекции можно ходить и так.

И, кстати, особенность настоящего аккордеона состоит в том, что его общая высота не меняется — это соблюдено только на сайте Apple, но, к сожалению, при помощи фиксированной высоты.

www.apple.com/downloads/
www.livejournal.ru/

…попробую написать свою версию.
0
XaocCPS #
идешники использую для создания одного jQuery запроса, вместо последовательной выборки — не знаю, может быть это неправильно, но мне показалось, что так лучше

css внутри js? вы про курсор мыши? это сделано для того, чтобы курсор становился pointer всегда и без создания css файла специально для контрола. Хотя конечно, можно убрать и вынести в отдельный css-файл.

Про не изменяющуюся высоту не согласен, по моему странное требование, ничем не оправданное. Ну если нужно — сделайте.

PS: по вашей ссылке на ЖЖ высота меняется
+1
pepelsbey #
для создания одного jQuery запроса, вместо последовательной выборки

Мне кажется, что настоящий самурайский JS должен работать именно с пустым HTML-кодом, с привязкой только к корневому элементу + поддежкой в виде классов, которые описаны в CSS. Вот это будет настоящее разделение данных от представления и динамики.

Да, кстати — эффект инициализации (прыжка при загрузке) может нормально исправить описание в CSS, но только с обратной совместимостью.

document.documentElement.id = 'js';

#js DD { display:none; }
+3
DmitryBaranovskiy #
Присоединяюсь к Вадиму по поводу CSS и id. Крому того можно слегка оптимизировать код:
jQuery.fn.accordion = function() {
    function update(dl) {
        $("dt:not(.active)", dl).css("cursor", "pointer");
        $("dt.active", dl).css("cursor", "");

        $("dd div", dl).hide();
        $("dd span.remark", dl).show();

        $("dt.active", dl).next().children("div").show(300);
        $("dt.active", dl).next().children("span.remark").hide();
    }

    return this.each(function() {
        var dl = $(this), active = $("dt.active", dl);
        update(dl);

        $("dt", dl).click(function() {
            if (!$(this).hasClass("active")) {
                active && active.removeClass("active");
                active = $(this).addClass("active");
                update(dl);
            }
        });
    });
};


В данном случае вы не зависите от id плюс это выполняется чуть-чуть быстрее.
0
XaocCPS #
спасибо, отличная работа
0
XaocCPS #
благодаря вашим изменениям размер minified-версии уменьшился до 508 байт :)
кто меньше?
0
sil #
а как сделать так, что бы можно было бы выделять блоки, которые должны сворачиваться в зависимости от нажатия на элемент из этого блока, а элементы другого блока не изменяли своё состояние? :)
+2
amorphis #
А, если использовать slideUp() и slideDown() для пока и скрытия блоков, то на «аккордеон» больше похоже… Но, как говорится, на вкус и цвет… :-)

Примерно вот так будет выглядеть :-)

www.leonidknyazev.ru/_test/serialer.htm

0
XaocCPS #
хорошо, что в jQuery и в jQuery UI десяток, а то и больше анимаций
есть где поэкспериментировать всем и каждому :)
0
Joric #
Сначала упорно жал на «password recovery». Вы учтите на будущее, что ссылки, подчеркнутые пунктиром, не должны уводить со страницы.
0
XaocCPS #
сначала стоило прочитать заметку внимательно: "… ссылки и кнопки в примере не работают..."
+2
svistiboshka #
А как бы избавица от этого эффекта «инициализации»? (прыжка при загрузке)
0
Grady #
супер, вот можно ли еще экзампл работы данного кода добавить в статью??
0
XaocCPS #
а его там нет?
0
Grady #
ой, блин, сорри, после выходных тормажу......)) тогда просто спасиб
–1
EugeneDest #
у вас Понедельник и Вторник выходные O_o?
–1
Kolan #
Такой элемент управления обладает существенным недостатком. Он содержит невидимые функции. Что это такое рассказывает Джеф Раскин.
НЛО прилетело и опубликовало эту надпись здесь
0
sshz #
До такого вида можно легко доработать своими руками
0
xintrea #
Хорошо бы заголовок активного (раскрытого) элемента помещать на более другой фон, чем заголовки неактивных элементов. А то так как сейчас — сразу непонятно что происходит.
0
BonySoft #
небольшое замечание: в демке не прописан цвет фона страницы; у меня в браузере выставлен «серый» и на нем подсказки практически не читаются

реализация очень понравилась: красиво и лаконично
спасибо

… вариант анимации, предложенный хабраюзером amorphis мне тоже кажется более жизненным :)
0
silentroach #
мне кажется, лучше слайдом содержимое показывать.
и предыдущую открытую часть тоже закрывать плавно, а не рывком.
0
blaberus #
Технология офигенная, но надо продумать поведение — пока что мозги сворачиваются в трубочку после этих рывков. И у уважаемого amorphis вышло, увы, не лучше…

Анимация это классно, но… её надо к чему-то другому применить…

Друзья, не сердитесь, но если я куда-то щёлкнул — не надо передвигать это место! Всё должно появляться здесь же, в районе щелчка!
0
blaberus #
Похоже, можно попробовать сделать так:
При нажатии на заголовок свёрнуого блока, его разворачивать —
но НЕ сворачивать блоки, находящиеся сверху его. Тогда то, на что я щёлкнул, не будет выскакивать из-под мышки.

…Коллеги тут послушали меня и сказали, что я вообще против аккордеонов. Боюсь, что да, это неудобно.
Не согласны?

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