¿Cómo evitar el desplazamiento de página cuando se desplaza un elemento DIV?

Revisé y probé varias funciones para evitar que el cuerpo se desplazara mientras estaba dentro de un div y combiné una función que debería funcionar.

$('.scrollable').mouseenter(function() { $('body').bind('mousewheel DOMMouseScroll', function() { return false; }); $(this).bind('mousewheel DOMMouseScroll', function() { return true; }); }); $('.scrollable').mouseleave(function() { $('body').bind('mousewheel DOMMouseScroll', function() { return true; }); }); 
  • Esto está deteniendo todo el desplazamiento donde todavía es posible desplazarse dentro del contenedor
  • Esto tampoco se desactiva al dejar el mouse

¿Alguna idea o mejor forma de hacer esto?

Actualización 2: mi solución se basa en deshabilitar el desplazamiento nativo del navegador (cuando el cursor está dentro del DIV) y luego desplazar manualmente el DIV con JavaScript (estableciendo su propiedad .scrollTop ). Un enfoque alternativo e IMO mejor sería desactivar selectivamente el desplazamiento del navegador para evitar el desplazamiento de la página, pero no el desplazamiento DIV. Vea la respuesta de Rudie a continuación que demuestra esta solución.


Aqui tienes:

 $( '.scrollable' ).on( 'mousewheel DOMMouseScroll', function ( e ) { var e0 = e.originalEvent, delta = e0.wheelDelta || -e0.detail; this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30; e.preventDefault(); }); 

Demostración en vivo: https://jsbin.com/howojuq/edit?js,output

Así que configura manualmente la posición de desplazamiento y luego evita el comportamiento predeterminado (que sería desplazar el DIV o la página web completa).

Actualización 1: Como señaló Chris en los comentarios a continuación, en las versiones más recientes de jQuery, la información delta está anidada dentro del objeto .originalEvent , es decir, jQuery ya no lo expone en su objeto Event personalizado y tenemos que recuperarlo del evento nativo. objeto en su lugar.

Si no le importa la compatibilidad con las versiones anteriores de IE (<8), puede crear un complemento jQuery personalizado y luego llamarlo al elemento desbordado.

Esta solución tiene una ventaja sobre el propuesto por Šime Vidas, ya que no sobrescribe el comportamiento de desplazamiento, simplemente lo bloquea cuando es apropiado.

 $.fn.isolatedScroll = function() { this.bind('mousewheel DOMMouseScroll', function (e) { var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail, bottomOverflow = this.scrollTop + $(this).outerHeight() - this.scrollHeight >= 0, topOverflow = this.scrollTop <= 0; if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) { e.preventDefault(); } }); return this; }; $('.scrollable').isolatedScroll(); 

