Ir al contenido principal

[javascript] Scroll vertical se vuelve loco al trabajar un menú fijo (fixed menu)

Estoy trabajando en el desarrollo de una página web, en la cual va incluido un menú en la parte superior, de esos amplios que están tan de moda en el diseño gráfico web hoy en día. La idea es que al bajar por la página, este menú permanezca fijo y visible en la parte superior de la página, pero con el detalle de que sea una versión reducida del menú original. O sea, disminuye el alto del menú y el tamaño de la fuente, a fin de no molestar demasiado con el resto del contenido visible en la ventana. 

El problema:

Hasta ahí había logrado hacer toda la funcionalidad usando CSS y Javascript. De hecho iba perfecto, exceeeeepto en algunas ocasiones en las que, especialmente cuando la cantidad de contenido en la página no alcanzaba a traspasar mucho el alto de la ventana, y se generaba la barra de desplazamiento vertical, con la cual yo intentaba bajar con el scroll, el mismo se volvía loco y alternaba la apariencia del menú superior entre la amplia y la reducida, moviendo el scroll por sí mismo de arriba a abajo en un pinponeo visual super molesto. Como quien dice, parecía que la página se estancaba en este vaivén super rápido, y encima no permitía bajar, pues cada vez que bajaba, la página me devolvía a la parte superior de la misma.

La investigación:

Estuve depurando los datos por un rato en la consola de Chrome, y lo único que podía entender era que en algún punto, cuando el evento del scroll vertical superaba la posición que yo le daba para transformar el menú amplio por el reducido, efectivamente realizaba la reducción, pero de inmediato, por sí solo, volvía a moverse hacia arriba, a la posición 30px, por lo que volvía a mostrar el menú amplio, y eso lo repetía unas veinte veces hasta parar. Esos 30px que se repetían me hizo pensar que el problema tenía que ver con las medidas. Por eso entré a modificar mi función, mi algoritmo, incluso redondeé los valores, pero nada, no resultaba.

Investigué, y encontré mi pregunta en el sitio de StackOverflow (Toggling div position:fixed sometimes causes scrolling bug). Si bien había varias respuestas, e incluso una de ellas dada por correcta, todo estaba en inglés, y reconozco que en viernes por la tarde mi cerebro está medio fundido como para traducir bien, jajaja, así que con suerte logré entender la respuesta correcta, y no me convenció su solución (hablaban de crear una DIV que sirviera de referencia para el evento scroll, una DIV que no cambiara de posición, pues siempre iba a estar antes que el menú, por lo que el evento no causaría estragos. Pero esa solución de poner otro DIV antes de los míos no me gustaba.). 

Seguí leyendo las demás respuestas, pero ya en modo lectura rápida, y fue así que en una de ellas creí que entrever que hablaba sobre el "alto del documento". Y ahí me vino el "ting!" jajaja! Lo entendí: lo que estaba pasando era que el documento de mi página era modificado cada vez que yo alternaba entre el menú amplio y el reducido, y por eso el scroll se volvía loco. Ahora lo explico.

El documento de una página web tiene un alto. Esto suena tan obvio que ni siquiera había pensado que esto era así. O sea, siempre manejamos el ancho de nuestra página y de los elementos en ella, e incluso el alto de estos elementos, pero nunca nos preocupamos mucho del alto de la página en sí.

Pues bien, este alto existe, y en este caso, estaba medido en pixels. Pixels que se veían modificados cada vez que yo le cambiaba el alto a mi menú superior.


/* CSS de mi menú amplio (por defecto, al ver la página en su parte superior): */
#menu
{
  height:102px;
}

/* CSS de mi menú reducido (al desplazarnos hacia abajo en la página): */
#menu.reducido
{
  position:fixed;
  top:0;
  height:60px;
}

 

La solución: 

Agregar un <DIV> en el pie de página (generalmente llamado footer), que actuará como regulador del alto de la página. Para este elemento no necesitamos generar estilos (CSS), sólo necesitamos identificarlo.

  <!-- Esto sería el pie de página o Footer -->
  <div id="footer">
   <!-- Aca el contenido de nuestro footer -->
   <div id="ajuste"></div>
  </div>
 </body>
</html>

Luego, en el mismo Javascript donde tenemos nuestra función que realiza la conversión entre el menú amplio y el reducido, agregamos el "ajuste" de alto:

