Nesse artigo vou mostrar como construir um menu no Javascript, com tabelas, divs e css. Para construir um menu precisamos utilizar recursividade, cada nó pode ter nós irmãos e nós filhos.

Abaixo a figura do modelo de classes.

Modelagem das Classes

Figura 1. Modelagem das Classes

Classe Menu.

id: Identificador do objeto

height: Altura do menu.

width: Largura do menu.

Classe ItemMenu.

id: Identificador do objeto

text: Texto do item.

value: Valor do item.

image: URL da imagem.

isSeparator: Indica se o item é objeto separador.

isOpen: Indica se o menu de filhos está aberto.

elementMenuDiv: Objeto que o item está dentro.

Listagem 1: Construtores.


function Menu(id, height, width) {
    this.id = id;
    this.height = height;
    this.width = width;
    this.items = [];
}

function ItemMenu(id, text, value, image, isSeparator) {
    this.id = id;
    this.text = text;
    this.value = value;
    this.image = image;
    this.isSeparator = isSeparator;
    this.items = [];

    this.isOpen;
    this.elementMenuDiv;
    this.onClick;
}

Agora vamos criar os métodos que serão chamados quando os eventos OnMouseOver, OnMouseOut e OnClick forem disparados.

Menu.prototype.itemMenu_OnMouseOver: Quando o mouse passa sobre o objeto.

Menu.prototype.itemMenu_OnMouseOut: Quando o mouse deixa de estar em cima do objeto.

Menu.prototype.itemMenu_OnClick: Evento que abre e fecha o menu filho.

Listagem 2: Eventos.


Menu.prototype.itemMenu_OnMouseOver = function (element) {
    element.style.backgroundColor = "#FFE6A0";
};

Menu.prototype.itemMenu_OnMouseOut = function (element) {
    element.style.backgroundColor = "";
};

Menu.prototype.itemMenu_OnClick = function (element) {
    var index = parseInt(element.attributes['indexItemMenu'].value);

    var oItemMenu = this.items[index];

    for (var row = 0; row < this.items.length; row++) {
        if (oItemMenu.id != this.items[row].id) {
            this.items[row].hideItens(this.items[row]);
        }
    }

    if (oItemMenu.elementMenuDiv != null) {
        if (oItemMenu.isOpen == null || oItemMenu.isOpen == false) {
            oItemMenu.isOpen = true;

            oItemMenu.elementMenuDiv.style.display = 'block';
            oItemMenu.elementMenuDiv.style.top = element.offsetTop + parseInt(this.height) + 1;
            oItemMenu.elementMenuDiv.style.left = element.offsetLeft;
        }
        else {

            oItemMenu.hideItens(oItemMenu);
        }
    }
};

ItemMenu.prototype.itemMenu_OnMouseOver = function (element) {
    element.style.backgroundColor = "#FFE6A0";
};

ItemMenu.prototype.itemMenu_OnMouseOut = function (element) {
    element.style.backgroundColor = "";
};

ItemMenu.prototype.itemMenu_OnClick = function (element) {
    var index = parseInt(element.attributes['indexItemMenu'].value);

    var oItemMenu = this.items[index];

    if (oItemMenu.elementMenuDiv != null) {
        if (oItemMenu.isOpen == null || oItemMenu.isOpen == false) {
            oItemMenu.isOpen = true;

            oItemMenu.elementMenuDiv.style.display = 'block';
            oItemMenu.elementMenuDiv.style.top = element.offsetTop + element.parentNode.parentNode.parentNode.parentNode.offsetTop;
            oItemMenu.elementMenuDiv.style.left = element.offsetLeft + parseInt(widthListComponentMenu) + 1 + element.parentNode.parentNode.parentNode.parentNode.offsetLeft;
        }
        else {
            oItemMenu.hideItens(oItemMenu);
        }
    }
    else {
        if (oItemMenu.onClick != null) {
            oItemMenu.onClick(oItemMenu);
        }

        isClickComponentMenu = false;
        hideAllComponentMenu();
    }
};

Os métodos que criaremos agora é para adicionar os itens no menu.

ItemMenu.prototype.addItemMenu: Adiciona um objeto filho.

ItemMenu.prototype.addItemMenuSeparator: Adiciona um objeto filho, esse objeto separador.

Menu.prototype.addItemMenu: Adiciona um objeto filho.

Listagem 3: Adicionando itens.


