Scrolling child div desplaza la ventana, ¿cómo lo detengo?

Respuestas:

68

Puede desactivar el desplazamiento de toda la página haciendo algo como esto:

<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>
Teemu
fuente
33
Bien, pero hace que la barra de desplazamiento del navegador desaparezca cada vez que pasas el cursor sobre el div.
cstack
@cstack, tienes razón, por eso la respuesta de OP (Jeevan) es mejor.
Imran Bughio
2
Muchas ruedas de desplazamiento del navegador desaparecen y reaparecen en función de que el usuario mueva el mouse ahora, por lo que el comentario anterior ya no es un problema.
Keith Holliday
Sin embargo, esta solución no funciona cuando la página está incrustada en un iframe.
Gautam Krishnan
5
no es una solución. establecer el desbordamiento en oculto oculta la barra de desplazamiento que de repente reformatea todo el contenido del elemento exterior. también, esto asume que el elemento externo es body pero podría ser otro div de desplazamiento.
robisrob
43

Encontré la solución.

http://jsbin.com/itajok

Esto es lo que necesitaba.

Y este es el código.

http://jsbin.com/itajok/edit#javascript,html

Utiliza un complemento de jQuery.


Actualización por aviso de baja

Desde jquery-mousewheel :

El comportamiento anterior de añadir tres argumentos ( delta, deltaXy deltaY) al controlador de eventos ya no se utiliza y se eliminará en versiones posteriores.

Entonces, event.deltaYahora debe usarse:

var toolbox = $('#toolbox'),
    height = toolbox.height(),
    scrollHeight = toolbox.get(0).scrollHeight;

toolbox.off("mousewheel").on("mousewheel", function (event) {
  var blockScrolling = this.scrollTop === scrollHeight - height && event.deltaY < 0 || this.scrollTop === 0 && event.deltaY > 0;
  return !blockScrolling;
});

Manifestación

Jeevan
fuente
6
Su enlace usa github para js y se rompe porque el tipo de contenido bla bla bla aquí funciona: jsbin.com/itajok/207
Michael J. Calkins
Ambos jsbins no parecen funcionar en el emulador de dispositivos Chrome y en el iPhone. ¿Alguien sabe si esto todavía funciona?
Dirk Boer
2
Su "solución" solo funciona para eventos de desplazamiento del mouse, no para subir / bajar página o teclas de flecha.
Scott
¿Por qué se requiere "apagado" aquí?
Brad Johnson
16

La solución seleccionada es una obra de arte. Pensé que era digno de un complemento ...

$.fn.scrollGuard = function() {
    return this
        .on( 'wheel', function ( e ) {
            var event = e.originalEvent;
            var d = event.wheelDelta || -event.detail;
            this.scrollTop += ( d < 0 ? 1 : -1 ) * 30;
            e.preventDefault();
        });
};    

Este ha sido un inconveniente continuo para mí y esta solución es tan limpia en comparación con otros trucos que he visto. Curioso por saber más sobre cómo funciona y qué tan ampliamente apoyado sería, pero saluda a Jeevan y a quienquiera que originalmente se le ocurrió esto. Por cierto, ¡el editor de respuestas de stackoverflow necesita esto!

ACTUALIZAR

Creo que esto es mejor porque no intenta manipular el DOM en absoluto, solo evita el burbujeo condicionalmente ...

$.fn.scrollGuard2 = function() {
  return this
    .on( 'wheel', function ( e ) {
      var $this = $(this);
      if (e.originalEvent.deltaY < 0) {
        /* scrolling up */
        return ($this.scrollTop() > 0);
      } else {
        /* scrolling down */
        return ($this.scrollTop() + $this.innerHeight() < $this[0].scrollHeight);
      }
    })
  ;
};    

Funciona muy bien en Chrome y mucho más simple que otras soluciones ... avíseme cómo le va en otros lugares ...

VIOLÍN

robisrob
fuente
3
agregar el evento DOMMouseScroll hace que funcione con Firefox → jsfiddle.net/chodorowicz/egqy7mbz/1
chodorowicz
Me sorprende que esto no esté escrito en jquery. Pensé que uno de los puntos principales era no tener que preocuparse por qué navegador estás usando.
robisrob
en realidad parece que ambos están en desuso ... wheelevento
robisrob
Esto da como resultado un desplazamiento notablemente más desigual en Chrome en mi Macbook. La solución aceptada no tiene este problema.
danvk
decepcionante. hay tantas versiones diferentes de esto. parece un aspecto importante de la interfaz de usuario, y realmente es sorprendente que este sea el comportamiento predeterminado cuando es tan poco intuitivo. Obviamente, el navegador está equipado para saber que el div de desplazamiento interno está en su límite para poder aplicar el evento a la ventana, entonces, ¿por qué no hay controladores disponibles para consultarlo?
robisrob
8

