Programación en castellano
Inicio > Taller Web > Javascript > Barra de menús desplegables (III): En marcos distintos
-Artículos

Barra de menús desplegables (III): En marcos distintos

1 . ¿Qué cambia?
2 . Escribir el código HTML de los menús
3 . Cómo adaptar el código Javascript
    3.1 . Controlar el scroll
    3.2 . Sincronización

Parece que muchos de nuestros lectores desean tener, en un marco, las opciones principales y, en otro, los menús desplegados. Os mostramos cómo hacerlo, de una manera robusta.

¿Qué cambia?

Al contrario que el artículo anterior, este artículo no es una mejora que todos los usuarios de los menús desplegables deban utilizar. Tan sólo es recomendable para aquellos que quieran disponer de este tipo de menús en páginas con varios marcos. Desde luego, es recomendable la lectura de los anteriores artículos de esta serie para comprender éste.

El problema es el siguiente. Tenemos las opciones principales (es decir, la barra de menús) en un marco, y los menús deben desplegarse en otro distinto... Además, resulta conveniente no tener que estar escribiendo el código HTML correspondiente a las opciones en cada uno de los documentos en que se desplegarán, sino hacerlo sólo una vez en el marco que aloja las barras de menús. De este modo es más sencillo de mantener y más difícil cometer errores.

Escribir el código HTML de los menús

Si disponéis de Explorer 4 o Netscape 4 y superiores, podéis ver como nuestro ejemplo. El código no varía mucho, pero hay que hacer cambios. Entre otras cosas, ahora parte del código está en una página y otra parte en otra. Debemos tener cuidado de que ambos marcos estén ya totalmente cargados y cosas así.

Prácticamente todo el código estará en el marco que contiene la barra de menús desplegables, ya que será éste el que no cambie mientras navegamos por nuestra página. Sin embargo, necesitará un par de "ayuditas" del otro marco, que procuraramos sean lo más pequeñas posible.

Lo primero que debemos hacer es encontrar la manera de escribir el código HTML de los menús desde el marco que contiene la barra pero no los menús desplegados. A este marco lo llamaremos opciones siendo el otro principal. Esto lo conseguiremos, primero, añadiendo la siguiente función:

function escribirMenus() {
  top.principal.document.write('<DIV id="menu0" CLASS="menu">' +
  '<A HREF="../../recursos/img.htm">Imágenes</A><BR>' +
  ...
  '<A HREF="../131098.htm">No quedar encerrado en los marcos</A><BR>' +
  '</div>');
}

Sin embargo, esta función deberá ser llamada desde el otro marco. Esto es debido a la manera en que funciona document.write, tema que ya está tratado en el curso de Javascript, por lo que no abundaremos en exceso en ello. Lo que pasa es que si queremos escribir código HTML desde Javascript sin borrar el código que ya tiene la página, deberemos hacerlo antes de que se termine de leer la página. Es decir, llamando a document.write antes de que aparezca enel código fuente de la página la etiqueta </BODY>. Por tanto, colocaremos este código justo antes de esa etiqueta:

...
<SCRIPT LANGUAGE="Javascript1.2">
<!--
if (top.opciones.escribirMenus)
  top.opciones.escribirMenus();
else
  history.go(0);
//-->
</SCRIPT>
</BODY>
</HTML>

Lo primero que hacemos es comprobar si la función que hemos creado un poco más arriba ya existe. Si es así, la ejecuta, escribiendo el código HTML de nuestros amores. Si no es así, vuelve a cargarse la página. Esto puede parecer un desperdicio y, de hecho, lo es, pero es necesario ya que no tenemos manera de "obligar" al navegador a que cargue el otro marco antes que éste. De todos modos, no tendrá que recargarse muy a menudo, teniendo en cuenta que la función escribirMenus() la colocaremos al principio del fichero que contenga el otro marco y esta comprobación está al final del nuestro.

Por último, debemos incluir en el marco principal la hoja de estilos correspondiente a nuestros menús. Ya que lo que deseamos es poner el menor código repetido posible en esa página, lo mejor es guardar en un archivo externo dicha hoja de estilo:

menus.css
.menu {
  position:absolute;
  visibility:hidden; 
  background-color: white;
  layer-background-color: white;
  color: black;
  border-style: solid;
  border-color: black;
  border-width: 1px;
  padding: 3px;
  font-size : 12px;
  font-family: "arial", "helvetica";
}

.menu A:hover {text-decoration: underline; color: blue;}
.menu A {text-decoration: none; color: black;}

Y, por supuesto, llamarla desde nuestra página:

