Dynmenu, introdução aos menus dinâmicos com DHTML 
GuiaFicheirosImagensExtrasContacto
 
english
 

Nesta página encontra-se uma descrição pormenorizada de cada ficheiro usado neste guia. O leitor é livre de copiar, colar ou adaptar o código apresentado nas suas páginas.

Ao longo da página há excertos do código utilizado mas pode aceder aos ficheiros completos a partir das sources.

Os menus dinâmicos foram concebidos a partir do princípio da modularidade: a maior parte do código está escrito em páginas próprias que são chamadas nas páginas HTML. Esta prática ajudará o leitor a criar o código para múltiplas páginas e contribuirá sobremaneira para manter, rever e actualizar as páginas.

  Style sheet: dm.css
 

Este ficheiro desempenha uma dupla função:

  1. define a localização na página e as características dos layers que implementam os menus dinâmicos e
  2. define os estilos das tags de HTML usadas.

Embora só a primeira função seja relevante para os menus dinâmicos, é importante salientar que é preferível usar style sheets para codificar a apresentação visual das páginas do que embeber a informação visual nas páginas HTML: ao usar style sheets garante-se a coerência do design em todo o sítio e há apenas uma página a modificar ou actualizar quando se revê o sítio, em vez das dezenas ou centenas páginas HTML.

Cada menu dinâmico é definido por uma linha de código semelhante a:

#dynmenu0 { position: absolute; top: 67; left:3; z-index:99; visibility:hidden; background-color:#CC0000; }

Os elementos na linha têm o seguinte significado:

