Задача: написать как можно более универсальную функцию, которая, принимая многоуровневый хэш, смогла бы построить выпадающее меню любой вложенности
Чтобы было понятно о чем речь, вот пример многоуровневого выпадающего меню на JavaScript, код которого подробнее рассмотрен ниже.
Хэш с исходными данными
Задачу будем решать последовательно. Сначала определимся с форматом входных данных, а имеено: каким должен быть хэш? Наиболее простой и понятный синтаксис такой (JSON - JavaScript Objecj Notation):
// Многоуровнеый хэш c информацией о меню
// ******************
var menuArray = {
'1.html' : ["Мониторы", {
'5.html' : ["15 дюймов"],
'6.html' : ["17 дюймов"],
'7.html' : ["19 дюймов"],
'8.html' : ["21 и более дюймов"]
}],
'2.html' : ["Жесткие диски", {
'9.html' : ["40-80 Гб"],
'10.html' : ["120-160 Гб"],
'11.html' : ["180-250 Гб"],
'12.html' : ["300-500 Гб"],
'13.html' : ["более 500 Гб"]
}],
'3.html' : ["Процессоры", {
'proc=amd' : ["AMD", {
'14.html' : ["Athlon"],
'15.html' : ["Barton"],
'16.html' : ["Sempron"]
}],
'proc=intel' : ["Intel", {
'17.html' : ["Pentium"],
'18.html' : ["Celeron"]
}]
}],
'4.html' : ["Бонус-карта"]
}
Ключи хэша одновренно являются частью URL пунктов меню, что необычайно удобно. В качестве значений всегда выступает массив, первый элемент которого является строкой с именем пункта меню. Второй элемент в массиве присутствует только в случае, если есть вложение и представляет собой аналогичный хеш.
Такой объект очень легко сформировать на сервере, обработав результаты выборки из БД.
Функции и переменные для формирования меню
Для работы меню необходимы 2 глобальные переменные, использующиеся для того, чтобы убирать появляющиеся выпадушки с задержкой.
Функция drawJSMenu формирует HTML-код меню и передает его в элемент-контейнер. В качетсве аргументов принимаются id контейнера, вышерассмотренный хэш и путь для ссылок. Для задания имен классов, а также таймаута ожидания существуют константы в начале функции.
Функция hideJSMenu используется для скрытия выпадушек по таймауту.
// Глобальные переменные для работы функций
// ******************
var menuWaitTimer = {};
var menuOpenedCount = 0;
// ******************
// Функция формирования HTML-кода меню и вывода на страницу
// ******************
function drawJSMenu(containerId, hash, path) {
// ----------- Константы ------------
var commonClassName = "common";
var parentClassName = "parent";
var levelClassPrefix = "level_";
var menuWaitInterval = 500; // ms
// ----------- Переменные ------------
var container = document.getElementById(containerId);
var html = _class = mouseOverOut = id = "";
var i;
var idArray = []
// ----------- Функции ------------
// Рекурсивная функция для прохождения по многоуровневому
// хэшу и формированию HTML-кода меню
var cicleFunc = function(code, hash, level, parentId) {
if (typeof level != "undefined" && level != 1) code += "<span id='b" + parentId + "' style='display:none;'>\n";
for (i in hash) {
var randId = parseInt(Math.random() * 1e10).toString() + parseInt(Math.random() * 1e10);
_class =
((typeof hash[i][1] != "undefined") ? parentClassName : commonClassName) +
" " + levelClassPrefix + level;
if (typeof hash[i][1] != "undefined") {
id = " id='a" + randId + "'";
idArray.push(randId);
} else id = 0;
code += "<a" + (id ? id : "") + " class='" + _class + "' href='" + path + i + "'>" + hash[i][0] + "</a>\n";
if (typeof hash[i][1] != "undefined") code += cicleFunc(html, hash[i][1], level+1, randId);
}
if (typeof level != "undefined" && level != 1) code += "</span>\n";
return code;
}
// ----------- Добавление HTML-кода меню на страницу ------------
html = cicleFunc(html, hash, 1);
container.innerHTML = html;
// ----------- Добавление обработчиков событий ------------
for (i = 0; i < idArray.length; i++) {
document.getElementById("a" + idArray[i]).onmouseover = function() {
menuOpenedCount++;
var absId = this.id.substring(1, this.id.length);
document.getElementById("b" + absId).style.display = "";
}
document.getElementById("a" + idArray[i]).onmouseout = function() {
menuOpenedCount--;
var absId = this.id.substring(1, this.id.length);
if (typeof menuWaitTimer[absId] == "undefined" || menuWaitTimer[absId] == null) {
menuWaitTimer[absId] = setInterval("hideJSMenu('" + absId + "')", menuWaitInterval);
}
}
document.getElementById("b" + idArray[i]).onmouseover = function() {
menuOpenedCount++;
var absId = this.id.substring(1, this.id.length);
if (typeof menuWaitTimer[absId] != "undefined") {
clearInterval(menuWaitTimer[absId]);
menuWaitTimer[absId] = null;
}
}
document.getElementById("b" + idArray[i]).onmouseout = function() {
menuOpenedCount--;
var absId = this.id.substring(1, this.id.length);
if (typeof menuWaitTimer[absId] == "undefined" || menuWaitTimer[absId] == null) {
menuWaitTimer[absId] = setInterval("hideJSMenu('" + absId + "')", menuWaitInterval);
}
}
}
}
// ******************
// Функция для убирания выпадушек, запускается по таймауту
// ******************
function hideJSMenu(id) {
if (menuOpenedCount <= 0) {
menuOpenedCount = 0;
if (typeof menuWaitTimer[id] != "undefined") {
clearInterval(menuWaitTimer[id]);
menuWaitTimer[id] = null;
}
if (document.getElementById("b" + id)) {
document.getElementById("b" + id).style.display = "none";
}
}
}