¿Podría usar un evento de mouseover en el div para deshabilitar la barra de desplazamiento del cuerpo y luego un evento de mouseout para activarlo nuevamente?

Por ejemplo, el HTML

<div onmouseover="disableBodyScroll();" onmouseout="enableBodyScroll();">
    content
</div>

Y luego el javascript así:

var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
    body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
    body.style.overflowY = 'auto';
}
joe92
fuente
12
También puede usar document.bodyen su lugar.
Ry-
8

Aquí hay una forma de hacer esto en varios navegadores en el eje Y, funciona en computadoras de escritorio y dispositivos móviles. Probado en OSX e iOS.

var scrollArea = this.querySelector(".scroll-area");
scrollArea.addEventListener("wheel", function() {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var deltaY = event.deltaY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
}, {passive:false});

scrollArea.addEventListener("touchstart", function(event) {
    this.previousClientY = event.touches[0].clientY;
}, {passive:false});

scrollArea.addEventListener("touchmove", function(event) {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var currentClientY = event.touches[0].clientY;
    var deltaY = this.previousClientY - currentClientY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
    this.previousClientY = currentClientY;
}, {passive:false});
Patrick Matte
fuente
¡Esto funciona increíble! Vaya, exactamente lo que busqué. Definitivamente necesita más votos a favor.
delato468
Esta solución funciona bastante bien, pero necesita un pequeño ajuste: es necesario cambiar el scrollTop === maxScroll by scrollTop >= maxScroll. Parece ser extraño en 1 caso, era necesario
tchiot.ludo
7

Escribí resolviendo este problema

  var div;
  div = document.getElementsByClassName('selector')[0];

  div.addEventListener('mousewheel', function(e) {
    if (div.clientHeight + div.scrollTop + e.deltaY >= div.scrollHeight) {
      e.preventDefault();
      div.scrollTop = div.scrollHeight;
    } else if (div.scrollTop + e.deltaY <= 0) {
      e.preventDefault();
      div.scrollTop = 0;
    }
  }, false);
Alex Golovin
fuente
Una de las pocas soluciones aquí que funciona sin que los elementos de la página salten molestamente debido al desbordamiento oculto.
Escéptico
+1 esta es una solución sólida. si adjunta esto a un contenedor desplazable, querrá que div en este ejemplo sea e.currentTarget.
Matt
solución muy limpia. Gracias
Thinh Bui
Funcionó en Chrome 61.0.3163.100 y Safari 11.0, pero no en FireFox 56.0 (Mac OS El Capitan)
BigJ
Esta es la mejor solución nativa. Sin embargo, falseno es necesario especificarlo, addEventListenerya que es el valor predeterminado. Además, las comparaciones deben ser simples desigualdades ( >y <), no >=o <=.
Preguntas de Quolonel
6

Si entiendo su pregunta correctamente, entonces desea evitar el desplazamiento del contenido principal cuando el mouse está sobre un div (digamos una barra lateral). Para eso, la barra lateral puede no ser un elemento secundario del contenedor de desplazamiento del contenido principal (que era la ventana del navegador), para evitar que el evento de desplazamiento se propague a su elemento principal.

Esto posiblemente requiera algunos cambios de marcado de la siguiente manera:

<div id="wrapper"> 
    <div id="content"> 
    </div> 
</div> 
<div id="sidebar"> 
</div> 

Vea que está funcionando en este violín de muestra y compárelo con este violín de muestra que tiene un comportamiento de salida del mouse ligeramente diferente de la barra lateral.

Consulte también desplazar solo un div en particular con la barra de desplazamiento principal del navegador .

NGLN
fuente
En este caso, la barra lateral está fuera del div contenedor. Así que no creo que el pergamino llegue al padre
Jeevan
¿Por "este caso" te refieres a tu caso? Si no, tienes toda la razón. Si es así: si su página se desplaza al final del desplazamiento div, entonces el mensaje de desplazamiento aparece en el div padre, así que creo que su página y div no son hermanos, sino padre e hijo.
NGLN
Sí, Page es el padre de mi div, así que al llegar al final, comienza a desplazarse por la página, lo cual no quiero.
Jeevan
Si no puede hacer que su div sea un hermano del contenido principal, entonces este problema no se puede solucionar solo con HTML + CSS. Es un comportamiento predeterminado.
NGLN
1
Creo que esta es la solución más eficiente de todas las respuestas proporcionadas; con un buen marcado limpio en la base de una página, se requieren muchos menos hacks de CSS y JS para obtener el comportamiento deseado.
6

