Przewijanie elementu div podrzędnego przewija okno, jak to zatrzymać?

Odpowiedzi:

68

Możesz wyłączyć przewijanie całej strony, wykonując coś takiego:

<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>
Teemu
źródło
33
Dobrze, ale sprawia, że ​​pasek przewijania przeglądarki znika po najechaniu kursorem na element div.
cstack
@cstack masz rację, dlatego własna odpowiedź OP (Jeevan) jest lepsza.
Imran Bughio,
2
Wiele kółek przewijania przeglądarki znika i pojawia się ponownie, gdy użytkownik porusza teraz myszą, więc powyższy komentarz nie jest już problemem.
Keith Holliday
To rozwiązanie nie działa jednak, gdy strona jest osadzona w elemencie iframe.
Gautam Krishnan
5
nie jest rozwiązaniem. ustawienie przepełnienia na ukryte powoduje ukrycie paska przewijania, który nagle zmienia formatowanie całej zawartości elementu zewnętrznego. również przy założeniu, że zewnętrzny element to body, ale może to być inny przewijany element div.
robisrob
43

Znalazłem rozwiązanie.

http://jsbin.com/itajok

To jest to, czego potrzebowałem.

A to jest kod.

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

Używa wtyczki jQuery.


Aktualizacja z powodu powiadomienia o wycofaniu

Z jquery-mousewheel :

Stare zachowanie polegające na dodawaniu trzech argumentów ( delta, deltaXi deltaY) do programu obsługi zdarzeń jest obecnie przestarzałe i zostanie usunięte w późniejszych wersjach.

Następnie event.deltaYnależy teraz użyć:

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;
});

Próbny

Jeevan
źródło
6
Twój link używa github dla js i breaks, ponieważ rodzaj treści blah blah tutaj działa: jsbin.com/itajok/207
Michael J. Calkins
Wydaje się, że oba jsbins nie działają w emulatorze urządzenia Chrome i na iPhonie. Czy ktoś wie, czy to nadal działa?
Dirk Boer,
2
Twoje „rozwiązanie” działa tylko dla zdarzeń przewijania myszy, a nie dla klawiszy przewijania w górę / w dół lub klawiszy strzałek.
Scott
Dlaczego tutaj wymagane jest wyłączenie?
Brad Johnson,
16

Wybrane rozwiązanie to dzieło sztuki. Pomyślałem, że zasługuje na wtyczkę ...

$.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();
        });
};    

Jest to dla mnie ciągła niedogodność, a to rozwiązanie jest tak czyste w porównaniu z innymi hackami, które widziałem. Ciekawe, jak więcej o tym, jak to działa i jak szeroko byłoby to wspierane, ale wiwatuje Jeevanowi i temu, kto to wymyślił. BTW - edytor odpowiedzi stackoverflow tego potrzebuje!

AKTUALIZACJA

Uważam, że jest to lepsze, ponieważ w ogóle nie próbuje manipulować DOM, a jedynie zapobiega warunkowemu bulgotaniu ...

$.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);
      }
    })
  ;
};    

Świetnie sprawdza się w chrome i dużo prostszy niż inne rozwiązania ... daj mi znać, jak gdzie indziej wypada ...

SKRZYPCE

robisrob
źródło
3
dodanie zdarzenia DOMMouseScroll sprawi, że będzie działać z Firefoksem → jsfiddle.net/chodorowicz/egqy7mbz/1
chodorowicz
Dziwię się, że nie jest to zapisane w jquery. Pomyślałem, że jednym z głównych punktów jest brak dbałości o to, jakiej przeglądarki używasz.
robisrob
faktycznie wygląda na to, że oba są przestarzałe ... wheelwydarzenie
robisrob
Powoduje to zauważalnie szarpane przewijanie w Chrome na moim Macbooku. Przyjęte rozwiązanie nie ma tego problemu.
danvk
niezadowalający. jest tak wiele różnych wersji tego. wydaje się być ważnym aspektem interfejsu użytkownika i naprawdę zaskakujące jest to, że jest to domyślne zachowanie, gdy jest tak nieintuicyjne. Oczywiście przeglądarka jest wyposażona tak, aby wiedzieć, że wewnętrzny przewijany element div jest na granicy, aby móc zamiast tego zastosować zdarzenie do okna, więc dlaczego nie ma dostępnych uchwytów do odpytywania go?
robisrob
8

Możesz użyć zdarzenia mouseover na div, aby wyłączyć pasek przewijania treści, a następnie zdarzenia mouseout, aby go ponownie aktywować?

Np. HTML

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

A potem javascript w ten sposób:

var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
    body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
    body.style.overflowY = 'auto';
}
joe92
źródło
12
Możesz też użyć document.bodyzamiast tego.
Ry-
8

Oto sposób na zrobienie tego na osi Y w różnych przeglądarkach, działa na komputerach stacjonarnych i urządzeniach mobilnych. Przetestowano na OSX i 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
źródło
To działa świetnie! Wow, dokładnie to, czego szukałem. Potrzebuje zdecydowanie więcej głosów pozytywnych.
delato468
To rozwiązanie działa całkiem nieźle, ale wymaga niewielkiej korekty: trzeba zmienić scrollTop === maxScroll przez scrollTop >= maxScroll. Wydaje się to dziwne w 1 przypadku, było potrzebne
tchiot.ludo
7

Napisałem rozwiązanie tego problemu

  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