Menu.prototype.addItemMenu = function (id, text, value, image) {
    var index = this.items.length;

    this.items[index] = new ItemMenu(id, text, value, image, false);

    return this.items[index];
};

ItemMenu.prototype.addItemMenu = function (id, text, value, image) {
    var index = this.items.length;

    this.items[index] = new ItemMenu(id, text, value, image, false);

    return this.items[index];
};

ItemMenu.prototype.addItemMenuSeparator = function (id) {
    this.items[this.items.length] = new ItemMenu(id, '', '', '', true);
};

O próximo método tem a função de esconder os menus.

Listagem 4: Escondendo o menu.


ItemMenu.prototype.hideItens = function (oItemMenu) {
    if (oItemMenu.isOpen == true) {
        oItemMenu.isOpen = false;
        oItemMenu.elementMenuDiv.style.display = 'none';
    }

    for (var index = 0; index < oItemMenu.items.length; index++) {
        oItemMenu.hideItens(oItemMenu.items[index]);
    }
};

Menu.prototype.hideItens = function () {
    for (var index = 0; index < this.items.length; index++) {
        this.items[index].hideItens(this.items[index]);
    }
};

Métodos para construir o menu na página.

Menu.prototype.createMenu: Cria o menu principal.

ItemMenu.prototype.createItemMenu: Cria os menus filhos.

Listagem 5: Desenhando o menu.


Menu.prototype.createMenu = function (objRecipient) {
    var mainTable = document.createElement('TABLE');
    var mainTBody = document.createElement('TBODY');

    mainTable.style.width = this.width;
    mainTable.style.height = this.height;
    mainTable.cellPadding = 0;
    mainTable.cellSpacing = 0;
    mainTable.border = 0;
    mainTable.className = 'menuStyle';

    var mainTr = document.createElement('TR');
    var mainTd = document.createElement('TD');

    //########################### MENU ###############################

    var objTable = document.createElement('TABLE');
    var objTBody = document.createElement('TBODY');

    objTable.style.width = this.width;
    objTable.style.height = (parseInt(this.height) - 5) + 'px';
    objTable.cellPadding = 0;
    objTable.cellSpacing = 0;
    objTable.border = 0;

    var objTr = document.createElement('TR');

    var self = this;

    for (var index = 0; index < this.items.length; index++) {
        var objTd = document.createElement('TD');

        var oItemMenu = this.items[index];

        objTd.onmouseover = function () { self.itemMenu_OnMouseOver(this); }
        objTd.onmouseout = function () { self.itemMenu_OnMouseOut(this); }
        objTd.onclick = function () { self.itemMenu_OnClick(this); }
        objTd.onmousedown = function () { componentMenu_onMouseDown(); }

        var textItem = '';

        if (oItemMenu.image != null && oItemMenu.image != '') {
            textItem = '<img src="' + oItemMenu.image + '" /> <label class="fontItemMenuButton">' + oItemMenu.text + '</label>';
        }
        else {
            textItem = '<label class="fontItemMenuButton">' + oItemMenu.text + '</label>';
        }

        objTd.vAlign = 'middle';
        objTd.align = 'center';
        objTd.style.cursor = 'default';

        objTd.style.whiteSpace = 'nowrap';
        objTd.innerHTML = '<span> ' + textItem + ' </span>';
        objTd.setAttribute('indexItemMenu', index);

        objTr.appendChild(objTd);

        if (oItemMenu.items.length > 0) {
            oItemMenu.elementMenuDiv = oItemMenu.createItemMenu(objRecipient, oItemMenu.items);

            objRecipient.appendChild(oItemMenu.elementMenuDiv);
        }
    }

    var lastTd = document.createElement('TD');

    lastTd.style.width = '100%';
    lastTd.style.whiteSpace = 'nowrap';
    lastTd.innerHTML = ' ';

    objTr.appendChild(lastTd);

    objTBody.appendChild(objTr);
    objTable.appendChild(objTBody);

    //########################### MENU ###############################

    mainTd.appendChild(objTable);
    mainTr.appendChild(mainTd);
    mainTBody.appendChild(mainTr);
    mainTable.appendChild(mainTBody);
    objRecipient.appendChild(mainTable);
};

