1 (изменено: Санёк, 2008-01-02 17:20:25)

Тема: JavaScript: Выпадающее меню

Пример простого выпадающего меню, можно настроить (переписать) под разные цели и задачи.
Меню работает в различных популярных браузерах (IE, Opera, FireFox).

<script language="javascript" type="text/javascript">
// Объявление глобальных переменных
var DOM = (typeof(document.getElementById)!='undefined');
var menu_div, _menu_a, div_i = null;

var js =
{
    // addEvent - функция добавления события 
    /* Пример вызова: js.addEvent(document,'click',menu.check);
    document - объект, которому назначается событие
    click - название события
    menu.check - имя функции (ВАЖНО: при использовании конструкции вроде js.addEvent(document,'click',function(){alert('сообщение')});) - это событие не удаляется!!!
    */
    addEvent: function(el, evname, func){
        if(el.attachEvent) // IE
            el.attachEvent('on' + evname, func);
        else if(el.addEventListener) // Gecko / W3C
            el.addEventListener(evname, func, false);
        else
            el['on' + evname] = func;
    },
    
    // addEvent - функция удаления события
    // Пример вызова: аналогично предыдущей функции (если необходимо удалить созданное событие - объект, имя события и имя функции должны совпадать)
    removeEvent: function(el, evname, func){
        if(el.detachEvent) // IE
            el.detachEvent('on' + evname, func);
        else if(el.removeEventListener) // Gecko / W3C
            el.removeEventListener(evname, func, false);
        else
            el['on' + evname] = null;
    },
    
    /* Функция получения коодринат местоположения объекта на странице (el - объект, который анализируем)
    функция возвращает ассоциативный массив со значениями положения объекта
        Элементы массива:
        'top' - расстояние в пикселях от верхнего края объекта до верхней точки документа
        'left' - расстояние в пикселях от левого края объекта до левого края документа
        'right' - расстояние в пикселях от нижнего края объекта до верхней точки документа
        'bottom' - расстояние в пикселях от правого края объекта до левого края документа
         */
    GetRealPos: function(el){
        if(!el || !el.offsetParent)
            return false;
        var res=Array();
        res['left'] = el.offsetLeft;
        res['top'] = el.offsetTop;
        var objParent = el.offsetParent;
        while(objParent.tagName != 'BODY'){
            res['left'] += objParent.offsetLeft;
            res['top'] += objParent.offsetTop;
            objParent = objParent.offsetParent;
        }
        res['right'] = res['left'] + el.offsetWidth;
        res['bottom'] = res['top'] + el.offsetHeight;
        
        return res;
    }
}

// Объект выпадающего меню.
var menu =
{
    // Метод show - вызывает меню
    /* Получает параметры:
    obj - необходимо передавать объект, относительно которого будет рисоваться меню
    arr - массив определённой структуры:
        в данном случае [['иконка','название пункта','ссылка'],['иконка','название пункта','ссылка']]
    Пример вызова: onClick="menu.show(this,[['images/open.gif','Просмотр','/menu/#open'],['images/child.gif','Создать подкатегорию','/menu/#child']])"
    */
    show: function(obj,arr){
        if(div_i)div_i.className = 'menu_img';
        menu_div = document.getElementById('menu');
        _menu_a = document.getElementById('menu_a');
        if(!arr){_menu_a.innerHTML = '';}
        div_i = obj;
        div_i.className = 'menu_img_hover';
        pos = js.GetRealPos(obj);
        _menu = menu_div;
        _menu.style.top = pos.top + 9;
        _menu.style.left = pos.left + 28;
        if(arr){
            _menu_a.innerHTML = menu.initFunc(arr);
        }
        else{return;}
        _menu_pos = js.GetRealPos(_menu);        
        _menu.style.display = 'block';
        js.addEvent(document,'click',menu.check);
        js.addEvent(document,'keypress',menu.press);        
    },
    
    hide: function(){
        menu_div.style.display = 'none';
        div_i.className = 'menu_img';
        js.removeEvent(document, 'click', menu.check);
        js.removeEvent(document, 'keypress', menu.press);
    },
    
    /* Метод initFunc - описывает правила для обработки массива и генерации html кода - описывается отображение пунктов меню
    Этот метод не вызывается извне объекта menu
    Получает параметры:
    arr - массив определённой структуры:
        в данном случае [['иконка','название пункта','ссылка'],['иконка','название пункта','ссылка']]
    */
    initFunc: function(arr){
        var html = '<table>';
        for(i in arr){
            im = "gray_pic.gif";
            // Условие определяет какую картинку использовать, в зависимости от того, какой это пункт - первый или последний
            if(arr.length!=1){
                if(i==0){im = "s_menu_f.gif";}
                else if(parseInt(i)+1==arr.length){im = "s_menu_l.gif";}
            }
            else{
                im = "s_menu.gif";
            }
            // Здесь в переменную html добавлется по одной строке, соответственно здесь и настраивается вид пунктов меню
            html += '<tr><td height="30"><img src="'+arr[i][0]+'" style="margin:0px 6px 7px 9px" alt=""><img src="images/'+im+'" class="s_im" alt=""></td><td valign="middle"><a class="menu_a" href="'+arr[i][2]+'">'+arr[i][1]+'</a></td></tr>';
        }
        html += '</table>';
        return html;
    },
    
    // Метод press - скрывает меню при нажатии определённой клавиши, в данном случае - Esc
    press: function(e){
        if(!e) e = window.event;
        if(!e) return;
        if(e.keyCode == 27)
            menu.hide();
    },
    
    // Метод check - проверяет - нужно ли скрывать меню или нет.
    // В данном случае метод вызывается при кликании мышью, если координаты "клика" не совпадают с координатами меню и каких-либо объектов, то меню закрывается!
    check: function(e){
        if(!div_i)
            return;
        var x = e.clientX + document.body.scrollLeft;
        var y = e.clientY + document.body.scrollTop;
        /*menu_button region*/
        if(div_i){
            var pos = js.GetRealPos(div_i);
            if(x >= pos['left'] && x <= pos['right'] && y >= pos['top'] && y <= pos['bottom'])
                return;
        }
        /*menu region*/
        if(_menu){
            var pos = js.GetRealPos(_menu);
            if(x >= pos['left'] && x <= pos['right'] && y >= pos['top'] && y <= pos['bottom'])
                return;
        }
        menu.hide();
    },
    
    // Методы over и out - используются в данном случае для определения имени css класса, который нужно присвоить картинке, используется в частных случаях.
    over: function(){
        if(!div_i)
            return;
        div_i.className = 'menu_img_hover';
    },
    
    out: function(obj){
        if(!obj)
            return;
        _menu = menu_div;
        if(!_menu || obj!=div_i){obj.className = 'menu_img';}
        else{
            if(_menu.style.display != 'block'){obj.className = 'menu_img';}
        }
    }
}
</script>

Пример использования меню можно посмотреть здесь или во вложении этого поста.

Недостатки:
- Если не будет работать javascript - ссылки не будут видны (поэтому многие меню основаны на заранее описанных списках LI);
- Для создания многоуровнего меню необходимо дописывать методы, добавлять новые, т.е. скрипт не предусматривает изначально многоуровневые выпадающие меню.

Post's attachments

menu.rar 9.22 kb, 589 downloads since 2007-12-30 

You don't have the permssions to download the attachments of this post.
Нет ничего невозможного, для нас невозможное это только то, о чем мы не знаем на данном этапе...