#dynmenu0:
o nome do layer segue o cardinal(#).
Eu uso normalmente uso um só nome para todos os layers ("dynmenu"), seguido por um algarismo que é também usado para referir o menu nas variáveis de array.
position:
a posição é definida como absoluta em relação às coordenadas da janela - poderia ser definida relativamente a outros layers - e as coordenadas são indicadas 3px (pixels) da margem esquerda e 67px do topo da janela; o z-index define a ordem de sobreposição dos layers. Não há valores absolutes, apenas a relação entre eles conta: os valores mais altos ficam por cima e o código da secção <BODY></BODY> é sempre a camada mais baixa.
O leitor deve assegurar-se que as posições estão bem definidas para obter o melhor efeito visual. Uma das razões que me levam a usar os menus junto ao vértice superior esquerdo da janela é assegurar que não há elementos definidos antes dos menus que possam alterar a disposição gráfica da página, prejudicando o efeito visual.
visibility:
declara o estado de visibilidade inicial do layer:visible (visível) or hidden (escondido). Normalmente, os menus estão inicialmente escondidos, mas o leitor pode deixar um deles inicialmente vísivel para sugerir o seu uso aos visitantes do sítio; actualmente, esta ajuda pode parecer redundante à maioria das audiências.
background-color:
determina qual é a cor de fundo do layer. Se nada fôr declarado, o fundo do layer é transparente. Pode também definir a cor do fundo no código do layer.
Eu prefiro esta solução para garantir que o fundo dos layers é opaco. Como o leitor verá, fiz uma excepção no menu "Extras" para fins didáticos. Para mais informação leia a secção Fundos Transparentes.

Neste guia uso cinco menus de largura igual. Naturalmente, o leitor pode alterar o número, espaçamento e largura dos menus. Só é importante que as posições iniciais estejam rigorasemente definidas.

Aconselho o leitor a não limitar o tamanho dos layers para maior flexibilidade do conteúdo dos menus. Se desejar limitar a largura do menu, use uma tabela com a largura limitada e a altura livre para que possa usar texto mais curto ou mais longo, texto maior ou um número variável de entradas no menu.

Há também outros parâmetros adicionais que o leitor pode usar consoante as suas preferências pessoais, tais como:

border-width:1; border-style:solid; border-color:#404040;

 Funções: dm_func.js
 

Este ficheiro contém

  1. as funções e as variáveis para mostrar e esconder os layers e
  2. as funções que comuta as imagens roll-over.

Só as primeiras são relevantes para os menus dinâmicos mas as segundas implementam un efeito bastante apreciado - e actualmente muito comum - dos links (ligações) com imagens.

O ficheiro começa com a definição de dynmenu, um array de Booleans (valores possíveis: true, false), com uma célula por menu. Como os menus foram definidos por números de 0 a 4, da esquerda para a direita, a definição do array segue a mesma sequência. Quando um dos layers está visível, a entrada correspondente no array vale true, caso contrário vale false.

Segue-se a definição de timerID, o array de time-out, com uma célula por layer. Se a visibilidade de um layer está pendente de um time-out activo, a identificação do time-out é guardada na célula correspondente. Caso contrário, o valor da célula não tem significado.

Neste guia há cinco menus e por isso, os arrays e os ciclos têm dimensão 5. Para maior generalidade, deveria ser usada uma constante com o número de menus.


Só há duas acções aplicadas aos menus: mostrar e esconder os menus, correspondendo às funções showMenu(idx) e hideMenu(idx).

A função showMenu(idx) recebe o índice do menu, idx (número inteiro entre 0 e 4) e percorre o array de visibilidade para verificar se outro layer está visível (a célula dynmenu[i] vale true). Se tal ocorrer, o layer correspondente é escondido:

for(i=0;i<5;i++) if (dynmenu[i] && (i!=idx)) hideMenu(i);

A acção seguinte é mudar a visibilidade do layer pretendido para visible. No entanto, é necessário multiplicar o código para contemplar as implementações de layers em DHTML usadas nos diversos browsers:

if(document.getElementById) document.getElementById("dynmenu"+idx).style.visibility='visible';
else if (document.all) eval("document.all.dynmenu"+idx+".style.visibility='visible';");
else if (document.layers) eval("document.dynmenu"+idx+".visibility='visible';");

A primeira linha é interpretada pelos browsers mais recentes que seguem a especificação do Document Object Model do World Wide Web Consortium (W3C, www.w3c.org). É o caso do Netscape Navigator 6 e posterior e do Microsoft Internet Explorer 6 e posterior. O nome do Element é composto por dynmenu seguido do índice idx e a sua propriedade .style.visibility é definida como visible.

A segunda linha dirige-se ao Microsoft Internet Explorer, versões 4 e 5. O nome do elemento a mudar é composto por document.all.dynmenu ao qual se soma o índice idx e a sua propriedade .style.visibility que é definida como visible. Neste modelo, todos os layers se encontram sob o array de objectos document.all.

A terceira linha dirige-se ao Netscape Navigator 4. O nome do elemento a mudar é composto por document.dynmenu ao qual se soma o índice idx e a sua propriedade .visibility que é definida como visible. Neste modelo, os layers encontram-se sob o array de objectos document.layers mas podem ser acedidos directamente a partir de document. seguido do nome do layer.

De seguida o array dynmenu é actualizado, definindo a entrada idx como true:

dynmenu[idx]=true;

Para terminar, é lançado um time-out: se o utilizador não escolher uma entrada do menu ou outro menu no prazo de 8000 milisegundos o layer visível será automaticamente escondido:

timerID[idx]=setTimeout("hideMenu("+idx+")",8000);

Esta função dirige-se aos utilizadores com menos experiência e que não estão à vontade com os layers, preferindo ver as páginas sem serem perturbados por elementos sobrepostos. No entanto, esta solução tem um inconveniente: se o utilizador mantiver o ponteiro sobre o menu dinâmico ao fim do período especificado, o menu desaparecerá inesperadamente e o utilizador terá de regressar ao link do título para voltar a revelar o menu. Por isso, o período de time-out deve ponderar os dois interesses contraditórios.

Outras implementações teriam resolvido este problema escondendo o menu quando o ponteiro não está sobre ele. Prefiro esta solução porque é mais simples para utilizadores pouco experientes enquanto os utilizadores treinados escolhem a linha do menu que pretende sem quaisquer entraves.


A função hideMenu(idx) é um "espelho" da função showMenu(idx). Apaga o time-out lançado por showMenu(idx). Se o time-out já tiver expirado o browser ignora o comando:

clearTimeout(timerID[idx]);

De seguida, define-se a propriedade visibility do layer com o índice idx como hidden. Como anteriormente, o código tem de ser multiplicado para contemplar as implementações de layers em DHTML usadas nos diversos browsers:

if(document.getElementById) document.getElementById("dynmenu"+idx).style.visibility='hidden';
else if (document.all) eval("document.all.dynmenu"+idx+".style.visibility='hidden';");
else if (document.layers) eval("document.dynmenu"+idx+".visibility='hidden';");

A primeira linha é interpretada pelos browsers mais recentes que seguem a especificação do Document Object Model do World Wide Web Consortium (W3C, www.w3c.org).

A segunda linha dirige-se ao Microsoft Internet Explorer, versões 4 e 5. Neste modelo, todos os layers se encontram sob o array de objectos document.all.

A terceira linha dirige-se ao Netscape Navigator 4. Neste modelo, os layers encontram-se sob o array de objectos document.layers mas podem ser acedidos directamente a partir de document. seguido do nome do layer.

Finalmente, o array dynmenu é actualizado, definindo a célula idx como false:

dynmenu[idx]=false;


O efeito das imanges roll-over é implementado por duas funções: imgOver(imgHndl) e imgOut(imgHndl). A primeira está associada ao event onMouseOver enquanto a segunda está associada ao event onMouseOut.

Quando o ponteiro (também chamado cursor ou mouse (rato)) passa sobre uma imagem com o código roll-over, o ficheiro source da imagem (propriedade SRC) é alterado para um ficheiro com o nome do actual acrescido do sufixo "ov": se a source da imagem fôr o ficheiro "XYZ.jpg", a source da imagem roll-over é o ficheiro "XYZov.jpg". Em resumo, todas as imagens em que se pretende o efeito roll-over devem ter a sua source duplicada com um outro ficheiro com o sufixo "ov".

O código da função imgOver() é explicado abaixo:

imgHndl.src=imgHndl.src.substr(0,imgHndl.src.length-4)+"ov"+imgHndl.src.substr(imgHndl.src.length-4);

  1. O handler da imagem é passado para a função imgOver(imgHndl).
  2. A propriedade source é extraída do handler e guardada numa string.
  3. Esta string é dividida em duas sub-strings. A primeira parte vai do início (offset igual a 0) até ao comprimento (length) da string menos os últimos quatro caracteres; estes quatro caracteres constituem a segunda sub-string, começando neste ponto (offset igual length-4) e terminando no fim da string, o que corresponde à extensão da imagem, normalmente, ".jpg", ".jp2", ".gif" or ".png".
  4. A nova source da imagem é criada juntando a primeira sub-string com o sufixo "ov" e com a extensão, guardada na segunda sub-string.

Quando o ponteiro sai de cima de uma imagem com o efeito roll-over, a source original é reposta, ou seja, sem o sufixo "ov", através da função imgOut(imgHndl):

imgHndl.src=imgHndl.src.substr(0,imgHndl.src.length-6)+imgHndl.src.substr(imgHndl.src.length-4);

  1. O handler da imagem é passado para a função imgOut(imgHndl).
  2. A propriedade source é extraída do handler e guardada numa string.
  3. Esta string é dividida em duas sub-strings. A primeira parte vai do início (offset igual a 0) até ao comprimento (length) da string menos os últimos seis caracteres; a segunda sub-string guarda a extensão, começando a quatro caracteres do fim (offset igual length-4) e terminando no fim da string.
  4. A nova source da imagem é criada juntando a primeira sub-string com a extensão, guardada na segunda sub-string.

Este código é de aplicação genérica e é independente do caminho (path) para as imagens. Os únicos requisitos são o uso do sufixo "ov" e o uso de extensões com três letras.

Convém recordar que apenas a source é alterada com o roll-over, mantendo-se a dimensão da imagem original. Por isso as duas imagens devem ter as mesmas dimensões.

 Conteúdo dos menus: dm_po.js
 

O ficheiro dm_po.js guarda o conteúdo dos menus dinâmicos. Se o leitor desejar construir um sítio multilingue tal como este guia português/inglês os dois ficheiros anteriores seriam comuns a todas as versões e apenas este ficheiro seria específico para cada língua. O ficheiro dm_po.js contém os menus e os links para as páginas em português enquanto o ficheiro dm_en.js se destina às páginas em inglês.

A codificação é elementar. O ficheiro contém apenas uma instrução de JavaScript para escrever o código HTML no documento:

document.write('<DIV ID="dynmenu0"><TABLE WIDTH="134" BORDER="1" BORDERCOLOR="#FFEEEE" CELLPADDING="2" CELLSPACING="0"><TR><TD class="tabm"><A HREF="guia.html#02">Introdu&ccedil&atilde;o</A></TD></TR><TR><TD class="tabm"><A HREF="guia.html#03">Modularidade</A></TD></TR><TR><TD class="tabm"><A HREF="guia.html#04">Condicionamentos</A></TD></TR><TR><TD class="tabm" NOWRAP><A HREF="guia.html#05">Como funciona</A></TD></TR><TR><TD class="tabm" NOWRAP><A HREF="guia.html#06"><i>Layers</i> explicados</A></TD></TR><TR><TD class="tabm" NOWRAP><A HREF="guia.html#07"><i>Events</i> explicados</A></TD></TR><TR><TD class="tabm"><A HREF="guia.html#08">Conclus&otilde;es</A></TD></TR></TABLE></DIV>');

O código acima refere-se a um só menu. Os demais menus são idênticos.

Os caminhos (paths) dos links neste exemplo são relativos; se o leitor desejar usar o ficheiro dm_po.js em páginas dispersas por diversas pastas/directorias deve usar um de dois métodos:

  1. Criar um ficherio dm_po.js para cada pasta/directoria, actualizando os caminhos relativos
  2. Usar caminhos absolutos a partir da raiz do sítio.

O segundo método é mais eficiente mas o debug do sítio torna-se mais díficil, excepto nos casos em que o sítio se encontre na raiz do file system (nos sistemas operativos da MicroSoft isto é uma letra de drive, C:, D:, ...).

Aconselho o leitor a fazer a divisão das linhas deste código manualmente pois não se pode usar quebras de linha no interior de uma instrução de JavaScript. Não obstante a apresentação do código acima, não usei quebras de linha no ficheiro dm_po.js que consiste numa única linha com 2702 caracteres. Se o comprimento da linha exceder os limites do seu processador de texto, o leitor deve usar uma instrução document.write() por layer.

 Páginas HTML
 

Os três ficheiros anteriores contém o núcleo do código dos menus dinâmicos. Nas páginas HTML, só é necessário acrescentar os links para os três ficheiros e os event handlers. Como todos os ficheiros deste guia têm a mesma implementação dos menus dinâmicos, basta apresentar uma das amostras de código:

<link rel="stylesheet" href="dm.css" type="text/css">
Esta linha deve ser incluída na secção <HEAD></HEAD>. O ficheiro dm.css fornece as características dos menus e os estilos das tags de HTML.
<SCRIPT LANGUAGE="JavaScript1.2" TYPE="text/javascript" SRC="dm_func.js"></SCRIPT>
Esta linha deve ser incluída na secção <HEAD></HEAD>. O ficheiro dm_func.js fornece as funções que implementam os menus e as imagens roll-overs.
<SCRIPT LANGUAGE="JavaScript1.2" TYPE="text/javascript" SRC="dm_po.js"></SCRIPT>
Esta linha deve ser incluída fora da secção <HEAD></HEAD> e fora da secção <BODY></BODY>. O leitor pode adicioná-la entre as duas ou após a secção <BODY></BODY>, antes da tag de fecho </HTML>.
O ficheiro dm_po.js fornece o conteúdo dos menus dinâmicos: o texto dos menus e os respectivos links.
<img src="../../02/icon/vela2.png" onMouseOver="imgOver(this)" onMouseOut="imgOut(this)" width="74" height="40" border="0">
Esta linha não se refere aos menus dinâmicos mas às imagens de roll-over. Os events onMouseOver e onMouseOut têm de estar associados à imagem e não ao link para garantir que o handler "this" se refere à imagem. É a partir deste handler que se determina a source da imagem.
<td bgcolor="#CC0000" width="110" class="tab"><a href="tutorial.html" onMouseOver="showMenu(0)">Tutorial</a></td>
Esta é a amostra do código usado para mostrar o menu dinâmico - neste caso o primeiro menu com idx=0 (ver Funções: dm_po.js acima).
O leitor deve reparar que o menu dinâmico aparece em resposta ao event onMouseOver mas que não é contemplado o event onMouseOut. De facto, o menu dinâmico é escondido quando o utilizador segue um link para outra página, quando chama um outro menu ou quando expira o time-out lançado por showMenu(idx), lançando a função hideMenu(idx) (ver Functions: dm_po.js acima).
Os restantes título de menu são codificados de forma idêntica, bastando alterar o índice nas chamadas a showMenu(idx).

E a implementação dos menus dinâmicos está completa... simples, não é ?

©2003 João Gomes Mota
Home, Inicio → WEB DESIGNJAVASCRIPT, DHTML
Escrito em Outubro 2003.inicio da página