Задача
Сделать такой вот список. Несколько скрытых пунктов. Жмём на заголовок — текст раскрывается, жмём ещё раз — закрывается.
Усложняем задачу:
1. Тексты должны не просто открываться, а плавно раскрываться.
2. Если нажать на заголовок в том момент, когда блок раскрывается, он должен начать с той же позиции закрываться.
3. Еще быстро нащёлкать несколько заголовков, блоки должны открываться/закрываться параллельно, не мешая друг другу и уж тем более работе с другими элементами.
Решение в лоб
<li> <a href="#" onclick="aclick('div25');return false;">Заголовок пункта</a> <div id="div25">Текст пункта</div> </li> |
Каждому блоку вешаем уникальный ID. В ссылке прописываем обработчик, который вызывает функцию (в нашем случае aclick), передавая ей ID связанного с этой ссылкой блока. Обработчик же занимается раскрыванием/скрыванием полученного в аргументе блока.
Минусы данного решения
Первый минус — много мусора в HTML.
Второй минус более существенен. Здесь мы используем процедурное программирование со всеми его недостатками.
В чём главный недостаток процедурного программирования? В том, что данные не связаны с обрабатывающим их кодом. Вот и у нас здесь куча ссылок, куча блоков и одна функция которая их всех разруливает.
Каждому немного знающему JS очевидно, что блок нельзя раскрыть простым циклом с задержками, типа следующего:
function aclick(divid) { var div = document.getElementById(divid); div.style.height = "0"; for (var i = 10; i < 500; i += 10) { usleep(100); div.style.height = i + "px"; } } |
При таком цикле браузер просто полностью повиснет на несколько секунд. Причём в большинстве браузеров блок отрисуется только целиком в конце.
Поэтому, хотя нам этого явно не хотелось, придётся использовать таймеры и потихоньку открывать блок.
И вот у нас получается несколько параллельно идущих процессов со связанными с ними данными (таймер, состояние и т.п). И все их разруливает одна единственная функция. Несчастная функция.
Наиболее частое решение — использовать массивы значений.
var divs = {}; var timers = {}; function aclick(divid) { divs[divid] = document.getElementById(divid); ... timers[divid] = setInterval(...); ... } |
Массивы хранятся в глобальных переменных. Их нужно вовремя инициализовывать, сбрасывать и т.д. и т.п.
Вобщем, вместо удовольствия от любимой работы, получаем в крайнем случае удовольствие анальное.
Ещё вариант хранить данные привязанные к блоку в свойствах DOM-объекта этого блока. Уже лучше, но заморочек по прежнему хватает.
Делаем по другому
Не пишем вообще ничего лишнего в HTML, а ниже списка вставляем сценарий.
Что здесь делаем? Перебираем все элементы списка и содержащиеся в них пары ссылка-блок. Для каждой пары вызываем функцию linkAD(). Функция linkAD() связывает ссылку и блок (по нажатии ссылки открывается блок).
Что здесь нового? То что связывая ссылку и блок внутри linkAD() мы имеем дело с одной ссылкой и одним блоком. И нам абсолютно при этом не интересно сколько ещё таких блоков существует, сколько из них будут параллельно раскрываться с нашим текущим блоком и прочая и прочая…
Вызывая для каждой пары функцию мы каждый раз создаём отдельный контекст. Отдельный контекст со своими отдельными данными (timer, height, dh, cheight, a, div) привязанными только к нашему конкретному блоку.
Вложенные функции onclick() и clock() через замыкание связаны с этим контекстом. И внутри обработчика мне не нужно думать, а тот ли это timer, который относится к моему блоку и та ли это height, которая содержит высоту моего блока.
То есть мы произвели инкапсуляцию данных относящихся к одной сущности в одном месте. Маленький пример для тех, кто не понимает почему замыкания считаются аналогом ООП.
Выводы
Вывод раз: глобальные функции и переменные в сложных случаях и при количестве обрабатываемых процессов больше одного, это писец кромешный. Пользуйтесь объектами или замыканиями чтобы объединять данные с обрабатывающим их кодом.
Вывод два: не перебарщивайте в простых случаях. В данной статье исходный код раскрывается через обычный onclick в HTML и я нисколько не комплексую по этому поводу.
P.S.
Это статья про сами принципы программирования. То, что плавно раскрывать списки в каком-нибудь jQuery можно в две строчки к теме отношения не имеет.
Согласен по этому поводу, сам на горьком опыте в этом убедился…
adw0rd, 20.04.2009, 14:20
Поделись им :)
vasa_c, 20.04.2009, 14:36
Ну а что тут делится… каждый раз надо было менять кучу онкликов и код засорялся :(
adw0rd, 20.04.2009, 14:40
Да вот пробую как вы написали. без сглаженого выезжания
Zero, 26.10.2009, 10:55
Полезно и грамотно- спасибо!
Саша, 26.08.2010, 23:20
Сейчас попробую у себя на сайте :)
Витя, 9.07.2012, 13:56