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ł.

Robert E. McIntosh
źródło
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;
});
Mr.Manhattan
źródło
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

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

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

Alvaro
źródło
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);
Sasidharan
źródło
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

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

yisheng wu
źródło
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.

Krishnadas PC
źródł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 ..

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

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

mfluehr
źródło
1

Możesz użyć czegoś takiego,

var element = document.getElementById("yourDivID");
window.scrollTo(0,element.offsetHeight);
Yashsz Chauhan
źródło
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);
}
Marco
źródło