Przechowuj przepełnienie div przewiń do dołu, chyba że użytkownik przewinie w górę


224

Mam div, który ma tylko 300 pikseli, i chcę, aby przy ładowaniu strony przewijał się na dół zawartości. Ten div ma zawartość dodawaną dynamicznie i musi być przewijany do końca. Teraz, jeśli użytkownik zdecyduje się przewinąć w górę, nie chcę, aby przeskakiwał z powrotem na dół, dopóki użytkownik nie przewinie do końca

Czy jest możliwe, aby div, który pozostanie przewinięty do dołu, chyba że użytkownik przewinie w górę, a kiedy użytkownik przewinie z powrotem do dołu, musi pozostać na dole, nawet po dodaniu nowej zawartości dynamicznej. Jak bym to stworzył.


1
użyj css position top trzymaj go na dole {position : relative; bottom:0;}. Usuń właściwość css po przewinięciu przez użytkownika.
TCHdvlp

skoro nie przyjąłeś odpowiedzi, chcę zapytać: czy to zadziałało dla ciebie?
Mr.Manhattan

4
Brzmi jak chatbox, który chcesz osiągnąć
Thielicious

1
Aby poznać nowe pytanie, wypróbuj css snap w 2020 roku .
wener

Odpowiedzi:


136

To może ci pomóc:

var element = document.getElementById("yourDivID");
element.scrollTop = element.scrollHeight;

[EDYCJA], aby dopasować komentarz ...

function updateScroll(){
    var element = document.getElementById("yourDivID");
    element.scrollTop = element.scrollHeight;
}

za każdym razem, gdy dodawana jest treść, wywołaj funkcję updateScroll () lub ustaw licznik czasu:

//once a second
setInterval(updateScroll,1000);

jeśli chcesz zaktualizować TYLKO, jeśli użytkownik się nie przeprowadził:

var scrolled = false;
function updateScroll(){
    if(!scrolled){
        var element = document.getElementById("yourDivID");
        element.scrollTop = element.scrollHeight;
    }
}

$("#yourDivID").on('scroll', function(){
    scrolled=true;
});

3
To dobrze przewiń go do dołu przy ładowaniu strony, ale muszę pozostać na dole, gdy dodawana jest zawartość dynamiczna, chyba że użytkownik przewinął w górę.
Robert E. McIntosh

12
O ile wiem, nie włącza ponownie dynamicznego przewijania, gdy użytkownik przewija z powrotem na dół ...
TvE

@TvE, czy nie można dodać tego wsparcia?
Barkermn01,

6
Złe rozwiązanie. Nie ma powodu, aby dodawać setIntervaldo tego łagodnego problemu.
etancryst

6
@ethancrist jednak nie oferujesz żadnej alternatywy ... wzrusza ramionami.
Jarett Lloyd,

180

Byłem w stanie uzyskać to działa tylko z CSS.

Sztuką jest użycie display: flex;iflex-direction: column-reverse;

Przeglądarka traktuje dno jak górę. Zakładając, że przeglądarki, na które jesteś celem wsparcia flex-box, jedynym zastrzeżeniem jest to, że znaczniki muszą być w odwrotnej kolejności.

Oto działający przykład. https://codepen.io/jimbol/pen/YVJzBg


42
Możesz obejść się przy konieczności odwrócenia zawartości, dodając dodatkowe opakowanie i nakładając overflow:auto; display:flex; flex-direction:column-reverse;na zewnętrzne opakowanie zamiast wewnętrznego opakowania.
Nathan Arthur

6
prawdopodobnie najlepsze rozwiązanie, ponieważ nie wymaga javascript.
Amit Kumar Khare

3
@NathanArthur u da real mvp
php_nub_qq

13
@NathanArthur W rzeczywistości, jeśli dodasz div pod kontenerem, aby cofnąć wszystko: codepen.io/anon/pen/pdrLEZ
Coo