ItemMenu.prototype.createItemMenu = function (objRecipient, itemMenuCollection) {
    var objDIV = document.createElement('DIV');

    objDIV.style.display = 'none';
    objDIV.style.position = 'absolute';

    var objTable = document.createElement('TABLE');
    var objTBody = document.createElement('TBODY');

    objTable.style.width = widthListComponentMenu;
    objTable.cellPadding = 0;
    objTable.cellSpacing = 0;
    objTable.border = 0;
    objTable.className = 'menuStyle';

    var self = this;

    for (var index = 0; index < itemMenuCollection.length; index++) {
        var oItemMenu = itemMenuCollection[index];

        if (oItemMenu.isSeparator == false) {
            var objTr = document.createElement('TR');
            var objTd = document.createElement('TD');

            objTr.style.height = heightItemComponentMenu;

            objTd.onmouseover = function () { self.itemMenu_OnMouseOver(this); }
            objTd.onmouseout = function () { self.itemMenu_OnMouseOut(this); }
            objTd.onclick = function () { self.itemMenu_OnClick(this); }
            objTd.onmousedown = function () { componentMenu_onMouseDown(); }

            objTd.style.cursor = 'default';
            objTd.style.whiteSpace = 'nowrap';
            objTd.vAlign = 'middle';

            var textItem = '';

            if (oItemMenu.image != null && oItemMenu.image != '') {
                textItem = '<img src="' + oItemMenu.image + '" />  <label class="fontItemMenuButton">' + oItemMenu.text + '</label>';
            }
            else {
                textItem = '<label class="fontItemMenuButton">' + oItemMenu.text + '</label>';
            }

            if (oItemMenu.items.length > 0) {
                objTd.innerHTML = '<table cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td><span> ' + textItem + '</span></td><td style="width:6px"><img src="Arrow.png" /></td></tr></table>';
            }
            else {
                objTd.innerHTML = '<span> ' + textItem + ' </span>';
            }

            objTd.setAttribute('indexItemMenu', index);

            objTr.appendChild(objTd);
            objTBody.appendChild(objTr);

            if (oItemMenu.items.length > 0) {
                oItemMenu.elementMenuDiv = oItemMenu.createItemMenu(objRecipient, oItemMenu.items);

                objRecipient.appendChild(oItemMenu.elementMenuDiv);
            }
        }
        else {
            var objTr = document.createElement('TR');
            var objTd = document.createElement('TD');

            objTd.align = 'center';

            objTd.innerHTML = '<table cellpadding="0" cellspacing="0" border="0" width="95%"><tr><td class="itemMenuSeparatorStyle"><img style="height:1px;visibility:hidden;" /></td></tr></table>';

            objTr.appendChild(objTd);
            objTBody.appendChild(objTr);
        }
    }

    objTable.appendChild(objTBody);
    objDIV.appendChild(objTable);

    return objDIV;
};

Listagem 6: HTML da página.