źródło
Jedno z niewielu rozwiązań, które działa bez irytująco skaczących elementów strony z powodu ustawienia ukrytego przepełnienia.
Sceptic
+1 to solidne rozwiązanie. jeśli dołączysz to do przewijalnego kontenera, w tym przykładzie będzie to div e.currentTarget.
Matt
bardzo czyste rozwiązanie. Dzięki
Thinh Bui,
Działał w Chrome 61.0.3163.100 i Safari 11.0, ale nie w FireFox 56.0 (Mac OS El Capitan)
BigJ
To najlepsze rozwiązanie natywne. Jednak falsenie trzeba go określać, addEventListenerponieważ jest to ustawienie domyślne. Ponadto porównania powinny być prostymi nierównościami ( >i <), a nie >=lub <=.
Pytania dotyczące quolonelów
7

Jak tu udzielono odpowiedzi , większość nowoczesnych przeglądarek obsługuje teraz overscroll-behavior: none;właściwość CSS, która zapobiega tworzeniu łańcuchów przewijania. I to wszystko, tylko jedna linijka!

Andriy Stolyar
źródło
2
To powinna być prawidłowa odpowiedź w 2020 roku. W moim przypadku najlepszym rozwiązaniem była overscroll-behavior: contain;.
Przystań
nie jest to w pełni obsługiwane w niektórych wersjach Edge. masz pomysł, jak rozwiązać ten problem w inny sposób?
YTG
6

Jeśli dobrze rozumiem twoje pytanie, chcesz zapobiec przewijaniu głównej treści, gdy wskaźnik myszy znajduje się nad elementem div (powiedzmy, pasek boczny). W tym celu pasek boczny może nie być elementem podrzędnym kontenera przewijania głównej zawartości (którym było okno przeglądarki), aby zapobiec pojawieniu się zdarzenia przewijania do jego elementu nadrzędnego.

Może to wymagać pewnych zmian w znacznikach w następujący sposób:

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

Zobacz, jak działa na tych przykładowych skrzypcach i porównaj to z tymi przykładowymi skrzypcami które ma nieco inne zachowanie paska bocznego przy opuszczaniu myszy.

Zobacz także przewijanie tylko jednego określonego elementu div za pomocą głównego paska przewijania przeglądarki .

NGLN
źródło
W tym przypadku pasek boczny znajduje się poza opakowującym elementem div. Więc nie sądzę, żeby zwój dotarł do rodzica
Jeevana
Mówiąc „ta sprawa” masz na myśli swoją sprawę? Jeśli nie: masz całkowitą rację. Jeśli tak: jeśli strona przewija się po zakończeniu przewijania elementu div, komunikat przewijania przechodzi do elementu nadrzędnego div, więc myślę, że strona i div nie są rodzeństwem, ale rodzicem i dzieckiem.
NGLN
Tak, Page jest rodzicem mojego div, więc po dotarciu do końca zaczyna przewijać stronę, czego nie chcę.
Jeevan,
Jeśli nie możesz uczynić swojego div jako rodzeństwa głównej treści, tego problemu nie można rozwiązać za pomocą samego HTML + CSS. Jest to zachowanie domyślne.
NGLN
1
Myślę, że jest to najbardziej efektywne rozwiązanie ze wszystkich udzielonych odpowiedzi; z dobrym, czystym znacznikiem u podstawy strony, potrzeba znacznie mniej hacków CSS i JS, aby uzyskać pożądane zachowanie.
3

wyłącza to przewijanie w oknie, jeśli wejdziesz do elementu selektora. działa jak urok.

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
źródło
To rzeczywiście działające rozwiązanie. Potrzebowałem go w czystym js, więc napisałem go ponownie. Jeśli ktoś tego potrzebuje, to jest tutaj . Dla każdego, kto wciąż szuka rozwiązania, sprawdź moją odpowiedź poniżej .
Andriy Stolyar,
2

Możesz wyłączyć przewijanie całej strony, wykonując coś takiego, ale wyświetlając pasek przewijania!

<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
źródło
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
źródło
1

W oparciu o odpowiedź Ceeda, oto wersja, która pozwala na zagnieżdżanie chronionych elementów przewijania. Tylko element, nad którym znajduje się mysz, będzie się przewijał i przewija się całkiem płynnie. Ta wersja jest również ponownie wprowadzana. Może być używany wiele razy na tym samym elemencie i poprawnie usunie i ponownie zainstaluje uchwyty.

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");
        });
};

Jednym prostym sposobem użycia jest dodanie klasy, na przykład scroll-guard, do wszystkich elementów na stronie, na których możesz przewijać. Następnie użyj, $('.scroll-guard').scrollGuard()aby ich chronić.

d'Artagnan Evergreen Barbosa
źródło
0

Jeśli zastosujesz overflow: hiddenstyl, powinien zniknąć

edycja: właściwie źle odczytałem twoje pytanie, to tylko ukryje pasek przewijania, ale nie sądzę, że tego szukasz.

JavaKungFu
źródło
1
Potrzebuję zarówno paska przewijania. Mówię o zachowaniu. Kiedy przewijam za pomocą kulki myszy po osiągnięciu końca przewijania div, nie może przewijać całej strony.
Jeevan
0

Nie mogłem uzyskać żadnej odpowiedzi do pracy w Chrome i Firefox, więc wymyśliłem to połączenie:

$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
źródło