Evitar el desplazamiento de página cuando un modal está abierto | Programar Plus

Por favor, deténgame si ha escuchado esto antes. Abres un modal, te desplazas por él, lo cierras y terminas en otro lugar de la página que cuando abriste el modal.

Eso es porque los modales son elementos en una página como cualquier otra. Puede permanecer en su lugar (asumiendo que eso es lo que debe hacer) pero el resto de la página continúa comportándose normalmente.

Ver la pluma
Evite el desplazamiento del cuerpo en safari cuando el diálogo modal lo muestra Geoff Graham (@geoffgraham)
en CodePen.

A veces, esto no es un problema, como las pantallas que tienen la altura exacta de la ventana gráfica. Sin embargo, cualquier otra cosa, estamos mirando a Scroll City. La buena noticia es que podemos evitarlo con una pizca de trucos CSS (y JavaScript).

Comencemos con algo simple

Podemos hacer una gran mella en open-modal-page-scrolling ™ configurando la altura de todo el cuerpo a la altura total de la ventana y ocultando el desbordamiento vertical cuando el modal está abierto:

body.modal-open {
  height: 100vh;
  overflow-y: hidden;
}

Eso es bueno y todo, pero si nos desplazamos por el <body> elemento antes de abrir el modal, obtenemos un pequeño reflujo horizontal. El ancho de la ventana gráfica se amplía unos 15 píxeles más, que es exactamente el de la barra de desplazamiento.

Ver la pluma
Evite el desplazamiento del cuerpo en safari cuando el diálogo modal lo muestra Geoff Graham (@geoffgraham)
en CodePen.

Ajustemos un poco el acolchado correcto del cuerpo para evitar eso.

body {
  height: 100vh;
  overflow-y: hidden;
  padding-right: 15px; /* Avoid width reflow */
}

Tenga en cuenta que el modal debe ser más corto que la altura de la ventana gráfica para que esto funcione. De lo contrario, será necesaria la barra de desplazamiento en el cuerpo.

Genial, ahora ¿qué pasa con los dispositivos móviles?

Esta solución funciona bastante bien en el escritorio y en Android Mobile. Dicho esto, Safari para iOS necesita un poco más de amor porque el cuerpo aún se desplaza cuando un modal está abierto al tocar y moverse por la pantalla táctil.

Podemos establecer el cuerpo en una posición fija como solución:

body {
  position: fixed;
}

¡Funciona ahora! El cuerpo no responderá cuando se toque la pantalla. Sin embargo, todavía hay un “pequeño” problema aquí. Digamos que el disparador modal está más abajo en la página y hacemos clic para abrirlo. ¡Estupendo! Pero ahora volvemos a desplazarnos automáticamente a la parte superior de la pantalla, lo que es tan desorientador como el comportamiento de desplazamiento que estamos tratando de resolver.

Ver la pluma
Evite el desplazamiento del cuerpo en safari cuando el diálogo modal lo muestra Geoff Graham (@geoffgraham)
en CodePen.

¡Abucheo!

Por eso tenemos que recurrir a JavaScript.

Podemos usar JavaScript para evitar la burbuja de evento táctil. Todos sabemos que debería haber una capa de fondo cuando un modal está abierto. Desafortunadamente, stopPropagation es un poco incómodo con la función táctil en iOS. Pero preventDefault funciona bien. Eso significa que tenemos que agregar detectores de eventos en cada nodo DOM contenido en el modal, no solo en el fondo o en la capa del cuadro modal. La buena noticia es que muchas bibliotecas de JavaScript pueden hacer esto, incluida la buena jQuery.

Ah, y una cosa más: ¿Qué pasa si necesitamos desplazarnos dentro del modal? Todavía tenemos que activar una respuesta para un evento táctil, pero al llegar a la parte superior o inferior del modal, aún debemos evitar el burbujeo. Parece muy complejo, por lo que no estamos totalmente fuera de peligro aquí.

Mejoremos el enfoque de cuerpo fijo

Esto es con lo que estábamos trabajando:

body {
  position: fixed;
}

Si conocemos la parte superior de la ubicación de desplazamiento y la agregamos a nuestro CSS, entonces el cuerpo no se desplazará hacia la parte superior de la pantalla, por lo que el problema está resuelto. Podemos usar JavaScript para esto calculando la parte superior del desplazamiento y agregando ese valor a los estilos de cuerpo:

// When the modal is shown, we want a fixed body
document.body.style.position = 'fixed';
document.body.style.top = `-${window.scrollY}px`;

// When the modal is hidden, we want to remain at the top of the scroll position
document.body.style.position = '';
document.body.style.top = '';

Esto funciona, pero todavía hay una pequeña fuga aquí después de que se cierra el modal. Específicamente, parece que la página ya pierde su posición de desplazamiento cuando el modal está abierto y el cuerpo configurado para ser fijo. Entonces tenemos que recuperar la ubicación. Modifiquemos nuestro JavaScript para tener en cuenta eso.

// When the modal is hidden...
const scrollY = document.body.style.top;
document.body.style.position = '';
document.body.style.top = '';
window.scrollTo(0, parseInt(scrollY || '0') * -1);

¡Eso lo hace! El cuerpo ya no se desplaza cuando un modal está abierto y la ubicación de desplazamiento se mantiene tanto cuando el modal está abierto como cuando está cerrado. ¡Hurra!

Ver la pluma
Evite el desplazamiento del cuerpo en safari cuando el diálogo modal lo muestra Geoff Graham (@geoffgraham)
en CodePen.