<html>
    <head>
        <title></title>
        <link rel="stylesheet" href="MainStyleSheet.css" type="text/css" />
    
        <script language="javascript" type="text/javascript" src="MenuJavaScript.js"></script>
        <script language="javascript" type="text/javascript">
            var oMenu;
            var isClickComponentMenu;
            var widthListComponentMenu = '130px';
            var heightItemComponentMenu = '30px';

            function componentMenu_onMouseDown() {
                isClickComponentMenu = true;
            }

            function hideAllComponentMenu() {
                if (isClickComponentMenu == false) {
                    oMenu.hideItens();
                }

                isClickComponentMenu = false;
            }

            function document_OnLoad() {
                isClickObject = false;
                
                oMenu = new Menu('oMenu', '30px', '100%');

                //################ FILE #################
                var fileMenu = oMenu.addItemMenu('fileMenu', 'File', '', '');

                var newFileMenu = fileMenu.addItemMenu('newFileMenu', 'New', '', '');
                var notePadNewFileMenu = newFileMenu.addItemMenu('notePadNewFileMenu', 'Notepad', '', 'Notepad.png');
                notePadNewFileMenu.onClick = menu_OnClick;
                var wordPadNewFileMenu = newFileMenu.addItemMenu('wordPadNewFileMenu', 'WordPad', '', 'WordPad.png');
                wordPadNewFileMenu.onClick = menu_OnClick;

                var openFileMenu = fileMenu.addItemMenu('openFileMenu', 'Open', '', 'Open.png');
                openFileMenu.onClick = menu_OnClick;

                var saveFileMenu = fileMenu.addItemMenu('saveFileMenu', 'Save', '', 'Save.png');
                saveFileMenu.onClick = menu_OnClick;

                var saveAsFileMenu = fileMenu.addItemMenu('saveAsFileMenu', 'Save As', '', 'Save.png');
                saveAsFileMenu.onClick = menu_OnClick;

                fileMenu.addItemMenuSeparator('separator1');
                var printFileMenu = fileMenu.addItemMenu('printFileMenu', 'Print', '', 'Print.png');
                printFileMenu.onClick = menu_OnClick;

                fileMenu.addItemMenuSeparator('separator2');
                var exitFileMenu = fileMenu.addItemMenu('exitFileMenu', 'Exit', '', 'Exit.png');
                exitFileMenu.onClick = menu_OnClick;
                //################ FILE #################

                //################ EDIT #################
                var editMenu = oMenu.addItemMenu('editMenu', 'Edit', '', '');
                var undoMenu = editMenu.addItemMenu('undoMenu', 'Undo', '', 'Undo.png');
                undoMenu.onClick = menu_OnClick;

                var redoMenu = editMenu.addItemMenu('redoMenu', 'Redo', '', 'Redo.png');
                redoMenu.onClick = menu_OnClick;

                editMenu.addItemMenuSeparator('separator3');
                var cutMenu = editMenu.addItemMenu('cutMenu', 'Cut', '', 'Cut.png');
                cutMenu.onClick = menu_OnClick;

                var copyMenu = editMenu.addItemMenu('copyMenu', 'Copy', '', 'Copy.png');
                copyMenu.onClick = menu_OnClick;

                var pasteMenu = editMenu.addItemMenu('pasteMenu', 'Paste', '', 'Paste.png');
                pasteMenu.onClick = menu_OnClick;

                var deleteMenu = editMenu.addItemMenu('deleteMenu', 'Delete', '', 'Delete.png');
                deleteMenu.onClick = menu_OnClick;

                editMenu.addItemMenuSeparator('separator4');
                var findMenu = editMenu.addItemMenu('findMenu', 'Find', '', 'Find.png');
                findMenu.onClick = menu_OnClick;

                var replaceMenu = editMenu.addItemMenu('replaceMenu', 'Replace', '', 'Replace.png');
                replaceMenu.onClick = menu_OnClick;

                //################ EDIT #################

                //################ FORMAT #################
                var formatMenu = oMenu.addItemMenu('formatMenu', 'Format', '', '');
                var fontMenu = formatMenu.addItemMenu('fontMenu', 'Font', '', 'Font.png');
                fontMenu.onClick = menu_OnClick;

                //################ FORMAT #################

                //################ VIEW #################
                var viewMenu = oMenu.addItemMenu('viewMenu', 'View', '', '');
                var statusBarMenu = viewMenu.addItemMenu('statusBarMenu', 'Status bar', '', 'statusBar.png');
                statusBarMenu.onClick = menu_OnClick;

                //################ VIEW #################

                //################ HELP #################
                var helpMenu = oMenu.addItemMenu('helpMenu', 'Help', '', '');
                var helpTopicsMenu = helpMenu.addItemMenu('helpTopicsMenu', 'Help Topics', '', '');
                helpTopicsMenu.onClick = menu_OnClick;

                helpMenu.addItemMenuSeparator('separator5');
                var aboutMenu = helpMenu.addItemMenu('aboutMenu', 'About', '', '');
                aboutMenu.onClick = menu_OnClick;
                //################ HELP #################

                oMenu.createMenu(document.getElementById('recipientTD'));
            }

            function menu_OnClick(oItemMenu) {
                document.getElementById('clickMenuLabel').innerHTML = oItemMenu.text;
                document.getElementById('clickMenuImg').src = (oItemMenu.image == null || oItemMenu.image == '' ? 'Nulo.png' : oItemMenu.image);
            }

            function document_OnMouseUp() {
                hideAllComponentMenu();
            }

            document.onmouseup = document_OnMouseUp;
        </script>
    </head>
    <body onload="document_OnLoad();" >
        <table cellpadding="0" cellspacing="0" border="0" width="100%" >
            <tr>
                <td id="recipientTD">
                </td>
            </tr>
            <tr style="height:400px;">
                <td align="center" valign="middle">
                    <img id="clickMenuImg" alt="" src="Nulo.png" />
                    <label id="clickMenuLabel"></label>
                </td>
            </tr>
        </table>
    </body>
</html>
Exemplo do Menu

Figura 2. Exemplo do Menu.

Até o próximo artigo.