JS: Убираем onclick из html

Задача

Сделать такой вот список. Несколько скрытых пунктов. Жмём на заголовок — текст раскрывается, жмём ещё раз — закрывается.

Усложняем задачу:

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 можно в две строчки к теме отношения не имеет.

6 комментариев »

Leave a comment