function alternarMenu()
{
  var menu = document.getElementById("menu");
  var ajuste = document.getElementById("ajuste");
  var altoMinimo = 60;

  if (window.scrollY > altoMinimo)
  {
    menu.className = "reducido";
    ajuste.style.height = "42px";
  }
  else if (
window.scrollY < altoMinimo)
  {
    menu.className = "";
    ajuste.style.height = "0";
  }
}
window.addEventListener("scroll", alternarMenu);


Este código, por supuesto, es una super mega simplificación del código real, pero es para que se entienda que en el mismo momento en que le ordenamos al menú cambiar su estilo (o clase), al mismo tiempo debemos alterar el alto del DIV de ajuste, de modo que nuestro alto de documento no varíe. Eso es lo más importante. De ese modo, "engañamos" al scroll, haciéndole creer que nada ha cambiado y que puede proceder como corresponde con nuestro menú cambiante ^_^

Y pues eso. Esa fue la solución que encontré, y que me puso muy contenta, jejeje.
Espero que les sirva también a ustedes.

Gracias por leer, saludos y que tengan buena programación! ^_^

Comentarios

Entradas populares de este blog

[tsql] Error: La instrucción INSERT EXEC no se puede anidar

Holas a todos. Mientras programaba un procedimiento almacenado, intenté obtener los datos de otro procedimiento, como lo he venido haciendo desde que descubrí tamaña maravilla de la programación sql. Pero hoy me topé con este extraño error: La instrucción INSERT EXEC no se puede anidar . Tras investigar por algunos lados, di con la respuesta: no se puede almacenar en una tabla temporal de procedimiento almacenado, el resultado de otro procedimiento que también esté realizando una inserción de este tipo. Esto es algo como tener: CREATE PROCEDURE miProcedimiento AS  INSERT INTO #tablita EXEC otroProcedimiento;  SELECT * FROM #tablita; END; CREATE PROCEDURE nuevoProcedimiento AS  INSERT INTO #tabla1 EXEC miProcedimiento; END; Esto significará que si ejecuto: EXEC nuevoProcedimiento; ...SQL me arrojará el error antes mencionado. La solución al problema es no llamar a un procedimiento que esté llamando a otro ya en su interior. En algunos lados leí que transf

[mysql] Pasar array a parámetro de procedimiento almacenado (Mysql)

Me tocó hacer una consulta que retornaba una lista de items relacionados con una lista de usuarios que podían o no tener registros en común (vale decir, tabla de quiebre). La lista debía retornar siempre la lista de items, independiente de si había usuarios por los cuales consultar y/o si los usuarios tenían relación con ellos, pero debía mostrarme el status de los usuarios por cada item, de haberlos, esto es, una lista de nombres con una columna que podía estar vacía o no. Para el caso de tener que consultar los items relacionados con usuarios, al hacer la consulta utilizando un LEFT JOIN, me daba resultados si los usuarios tenían relación con los ítems, pero no si los usuarios no tenían items asociados pues, obviamente, al no estar relacionados, la consulta retorna vacío. Por ello, la solución era hacer la consulta de los items primero, y luego por cada item preguntar el status del usuario por cada uno. Para ello, tenía dos alternativas: hacerlo por programación o hacerlo por bas

[php] NuSOAP HTTP Error: socket read of headers timed out

Holas a todos. Este es para comentar un problema que he tenido al trabajar un servicio web montado en PHP con la clase NuSOAP. El problema surgió cuando intenté llamar al servicio web desde el otro servidor, pero se caía a los exactos 30 segundos de ejecución, mostrando el mensaje que titula este registro: HTTP Error: socket read of headers timed out Sabía que el problema era el timeout, pero ¿el timeout de qué? En los servidores y páginas web hay timeouts por todos lados: el de la Conexión a internet o la red, el del Servidor (hardware), el del Servidor Web en sí (Apache, mi caso), el de PHP (mi caso)... Pero nunca se me habría ocurrido que las Aplicaciones o frameworks también pudieran tener :o Por eso, tras buscar por la red la solución a mi problema, la respuesta vino precisamente de alguien que señaló sencillamente que había que modificar el timeout de la clase NuSOAP. Y dicho y hecho, eso solucionó el problema. Si están usando en su servidor y/o cliente la clase NuSOAP, y d