6
Niestety to rozwiązanie nie działa w przeglądarce Firefox :(
Stuart

166

Właśnie to zaimplementowałem i być może możesz użyć mojego podejścia.

Powiedzmy, że mamy następujący kod HTML:

<div id="out" style="overflow:auto"></div>

Następnie możemy sprawdzić, czy przewinął do dołu za pomocą:

var out = document.getElementById("out");
// allow 1px inaccuracy by adding 1
var isScrolledToBottom = out.scrollHeight - out.clientHeight <= out.scrollTop + 1;

scrollHeight daje wysokość elementu, w tym każdy niewidoczny obszar z powodu przepełnienia. clientHeight podaje wysokość CSS lub, mówiąc inaczej, rzeczywistą wysokość elementu. Obie metody zwracają wysokość bez margin, więc nie musisz się o to martwić. scrollTop podaje pozycję przewijania w pionie. 0 to góra, a max to scrollHeight elementu minus jego wysokość. Podczas korzystania z paska przewijania może być trudno (dla mnie było to w Chrome), aby pasek przewijania znalazł się na samym dole. więc rzuciłem niedokładność 1px. Tak isScrolledToBottombędzie, nawet jeśli pasek przewijania znajduje się 1 piksel od dołu. Możesz ustawić to, co ci się podoba.

Następnie wystarczy po prostu ustawić scrollTop elementu na dole.

if(isScrolledToBottom)
    out.scrollTop = out.scrollHeight - out.clientHeight;

Zrobiłem dla ciebie skrzypce, aby pokazać koncepcję: http://jsfiddle.net/dotnetCarpenter/KpM5j/

EDYCJA: Dodano fragment kodu, aby wyjaśnić, kiedy isScrolledToBottomjest true.

Przyklej pasek przewijania do dołu

const out = document.getElementById("out")
let c = 0

setInterval(function() {
    // allow 1px inaccuracy by adding 1
    const isScrolledToBottom = out.scrollHeight - out.clientHeight <= out.scrollTop + 1

    const newElement = document.createElement("div")

    newElement.textContent = format(c++, 'Bottom position:', out.scrollHeight - out.clientHeight,  'Scroll position:', out.scrollTop)

    out.appendChild(newElement)

    // scroll to bottom if isScrolledToBottom is true
    if (isScrolledToBottom) {
      out.scrollTop = out.scrollHeight - out.clientHeight
    }
}, 500)

function format () {
  return Array.prototype.slice.call(arguments).join(' ')
}
#out {
    height: 100px;
}
<div id="out" style="overflow:auto"></div>
<p>To be clear: We want the scrollbar to stick to the bottom if we have scrolled all the way down. If we scroll up, then we don't want the content to move.
</p>


22
To właściwie jedyne rozwiązanie, które spełnia postawione pytanie. I powinien być oznaczony jako poprawna odpowiedź.
preezzzy

1
@dotnetCarpenter: Wygląda mi na to, że potrzebujesz if(!isScrolledToBottom): test wygląda dla mnie źle (i nie działał w moim kodzie, dopóki go nie naprawiłem).
luskwater

1
@luskwater czy możesz podać fsfiddle ze swoją poprawką? Nie rozumiem problemu z out.scrollHeight - out.clientHeight <= out.scrollTop + 1. Czy używasz wypełniania w swoim CSS?
dotnetCarpenter,

2
Właśnie tego szukałem. I to jest odpowiedź na zadane pytanie OP. Dzięki dotnetCarperter.
Luis Menjivar,

1
Jeśli ktoś dostaje 0, gdy wywołuje scrollTop, sugeruję użycie document.getScrollingElementzgodnie z sugestią tutaj: stackoverflow.com/questions/36227559/scrolltop-always-returns-0/…
Dane Jordan

29
$('#yourDiv').scrollTop($('#yourDiv')[0].scrollHeight);

Demo na żywo: http://jsfiddle.net/KGfG2/


4
To dobrze przewiń go do dołu przy ładowaniu strony, ale muszę pozostać na dole, gdy dodawana jest zawartość dynamiczna, chyba że użytkownik przewinął w górę.
Robert E. McIntosh

To działa idealnie. Aktualizuję zawartość dynamiczną, a przewijanie jest zawsze na dole.
Andy Bajka

14
$('#div1').scrollTop($('#div1')[0].scrollHeight);

Or animated:

$("#div1").animate({ scrollTop: $('#div1')[0].scrollHeight}, 1000);