Como se respondió aquí , la mayoría de los navegadores modernos ahora admiten la overscroll-behavior: none;propiedad CSS, que evita el encadenamiento de desplazamiento. Y eso es todo, ¡solo una línea!

Andriy Stolyar
fuente
2
Esta debería ser la respuesta correcta en 2020. Para mi caso de uso, la mejor solución fue overscroll-behavior: contain;.
Muelle
3

esto deshabilita el desplazamiento en la ventana si ingresa el elemento selector. funciona como amuletos.

elements = $(".selector");

elements.on('mouseenter', function() {
    window.currentScrollTop = $(window).scrollTop();
    window.currentScrollLeft = $(window).scrollTop();
    $(window).on("scroll.prevent", function() {
        $(window).scrollTop(window.currentScrollTop);
        $(window).scrollLeft(window.currentScrollLeft);
    });
});

elements.on('mouseleave', function() {
    $(window).off("scroll.prevent");
});
ceed
fuente
De hecho, esta es una solución funcional. Lo necesitaba en js puro, así que lo reescribí. Si alguien lo necesita, está aquí . Para cualquiera que todavía esté buscando la solución, debe consultar mi respuesta a continuación .
Andriy Stolyar
2

Puede desactivar el desplazamiento de toda la página haciendo algo como esto, ¡pero mostrando la barra de desplazamiento!

<div onmouseover="document.body.style.overflow='hidden'; document.body.style.position='fixed';" onmouseout="document.body.style.overflow='auto'; document.body.style.position='relative';"></div>
joseantgv
fuente
2
$this.find('.scrollingDiv').on('mousewheel DOMMouseScroll', function (e) {
  var delta = -e.originalEvent.wheelDelta || e.originalEvent.detail;
  var scrollTop = this.scrollTop;
  if((delta < 0 && scrollTop === 0) || (delta > 0 && this.scrollHeight - this.clientHeight - scrollTop === 0)) {
    e.preventDefault();
  }
});
Neil Taylor
fuente
1

Basado en la respuesta de ceed, aquí hay una versión que permite anidar elementos protegidos por desplazamiento. Solo el elemento sobre el que se encuentra el mouse se desplazará y se desplazará con bastante suavidad. Esta versión también es reentrante. Se puede usar varias veces en el mismo elemento y eliminará y reinstalará correctamente los controladores.

jQuery.fn.scrollGuard = function() {
    this
        .addClass('scroll-guarding')
        .off('.scrollGuard').on('mouseenter.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g[0].myCst = $g.scrollTop();
            $g[0].myCsl = $g.scrollLeft();
            $g.off("scroll.prevent").on("scroll.prevent", function() {
                $g.scrollTop($g[0].myCst);
                $g.scrollLeft($g[0].myCsl);
            });
        })
        .on('mouseleave.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g.off("scroll.prevent");
        });
};

Una forma fácil de usar es agregar una clase, por ejemplo scroll-guard, a todos los elementos de la página en los que permite desplazarse. Luego úsalos $('.scroll-guard').scrollGuard()para protegerlos.

d'Artagnan Evergreen Barbosa
fuente
0

Si aplicas un overflow: hiddenestilo, debería desaparecer.

editar: de hecho, leí mal tu pregunta, eso solo ocultará la barra de desplazamiento, pero no creo que eso sea lo que estás buscando.

JavaKungFu
fuente
1
Necesito tanto la barra de desplazamiento. Estoy hablando del comportamiento. Cuando me desplazo con la bola de seguimiento del mouse al llegar al final del desplazamiento del div, no debe desplazarse por toda la página.
Jeevan
0

No pude hacer que ninguna de las respuestas funcionara en Chrome y Firefox, así que se me ocurrió esta fusión:

$someElement.on('mousewheel DOMMouseScroll', scrollProtection);

function scrollProtection(event) {
    var $this = $(this);
    event = event.originalEvent;
    var direction = (event.wheelDelta * -1) || (event.detail);
    if (direction < 0) {
        if ($this.scrollTop() <= 0) {
            return false;
        }
    } else {
        if ($this.scrollTop() + $this.innerHeight() >= $this[0].scrollHeight) {
            return false;
        }
    }
}
d.breve
fuente