<link rel="STYLESHEET" href="menus.css" type="text/css">

Cómo adaptar el código Javascript

Lo que hacemos es copiar y pegar el código que hicimos en el artículo anterior y colocarlo en la página que contiene la barra de menús. Luego, procederemos a modificarlo para que "comprenda" que los menús están ahora en otro marco. Lo primero que vamos a hacer es crearnos una referencia al marco donde se despliegan los menús para ahorranos el escribirlo muchas veces y no tener que hacer muchos cambios si algún día le cambiamos el nombre:

var wref = parent.principal;

Lo siguiente será adaptar todas las referencias dentro del objeto menú para que sean referencias a objetos situados en el otro marco. Esto se logra cambiando una sola línea del código del constructor:

this.capaRefStr = (soporta.NS4) ?
  'wref.document["'+capaID+'"]' :
  ((soporta.IE4) ? 'wref.document.all["'+capaID+'"]' : 'this.domRef');

Por último, o casi, nos queda hacer una pequeña labor de inicialización del marco principal. Dado que los menús funcionan desapareciendo cuando pulsamos en cualquier lugar de nuestras páginas, debemos extender esa comprobación a todos los marcos que las componen. Quedaría un poco feo que pulsáramos en el marco donde se despliegan las opciones y no pasara nada. Por lo tanto, deberemos añadir el siguiente código en la cabecera de cada página situada en dicho marco:

<SCRIPT language="javascript">
var wref = parent.opciones;

// Inicializacion
function inicializar() {
  if (wref.soporta.DHTML) {
    if (wref.soporta.NS4)
      document.captureEvents(Event.MOUSEUP);
    document.onmouseup = wref.ocultarMenuActivo;
  }
}

window.onload = inicializar;
</SCRIPT>

Como vemos, es parte del código que hemos utilizado hasta ahora para inicializar, pero adaptado al hecho de que todo nuestro código está en un marco distinto, por lo que tenemos que colocar la referencia wref delante de cada llamada a funciones situadas en el marco opciones.

Controlar el scroll

Entre nuestros diversos problemas de adaptación, ahora tenemos uno nuevo, ¿qué pasa si el usuario intenta abrir un menú y la página situada en el marco principal está desplazada por medio de una barra de scroll? Pues que, como está colocada en un lugar fijo, seguramente se vea sólo parte. Queda muy feo. Jo. Así que tendremos que buscarnos la vida para colocarlo en el sitio adecuado. Para ello tendremos que utilizar tres soluciones distintas.

La más sencilla de las tres nos viene dada por un estándar: el CSS2. Desafortunadamente, la característica que vamos a utilizar sólo está disponible en el Netscape 6PR1, así que tendremos que utilizar otra cosa tanto para Explorer como para Netscape 4. Consiste en posicionar cada uno de los menús no en forma absoluta (absolute) sino de forma fija (fixed). Esto lo haremos en el constructor de los menús:

if (soporta.DOM) {
  ...
  if (!soporta.IE4)
    this.domRef.style.position = "fixed";
}

Tenemos que colocar esta segunda condición, porque el Explorer 5 soporta el DOM pero no esta característica del CSS2. En este navegador y el Netscape 4, tendremos que utilizar dos propiedades que nos indican cuantos pixels está desplazada la página de la esquina superior izquierda. En caso del Netscape esas propiedades son pageXOffset y pageYOffset. En el del Explorer document.body.scrollLeft y document.body.scrollTop. Así, de nuevo en el constructor, creamos cuatro nuevas propiedades en el menú.

this.topOffsetStr = (soporta.NS4) ? 'wref.pageYOffset' :
  (soporta.IE4 ? 'wref.document.body.scrollTop' : '0');
this.leftOffsetStr = (soporta.NS4) ? 'wref.pageXOffset' :
  (soporta.IE4 ? 'wref.document.body.scrollLeft' : '0');
this.top = top;
this.left = left;

Las dos primeras cadenas contienen el código necesario para acceder a esas propiedades en Netscape y Explorer y cero si es el Netscape 6. Ahora modificamos la función que coloca el menú:

function cambiarPosicionMenu(top, left) {
  eval(this.capaRefStr + this.estiloRefStr + this.topRefStr +
    ' = this.top + ' + this.topOffsetStr);
  eval(this.capaRefStr + this.estiloRefStr + this.leftRefStr +
    ' = this.left + ' + this.leftOffsetStr);
}