1
To dobrze przewiń go do dołu przy ładowaniu strony, ale muszę pozostać na dole, gdy dodawana jest zawartość dynamiczna, chyba że użytkownik przewinął w górę.
Robert E. McIntosh

To nie działa, gdy div rośnie dużo przy pierwszym strzale. na przykład w Angular-js, reaguj js, ładowanie szablonu Meteor Blaze, to nie zadziała.
Ankur Soni

To zadziałało dla mnie. Myślę, że to działa dobrze, jeśli div będzie utrzymywać stałą wysokość. Nie testowałem tego z dynamicznymi wysokościami.
doij790

11

W 2020 r. Możesz użyć przyciągania css , ale przed Chrome 81 zmiana układu nie spowoduje ponownego przyciągania , interfejs użytkownika czatu w czystym css działa w Chrome 81, możesz także sprawdzić Czy mogę użyć przyciągania CSS .

To demo przyciągnie ostatni element, jeśli jest widoczny, przewiń w dół, aby zobaczyć efekt.

.container {
  overflow-y: scroll;
  overscroll-behavior-y: contain;
  scroll-snap-type: y mandatory;
}

.container > div > div:last-child {
  scroll-snap-align: end;
}

.container > div > div {
  background: lightgray;
  height: 3rem;
  font-size: 1.5rem;
}
.container > div > div:nth-child(2n) {
  background: gray;
}
<div class="container" style="height:6rem">
<div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
</div>

wprowadź opis zdjęcia tutaj


5

.cont{
height: 100px;
overflow-x: hidden;
overflow-y: auto;
transform: rotate(180deg);
direction:rtl;
text-align:left;
}
ul{
overflow: hidden;
transform: rotate(180deg);
}
<div class="cont"> 
 <ul>
   <li>0</li>
   <li>1</li>
   <li>2</li>
   <li>3</li>
   <li>4</li>
   <li>5</li>
   <li>6</li>
   <li>7</li>
   <li>8</li>
   <li>9</li>
   <li>10</li>  
 </ul>