Creo que es posible cancelar el evento mousescroll a veces: http://jsfiddle.net/rudiedirkx/F8qSq/show/

 $elem.on('wheel', function(e) { var d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down', stop = (dir == 'up' && this.scrollTop == 0) || (dir == 'down' && this.scrollTop == this.scrollHeight-this.offsetHeight); stop && e.preventDefault(); }); 

Dentro del controlador de eventos, necesitarás saber:

  • dirección de desplazamiento
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' porque un número positivo significa desplazarse hacia abajo
  • posición de desplazamiento
    scrollTop para arriba, scrollHeight - scrollTop - offsetHeight para abajo

Si eres

  • desplazarse hacia arriba, y top = 0 , o
  • desplazarse hacia abajo, e bottom = 0 ,

cancelar el evento: e.preventDefault() (y tal vez incluso e.stopPropagation() ).

Creo que es mejor no anular el comportamiento de desplazamiento del navegador. Solo cancélelo cuando corresponda.

Probablemente no sea perfectamente xbrowser, pero no puede ser muy difícil. Sin embargo, tal vez la dirección de desplazamiento dual de Mac es complicada ...

mira si esto te ayuda:

demo: jsfiddle

 $('#notscroll').bind('mousewheel', function() { return false }); 

editar:

prueba esto:

  $("body").delegate("div.scrollable","mouseover mouseout", function(e){ if(e.type === "mouseover"){ $('body').bind('mousewheel',function(){ return false; }); }else if(e.type === "mouseout"){ $('body').bind('mousewheel',function(){ return true; }); } }); 

Una solución menos hacky, en mi opinión es establecer el desbordamiento oculto en el cuerpo cuando pasas el mouse sobre el div desplazable. Esto evitará que el cuerpo se desplace, pero se producirá un efecto de “salto” no deseado. La siguiente solución funciona alrededor de eso:

 jQuery(".scrollable") .mouseenter(function(e) { // get body width now var body_width = jQuery("body").width(); // set overflow hidden on body. this will prevent it scrolling jQuery("body").css("overflow", "hidden"); // get new body width. no scrollbar now, so it will be bigger var new_body_width = jQuery("body").width(); // set the difference between new width and old width as padding to prevent jumps jQuery("body").css("padding-right", (new_body_width - body_width)+"px"); }) .mouseleave(function(e) { jQuery("body").css({ overflow: "auto", padding-right: "0px" }); }) 

Podría hacer que su código sea más inteligente si es necesario. Por ejemplo, puede probar si el cuerpo ya tiene un relleno y si es así, agregue el nuevo relleno a eso.

En la solución anterior, hay un pequeño error con respecto a Firefox. En Firefox, el evento “DOMMouseScroll” no tiene propiedad e.detail, para obtener esta propiedad debe escribir lo siguiente ‘e.originalEvent.detail’.

Aquí hay una solución funcional para Firefox:

 $.fn.isolatedScroll = function() { this.on('mousewheel DOMMouseScroll', function (e) { var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.originalEvent.detail, bottomOverflow = (this.scrollTop + $(this).outerHeight() - this.scrollHeight) >= 0, topOverflow = this.scrollTop <= 0; if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) { e.preventDefault(); } }); return this; }; 

aquí una solución simple sin jQuery que no destruye el desplazamiento nativo del navegador (esto es: sin desplazamiento artificial / feo):

 var scrollable = document.querySelector('.scrollable'); scrollable.addEventListener('wheel', function(event) { var deltaY = event.deltaY; var contentHeight = scrollable.scrollHeight; var visibleHeight = scrollable.offsetHeight; var scrollTop = scrollable.scrollTop; if (scrollTop === 0 && deltaY < 0) event.preventDefault(); else if (visibleHeight + scrollTop === contentHeight && deltaY > 0) event.preventDefault(); }); 

Demostración en vivo: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/

Aquí está mi solución que he usado en aplicaciones.

Inhabilité el desbordamiento del cuerpo y coloqué todo el sitio web html dentro del contenedor div. Los contenedores del sitio web se han desbordado y, por lo tanto, el usuario puede desplazarse por la página como se esperaba.

Luego creé un div hermano (#Prevent) con un índice z más alto que cubre todo el sitio web. Como #Prevent tiene un índice z más alto, se superpone al contenedor del sitio web. Cuando #Prevent está visible, el mouse ya no está sobrevolando los contenedores del sitio web, por lo que no es posible desplazarse.

Por supuesto, puede colocar otro div, como su modal, con un índice z más alto que #Prevent en el marcado. Esto le permite crear ventanas emergentes que no sufren problemas de desplazamiento.

Esta solución es mejor porque no oculta las barras de desplazamiento (efecto de salto). No requiere oyentes de eventos y es fácil de implementar. Funciona en todos los navegadores, aunque con IE7 y 8 tienes que jugar (depende de tu código específico).

html

    
website goes here...

css

 body { overflow: hidden; } #YourModal { z-index:200; /* modal styles here */ } #Prevent { z-index:100; position:absolute; left:0px; height:100%; width:100%; background:transparent; } #WebsiteContainer { z-index:50; overflow:auto; position: absolute; height:100%; width:100%; } #Website { position:relative; } 

jquery / js

 function PreventScroll(A) { switch (A) { case 'on': $('#Prevent').show(); break; case 'off': $('#Prevent').hide(); break; } } 

desactivar / habilitar el desplazamiento

 PreventScroll('on'); // prevent scrolling PreventScroll('off'); // allow scrolling 

Necesitaba agregar este evento a múltiples elementos que podrían tener una barra de desplazamiento. Para los casos en que no haya una barra de desplazamiento presente, la barra de desplazamiento principal no funcionó como debería. Así que hice un pequeño cambio al código @ Šime de la siguiente manera:

 $( '.scrollable' ).on( 'mousewheel DOMMouseScroll', function ( e ) { if($(this).prop('scrollHeight') > $(this).height()) { var e0 = e.originalEvent, delta = e0.wheelDelta || -e0.detail; this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30; e.preventDefault(); } }); 

Ahora, solo los elementos con una barra de desplazamiento evitarán que el desplazamiento principal comience detenido.

Versión pura de javascript de la respuesta de Vidas, el$ es el nodo DOM del plano que estás desplazando.

 function onlyScrollElement(event, el$) { var delta = event.wheelDelta || -event.detail; el$.scrollTop += (delta < 0 ? 1 : -1) * 10; event.preventDefault(); } 

¡Asegúrate de no unir el par incluso varias veces! Aquí hay un ejemplo,

 var ul$ = document.getElementById('yo-list'); // IE9, Chrome, Safari, Opera ul$.removeEventListener('mousewheel', onlyScrollElement); ul$.addEventListener('mousewheel', onlyScrollElement); // Firefox ul$.removeEventListener('DOMMouseScroll', onlyScrollElement); ul$.addEventListener('DOMMouseScroll', onlyScrollElement); 

Es obvio que la función debe ser constante, si reinicia la función cada vez antes de conectarla, es decir. var func = function (...) removeEventListener no funcionará.

Puedes hacer esto sin JavaScript. Puede establecer el estilo en ambos divs en la position: fixed y overflow-y: auto . Puede necesitar hacer uno de ellos más alto que el otro estableciendo su z-index (si se superponen).

Aquí hay un ejemplo básico en CodePen .