Y ahora, debemos llamar a esta función desde los sitios adecuados. El primer sitio donde lo haremos será la función mostrarMenu(), de modo que así nos aseguramos que, cada vez que mostremos un menú, lo hagamos en el sitio adecuado. Ahora, como mejora adicional, intentaremos que los menús se coloquen bien cada vez que se muevan las barras de scroll. Para ello creamos la siguiente función:

function moverMenuActivo(e) {
  if (menuActivo)
    menuActivo.cambiarPosicion();
}

Y la llamamos desde la función de inicialización:

function inicializar() {
  if (wref.soporta.DHTML) {
    if (wref.soporta.NS4)
      document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
    document.onmouseup = wref.ocultarMenuActivo;
    document.onmousemove = wref.moverMenuActivo;
  }
}

Sincronización

Lo que hemos hecho funcionaría a la perfección si vivieramos en un Internet de velocidad brutal donde acceder a una página fuera lo mismo que leerla del disco duro. Pero ya que no es así tenemos que lidiar con un problema adicional: la sincronización. Esto significa, en la práctica, que el código que maneja los menús debe saber de antemano que los dos marcos se han terminado de cargar.

Para lograrlo, lo primero que debemos decidir es cual de los dos marcos se encargara de este trabajo. Sólo puede haber una respuesta. Uno de los marcos se queda fijo, el de la barra de menús, y el otro varía, que es el principal. Lo más cómodo será que éste último controle lo que pasa en el fijo, que normalmente estará ya cargado y no nos causará mayores problemas. Lo haremos de la siguiente manera:

function inicializar() {
  if (!wref.inicializar) {
    setTimeout("inicializar()",10);
    return;
  }
  if (wref.soporta.DHTML) {
    if (wref.soporta.NS4)
      document.captureEvents(Event.MOUSEUP | Event.MOUSEMOVE);
    document.onmouseup = wref.ocultarMenuActivo;
    document.onmousemove = wref.moverMenuActivo;
    wref.inicializar();
  }
}

window.onload = inicializar;

La función de inicialización del marco principal se ejecuta cuando éste termina de cargar. Si el código fuente del otro marco (colocaremos la función inicializar() la última) no está aún cargado, esperamos a que lo esté. Y cuando eso sucede inicializamos también el marco de opciones con la llamada a wref.inicializar().

Pero existe un último problema. Y de verdad que éste sí que es el último. En los intervalos en los que se pulsa un enlace y el marco que contiene la barra de menús está cargado e inicializado pero en el otro todavía no se ha cargado ninguna página, tendremos un error de Javascript si intentamos desplegar un menú. Para evitarlo colocamos la siguiente comprobación:

function mostrarMenu() {
  if (!eval(this.capaRefStr)) return;
  ...
}

function ocultarMenu() {
  if (!eval(this.capaRefStr)) return;
  ...
}

Comprobamos si existe el menú y, si no es así, no hacemos nada. Y esto es todo, amigos.

 

Últimos comentarios
Últimos 5 comentarios

duda (30/10/2007)

Por
Cuál es el artículo anterior? ya que se hace re ferencia a él para que el programa de este artículo funcione.
Quisiera ver el codigo completo

muy bueno su ejemplo (12/08/2005)

Por
deseo saber como hago para hacer que en el ejemplo pueda crear un submenu de nivel 2 ej:
nivel1 | nivel1 |nivel1
__nivel2
__nivel2 >
|_____nivel3
|_____nivel3

|_____

Duda (23/12/2004)

Por
Porque nunca colocan el ejemplo completo, como para poder verlo funcionando..

Un poco mas de ayuda (29/07/2004)

Por
He leido y seguido al pie de la letra sus informes sobre los menus desplegables pero no me funcionan, seria posible que me enviaran el codigo?.
Ademas tengo una pregunta. ¿con este codigo puedo hacer que se genere un menu con las opciones que se guardan en una base de datos mysql y php?
si es asi me podrian ayudar. Mil y Mil gracias.

Ocultar automaticamente el menú. (12/07/2004)

Por
Amigos que tal.
Solamente quisiera saber que hacer para que los menús se ocultaran automáticamente de alguna forma, después de dejar de pasarlos con el ratón. Tengo una página con "Iframes" y cuando le doy clic encima de un iframe no se cierra. Por eso deseo que se oculte automáticamente. Gracias.
 
Tienda
Patrocinados
 

Copyright © 1999-2007 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad

Hospedaje web y servidores dedicados linux por Ferca Network

red internet: musica mp3 | logos y melodias | hospedaje web linux | registro de dominios | servidores dedicados
más internet: comprar | recursos gratis | posicionamiento en buscadores | tienda virtual | gifs animados