Задайте свой вопрос о JavaScript на нашем форуме

Многоуровневое выпадающее JavaScript меню

Задача: написать как можно более универсальную функцию, которая, принимая многоуровневый хэш, смогла бы построить выпадающее меню любой вложенности

Чтобы было понятно о чем речь, вот пример многоуровневого выпадающего меню на 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";
        }
    }
}

Александр Бурцев 31 августа 2008

© Все права на данную статью принадлежат порталу fastcoder.org. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.

Комментарии

 
Rambler's Top100 Flede HTML valid CSS valid