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

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.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
SIGUIENTE ARTÍCULO

HAY 41 COMENTARIOS
  • Marco dijo:

    Tengo el Explorer 4.5. Probe lo que decia en el articulo, pero se me corre la barra de menus y se cruzan los textos. Saludos, Marc

  • Ximo dijo:

    He provado ha sustituir la referencia por la mia del web, y no aparecen los menus si no utilizo esta: ¿Que significa el numero 0056?

  • debial dijo:

    si vuestros navegadores no pueden con el codigo no se a k koñ.. esperais para hactualizarlos y ahora pa ti tiu sigue asi las explicaciones tan muy bien DEBIAL :p

  • Triki dijo:

    Muy buena la lección, pero mi problema surge cuando quiero hacer lo mismo en vertical y con + d 1 nivel. ¿cómo puedo hacerlo?

  • Pedro dijo:

    este script es incompleto, ya que solo sirve la primera ves que se carga, luego al moverse en la pagina, los links que muestrarn no funcionan

  • Alfredo S. dijo:

    Yo soy una mas de esas personas que necesitan la barra de menus desplegables en marcos distintos ¿Alguien lo ha conseguido? Salu2.

  • Sergio dijo:

    Esta tan limitado como la mayoria de menus que hay en la web , si insertas un 3 frame , las opciones del menu , solo se muestran en el espacio del 2 frames. Conclusion: el menu es dependiente de la estructura de la pagina y por lo tanto es poco flexible. Saludos

  • Guillermo dijo:

    El programa anda excelente en Netscape 6, solo surge un pequeño inconveniente en cuanto a los hipervinculos.... que no me funcionan... Por favor ¿Alguien sabe como solucionar esto para Netscape 6?

  • chica dijo:

    necesito hacer un menu desplegable hacie el lado y con más de 2 niveles. Intente modificar este pero no me resulto. Espero uds. me puedan dar alguna idea o ayuda. Gracias

  • Marco Antonio dijo:

    Hey amigo necesito ayuda para realizar un menu desplegable, quisiera si se podria, me pudieras mandar un ejemplo completo de esto, con sus respectivos codigos... porfavor .. GRACIAS

  • jose dijo:

    He tenido problemas a la hora de implementarlo.

  • john dijo:

    No he podido encontrar la opcion para colocarle submenus a la barra de menus desplegables de antemano muchas gracias su codigo me ha servido de mucha ayuda

  • Miluska dijo:

    Estoy tratando de anadir un menu desplegable en el frame izq. pero no se ve todo y no entiendo muy bien su explicacion, por favor contesten. y como abrirlo en el frame derecho

  • Javier Diaz dijo:

    Aupa, lo primero felicitaros por vuestra página, está muy bien. Los segundo haber si me puedes mandar los fuentes, por que no consigo hacerlo funcionar. Gracias.

  • eloy dijo:

    Necesito urgentemente que si puedes mandarme un erjemplo de un menu desplegable como el que utiliza microsoft en su pagina principal es muy importante, te lo agradeceria mucho ademas puedo hacerte publicidad de tu pagina en las que construyo. gracias !

  • Antonio dijo:

    Necesito que algunas opciones del menú presenten páginas ubicadas en otra URL. Las presenta pero da un error de JavaScript y el menú ya no funciona. ¿Alguna solución conocida?

  • Jorge dijo:

    EL ejemplo mostrado para menus desplegables en frame funciona bastante bien, ¿pero si quiero que estos menus se despliegen desde un iframe como hago referencia a este iframe para que se despliege el menu en el marco central?

  • frodo dijo:

    Haber si dejais de pedir códigos completos y cosas por el estilo. Sois unos vagos de cojones. ¿Para que esta el tutorial? Si lo seguis desde el principio y con un poco de paciencia os funcionara, pero no, es mejor q nos lo den hecho verdad? y todos ademas con prisas...no teneis cara! Yo ya he conseguido que los menús desaparezcan solos si el usuario deja el ratón unas décimas de segundo fuera del mismo. Es cuestión de echarle un rato a pensar. ¡Enhorabuena al autor del tutorial!

  • Teresa dijo:

    Ejemplo 3 a:link {text-decoration: none} a:visited {text-decoration: none} a:active {text-decoration: none} a:hover {text-decoration: underline} .menu{ position:absolute; width:90px; border:1px solid black; background-color:#6699CC; font-family:Verdana; font-size: 10px; line-height:11px; cursor:default; visibility:hidden; } .menuitems{ padding-left:10px; padding-right:10px; } .subtitulo{ padding-left:10px; padding-right:10px; } var menuskin=0; function mostrarMenu(){ var bordeDerecho = document.body.clientWidth - event.clientX; //lo que queda hasta el borde derecho var bordeInferior = document.body.clientHeight - event.clientY; //lo que queda hasta el borde inferior if (bordeDerecho Aragua Obras Proyectos if (document.all && window.print){ document.onmouseOut=ocultarMenu; document.onmouseup=ocultarMenu; document.onMouseOver=mostrarMenu; } Tramites Si me pueden ayudar ya que mi menu solo desaparece cuando esta en el ultima item se que con setTimeout se puede hacer pero no se en que parte colocarle el evento

  • Carolina Fuica Carriel dijo:

    Los felicito por darnos la oportunudad de utilizar los menus que cada vez son mas buenos,, pero tengo un problema necesito uno con mas de un nivel urgente please

  • vanessa dijo:

    Hola a todos. Tengo un ligero problemilla con mi menú. Resulta, que al desplegarlo, queda por debajo de los combo box. Con los demás controles (cajas de texto etc.) el menu queda por encima como tiene que ser. Sabe alquien que tengo que hacer para corregir este error?

  • Francisco Bañuls dijo:

    He hecho un menu con desplegables en fireworks, al ponerlo en diferente marco el desplegable se queda detrás del marco principal. Y si lo pongo todo en el mismo marco el desplegable se queda detrás de las capas o textos que haya incluido. Os agraderecía que me pudieseis explicar una solución para esto. Un saludo y gracias de antemano. Posdata. Vuestra web es muy interesante.

  • jordi dijo:

    Hola me gustaria haber sialguien me puede ayudar he estado probando el menu desplegable este en diferentes frames , y me gustaria que cuando el raton no estubiera encima del menu se cerraran todos los menus gracias

  • Silvia dijo:

    Hola, tengo una web con un menú desplegable que realicé con Fireworks (http://www.iac.es/eav ), pero cuando se navega con Netscape los desplegables se ven mal (no se despliegan hacia abajo como en Internet Explorer). He buscado información sobre ésto y no encuentro nada....¿alguien tiene alguna idea? Gracias.

  • María Robledo Gómez dijo:

    De gran ayuda los menus desplegables con frames, me funciona el de un nivel que usted puso en el ejemplo, pero necesito incluirle más de un nivel y más de dos frames, qué haría en este caso?, le doy gracias por la ayuda que presta a los que se inician.

  • RhOsS dijo:

    Me gustaria saber como puedo hacer aparecer el menu por encima de la pagina principal de una pagina con marcos, es decir, tengo un encabezado y una pagina pricipal, y el menu se desliza sobre la pagina de encabezado y quiero que se deslice por encima de la pagina principal. Muchas gracias y el articulo está genial!!

  • Benito Peñín dijo:

    He hecho un menu con desplegables en fireworks, al ponerlo en diferente marco el desplegable se queda detrás del marco principal. Y si lo pongo todo en el mismo marco el desplegable se queda detrás de las capas o textos que haya incluido. Os agraderecía que me pudieseis explicar una solución para esto. Un saludo y gracias de antemano. Posdata. Vuestra web es muy interesante.

  • Jorge dijo:

    En opciones.htm tengo un objeto flash, y quiero que este cambie en función de la página que visite en principal. ¿Como paso el nombre del flash de principal a opciones? Gracias de antemano

  • Jesus Alejandre dijo:

    En primer lugar felicitaciones, la idea es genial. Llevo varios dias y partes de las noches luchando por resolver el enigma: 1)He repasado todos los articulos, I,II y este III 2)El editor es:IBM WebSphere Page Designer V3.5 for Windows. 3) Errores: (3.1) top.opciones.escribirMenus es nulo o no es un objeto.

  • Jesus Alejandre dijo:

    En primer lugar felicitaciones, la idea es genial. Llevo varios dias y partes de las noches luchando por resolver el enigma. 1)He repasado todos los articulos, I,II y este III 2)El editor es:IBM WebSphere Page Designer V3.5 for Windows. 3) Errores: (3.1) top.opciones.escribirMenus es nulo o no es un objeto. (3.2) wref.inicializar es nulo o no es un objeto 4)Lineas que no acepta: , solo permite BGCOLOR 5) A los marcos les he nombrado opciones i principal como apunta en el fuente,tambien he hecho diversas pruebas cambiando los nombres de las paginas con opciones,top,principal,etc. 6) Tambien la hoja.css esta agregada al proyecto. Las pruebas que he realizado con el ej.II han sido correctas pero no se adapta a lo que necesito ya que el codigo de las paginas se incrementa demasiado y si utilizo este ejemplo con marcos el menu obviamente no se ve en su totalidad. Gracias por todo, por el sitio, el articulo y tu respuesta si ha lugar.

  • Starboy dijo:

    Y esto se lo digo a nuestro coleguita que nos ha llamado vagos. Aquí en la oficina nos mandan tareas de especial urgencia y encima tenemos otros trabajos pendientes que también precisan de la misma, así que supongo que este señor estará en casita con horas y horas libres para jugar con su ordenador. Aquí estamos trabajando y siempre nos viene bien una ayudita, para algo están los foros. Yo no sé todavía que hace este VAGO aquí.

  • Diego Alejandro dijo:

    Saludos a todos los que lean este mensaje. Primero que todo los felicito por la pagina y los artículos que publican. Solo me gustaría preguntarles como hacer para que los menús sigan con su funcionamiento normal cuando hay una pagina diferente a las mías en al marco principal. Ej.: tengo un marco de enlaces en la parte superior de la página y cuando uno de estos es pulsado es abierto en el marco inferior (principal), cuando esto pasa los menús no funcionan porque la página abierta no tiene el código necesario para que estos funcionen. ¿Cómo puedo hacer para que sigan funcionando correctamente? Gracias.

  • encina dijo:

    necesito saber como se hace un menú desplegables pero en flash

  • juanito dijo:

    que tal a todos, necesito saber si hay alguna forma de usar smartnavigation sin que el menu hecho en javascript desaparezca de mi pagina, si alguien puede ayudarme gracias!!

  • Cristián Oliva dijo:

    Por favor podrian explicar mejor el ejemplo, al seguir los pasos no funciona, podrían explicar en detalle que es lo que se debe escribir en la pagina opciones y en la pagina principal, etc... Gracias.

  • Usuario2 dijo:

    No os comais la cabeza con tanto codigo este programa te lo hace solo: AllWebMenus PRO 3.1: Una prueba: http://www.woxter.com/index_esp.htm

  • Diego Gómez dijo:

    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.

  • edgar cruz dijo:

    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.

  • David dijo:

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

  • Nelson yovani lugo dijo:

    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 |_____

  • Norma dijo:

    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

Conéctate o Regístrate para dejar tu comentario.