</div>

  1. Run code snippetzobaczyć efekt. (PS: Jeśli Run code snippetnie działa, spróbuj tego: https://jsfiddle.net/Yeshen/xm2yLksu/3/ )

  2. Jak to działa:

Domyślnym przepełnieniem jest przewijanie od góry do dołu.

transform: rotate(180deg) może powodować przewijanie lub ładowanie bloku dynamicznego od dołu do góry.

  1. Oryginalny pomysł:

https://blog.csdn.net/yeshennet/article/details/88880252


Dodaj dodatkowe wyjaśnienie, w jaki sposób Twój kod rozwiązuje pytanie OP
jro

1, dodał dodatkowe wprowadzenie. 2, proszę Run code snippetbezpośrednio zobaczyć efekt.
yisheng wu

Przycisk fragmentu kodu uruchomienia nie jest dla mnie wyświetlany. Sprawdź swoje formatowanie
Jro

To wspaniale. Dołącz link do swojej odpowiedzi
jro

2
Bardzo kreatywne rozwiązanie. Odwraca jednak normalne działanie kółka przewijania myszy. Aby przejść w górę, muszę przewinąć „w dół” i odwrotnie.
mfluehr

3
$('#yourDivID').animate({ scrollTop: $(document).height() }, "slow");
return false;

Spowoduje to obliczenie pozycji ScrollTop z wysokości #yourDivIDkorzystania z $(document).height()właściwości, dzięki czemu nawet jeśli zawartość dynamiczna zostanie dodana do div, scroller będzie zawsze w dolnej pozycji. Mam nadzieję że to pomoże. Ale ma też mały błąd, nawet jeśli przewiniemy w górę i opuszczymy wskaźnik myszy z rolki, automatycznie dojdzie do dolnej pozycji. Jeśli ktoś może to naprawić, to też będzie miło.


2
//Make sure message list is scrolled to the bottom
var container = $('#MessageWindowContent')[0];
var containerHeight = container.clientHeight;
var contentHeight = container.scrollHeight;

container.scrollTop = contentHeight - containerHeight;

Oto moja wersja oparta na odpowiedzi dotnetCarpenter. Moje podejście jest czystym jQuery i zmieniłem nazwy zmiennych, aby wszystko było trochę jaśniejsze. Dzieje się tak, gdy wysokość zawartości jest większa niż kontener przewijamy o dodatkową odległość w dół, aby osiągnąć pożądany rezultat.

Działa w IE i chrome ..


2

Odpowiedź Jima Halla jest lepsza, ponieważ chociaż nie przewija się ona do dołu, gdy jesteś przewijany w górę, jest to również czysty CSS.

Niestety, nie jest to stabilne rozwiązanie: w chrome (prawdopodobnie z powodu problemu 1-px opisanego przez dotnetCarpenter powyżej) scrollTopzachowuje się niedokładnie o 1 piksel, nawet bez interakcji użytkownika (po dodaniu elementu). Możesz ustawić scrollTop = scrollHeight - clientHeight, ale to utrzyma div na miejscu, gdy zostanie dodany inny element, inaczej funkcja „trzymaj się dołu” już nie działa.

Krótko mówiąc, dodanie niewielkiej ilości Javascript (westchnień) naprawi to i spełni wszystkie wymagania:

Coś w stylu https://codepen.io/anon/pen/pdrLEZ this (przykład autorstwa Coo), a po dodaniu elementu do listy, również:

container = ...
if(container.scrollHeight - container.clientHeight - container.scrollTop <= 29) {
    container.scrollTop = container.scrollHeight - container.clientHeight;
}

gdzie 29 to wysokość jednej linii.

Tak więc, gdy użytkownik przewinie w górę o pół linii (jeśli to w ogóle możliwe?), JavaScript zignoruje go i przewinie w dół. Ale wydaje mi się, że jest to nieistotne. I naprawia Chrome 1 px rzeczy.


2

Oto rozwiązanie oparte na poście na blogu autorstwa Ryana Hunta . Zależy to od overflow-anchorwłaściwości CSS, która przypina pozycję przewijania do elementu na dole przewijanej treści.

const messages = [
  'Expect rain today.',
  'Tomorrow will be sunny.',
  'Snow is coming next week.',
  'Hailstorms are imminent.',
];

function addMessage() {
  const $message = document.createElement('div');
  $message.className = 'message';
  $message.innerText = messages[(Math.random() * messages.length) | 0];
  $messages.insertBefore($message, $anchor);

  // Trigger the scroll pinning when the scroller overflows
  if (!overflowing) {
    overflowing = isOverflowing($scroller);
    $scroller.scrollTop = $scroller.scrollHeight;
  }
}

function isOverflowing($el) {
  return $el.scrollHeight > $el.clientHeight;
}

const $scroller = document.querySelector('.scroller');
const $messages = document.querySelector('.messages');
const $anchor = document.querySelector('.anchor');
let overflowing = false;

setInterval(addMessage, 1000);
.scroller {
  overflow: auto;
  height: 90vh;
  max-height: 11em;
  background: #555;
}

.messages > * {
  overflow-anchor: none;
}

.anchor {
  overflow-anchor: auto;
  height: 1px;
}

.message {
  margin: .3em;
  padding: .5em;
  background: #eee;
}
<section class="scroller">
  <div class="messages">
    <div class="anchor"></div>
  </div>
</section>

Pamiętaj, że overflow-anchorobecnie nie działa w Safari ani Edge, więc to rozwiązanie nie działa obecnie we wszystkich przeglądarkach.


1

Możesz użyć czegoś takiego,

var element = document.getElementById("yourDivID");
window.scrollTo(0,element.offsetHeight);

Wyjaśnij to, proszę!
Szabolcs Páll

1. scrollTo to metoda, która przewija całe okno do określonych współrzędnych. 2. offset zapewnia wysokość elementu, dzięki czemu druga linia powyższego kodu przewija okno w dół podczas przypisywania czegoś.
Yashsz Chauhan

0

Oto jak do tego podszedłem. Mój div ma wysokość 650px. Zdecydowałem, że jeśli wysokość przewijania mieści się w granicach 150 pikseli od dołu, to przewiń automatycznie. W przeciwnym razie pozostaw to użytkownikowi.

if (container_block.scrollHeight - container_block.scrollTop < 800) {
                    container_block.scrollTo(0, container_block.scrollHeight);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.