Mantenha div overflow rolado para baixo, a menos que o usuário role para cima

222

Eu tenho uma div que tem apenas 300 pixels de tamanho e quero quando a página carrega rolar para a parte inferior do conteúdo. Essa div possui conteúdo adicionado dinamicamente e precisa permanecer rolada até o fim. Agora, se o usuário decidir rolar para cima, não quero voltar ao fundo até que o usuário role novamente

É possível ter uma div que fique rolada para baixo, a menos que o usuário role para cima e quando o usuário voltar para a parte inferior, ele precisará se manter na parte inferior, mesmo quando o novo conteúdo dinâmico for adicionado. Como eu iria criar isso?

Robert E. McIntosh
fonte
1
use a posição css superior e mantenha-a na parte inferior {position : relative; bottom:0;}. Remova a propriedade css assim que o usuário tiver rolado.
TCHdvlp 4/13
como você não aceitou uma resposta, quero perguntar: funcionou para você?
Mr.Manhattan
4
Soa como um chatbox você quer realizar
Thielicious
1
Para novos nesta questão, tente o css snap em 2020 .
wener 5/03

Respostas:

136

Isso pode ajudá-lo a:

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

[EDIT], para corresponder ao comentário ...

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

sempre que o conteúdo for adicionado, chame a função updateScroll () ou defina um timer:

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

se você deseja atualizar SOMENTE se o usuário não se moveu:

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

$("#yourDivID").on('scroll', function(){
    scrolled=true;
});
Mr.Manhattan
fonte
3
Isso rolará para o final do carregamento da página, mas eu preciso que ele permaneça no final quando o conteúdo dinâmico for adicionado, a menos que o usuário role para cima.
Robert E. McIntosh
12
Tanto quanto eu posso dizer isso não reativar rolagem dinâmica quando o usuário rola volta para o fundo ...
TVE
@TvE, não podemos adicionar esse suporte?
Barkermn01
6
Solução ruim. Não há razão para adicionar um setIntervalpara esse problema leve.
ethancrist
6
@ ethancrist no entanto, você não oferece alternativa ... encolhe os ombros.
21418 Jarret Lloyd
180

Consegui fazer isso funcionar apenas com CSS.

O truque é usar display: flex;eflex-direction: column-reverse;

O navegador trata o fundo como se fosse o topo. Supondo que os navegadores que você está direcionando para o suporte flex-box, a única ressalva é que a marcação deve estar na ordem inversa.

Aqui está um exemplo de trabalho. https://codepen.io/jimbol/pen/YVJzBg

Jim Hall
fonte
42
Você pode evitar a reversão do conteúdo adicionando um invólucro extra e aplicando overflow:auto; display:flex; flex-direction:column-reverse;-o ao invólucro externo, em vez do invólucro interno.
Nathan Arthur
6
provavelmente a melhor solução porque não precisa de javascript.
Amit Kumar Khare
3
@NathanArthur u da real mvp
php_nub_qq 11/11
13
@NathanArthur De fato, se você apenas adicionar uma div no contêiner para cancelar a reversão de tudo: codepen.io/anon/pen/pdrLEZ
Coo
6
Infelizmente, esta solução não parece funcionar no Firefox :(
Stuart
166

Acabei de implementar isso e talvez você possa usar minha abordagem.

Digamos que temos o seguinte HTML:

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

Em seguida, podemos verificar se rolou para baixo com:

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

scrollHeight fornece a altura do elemento, incluindo qualquer área não visível devido ao estouro. clientHeight fornece a altura do CSS ou, de outra maneira, a altura real do elemento. Ambos os métodos retornam a altura sem margin, portanto você não precisa se preocupar com isso. scrollTop fornece a posição da rolagem vertical. 0 é superior e max é o scrollHeight do elemento menos a altura do elemento. Ao usar a barra de rolagem, pode ser difícil (estava no Chrome para mim) fazer a barra de rolagem descer até o final. então joguei uma imprecisão de 1px. Isso isScrolledToBottomserá verdade mesmo que a barra de rolagem esteja 1px a partir da parte inferior. Você pode definir isso como achar melhor para você.

Depois, basta definir o scrollTop do elemento na parte inferior.

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

Eu criei um violino para você mostrar o conceito: http://jsfiddle.net/dotnetCarpenter/KpM5j/

EDIT: Adicionado trecho de código para esclarecer quando isScrolledToBottomé true.

Posicione a barra de rolagem na parte inferior

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
fonte
22
Esta é realmente a única solução que realiza a pergunta declarada. E deveria ter sido marcado como a resposta correta.
preezzzy
1
@dotnetCarpenter: Parece-me que você precisa if(!isScrolledToBottom): o teste parece errado para mim (e não funcionou no meu código até que eu o corrigisse).
Luskwater
1
@luskwater você pode fornecer um fsfiddle com sua correção? Eu não entendo o problema com out.scrollHeight - out.clientHeight <= out.scrollTop + 1. Você está usando preenchimento no seu CSS?
DotnetCarpenter
2
Era isso que eu estava procurando. E essa é a resposta para a pergunta do OP. Obrigado dotnetCarperter.
Luis Menjivar 10/10
1
Se alguém está ficando 0 quando eles chamam scrollTop, eu sugiro usar document.getScrollingElementcomo sugerido aqui: stackoverflow.com/questions/36227559/scrolltop-always-returns-0/...
Dane Jordan
29
$('#yourDiv').scrollTop($('#yourDiv')[0].scrollHeight);

Demonstração ao vivo: http://jsfiddle.net/KGfG2/

Alvaro
fonte
4
Isso rolará para o final do carregamento da página, mas eu preciso que ele permaneça no final quando o conteúdo dinâmico for adicionado, a menos que o usuário role para cima.
Robert E. McIntosh
Isso funciona perfeitamente. Estou atualizando com conteúdo dinâmico e a rolagem está sempre na parte inferior.
Andy Bajka 5/03
14
$('#div1').scrollTop($('#div1')[0].scrollHeight);

Or animated:

$("#div1").animate({ scrollTop: $('#div1')[0].scrollHeight}, 1000);
Sasidharan
fonte
1
Isso rolará para o final do carregamento da página, mas eu preciso que ele permaneça no final quando o conteúdo dinâmico for adicionado, a menos que o usuário role para cima.
Robert E. McIntosh
Isso não funciona quando div cresce muito no primeiro tiro. por exemplo, em Angular-js, react js, carregamento do modelo Meteor Blaze, isso não funcionará.
Ankur Soni
Isso funcionou para mim. Eu acho que funciona bem se o div em questão permanecer uma altura consistente. Não o testaram com alturas dinâmicas.
doij790 26/03
10

Em 2020, você pode usar o snap a CSS , mas antes do Chrome 81 a alteração de layout não aciona o snap novamente , uma interface de usuário pura de bate-papo em CSS funciona no Chrome 81, também é possível marcar Posso usar o snap ao CSS .

Esta demonstração fará o snap do último elemento, se visível, role para baixo para ver o efeito.

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

insira a descrição da imagem aqui

Wener
fonte
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 snippetpara ver o efeito. (PS: Se Run code snippetnão estiver funcionando, tente o seguinte: https://jsfiddle.net/Yeshen/xm2yLksu/3/ )

  2. Como isso funciona:

O excesso padrão é a rolagem de cima para baixo.

transform: rotate(180deg) pode fazê-lo rolar ou carregar bloco dinâmico de baixo para cima.

  1. Ideia original:

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

yisheng wu
fonte
Por favor, adicione mais explicações sobre a forma como os seus resolve código pergunta do OP
JRO
1, adicionou uma introdução adicional. 2, por favor Run code snippet, veja diretamente o efeito.
yisheng wu 01/04/19
O botão do snippet de código de execução não aparece para mim. Verifique sua formatação
jro
Isso é ótimo. Por favor inclua o link na sua resposta
jro
2
Solução muito criativa. No entanto, ele reverte o funcionamento normal da roda de rolagem do mouse. Para subir, preciso rolar "para baixo" e vice-versa.
22419 mfluehr
3
$('#yourDivID').animate({ scrollTop: $(document).height() }, "slow");
return false;

Isso calculará a posição ScrollTop a partir da altura do #yourDivIDuso da $(document).height()propriedade, de modo que, mesmo que conteúdos dinâmicos sejam adicionados à div, o scroller estará sempre na posição inferior. Espero que isto ajude. Mas também possui um pequeno erro, mesmo se rolarmos para cima e deixarmos o ponteiro do mouse no rolo, ele automaticamente chegará à posição inferior. Se alguém puder corrigir isso também será bom.

Krishnadas PC
fonte
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;

Aqui está a minha versão com base na resposta do dotnetCarpenter. Minha abordagem é um jQuery puro e nomeei as variáveis ​​para tornar as coisas um pouco mais claras. O que está acontecendo é que se a altura do conteúdo for maior que o contêiner, percorremos a distância extra para baixo para alcançar o resultado desejado.

Funciona no IE e no Chrome.

KingOfDaNorth
fonte
2

A resposta de Jim Hall é preferível porque, embora de fato não role para baixo quando você é rolado para cima, também é CSS puro.

Infelizmente, no entanto, essa não é uma solução estável: no chrome (possivelmente devido ao problema de 1 px descrito pelo dotnetCarpenter acima), scrollTopse comporta de maneira imprecisa em 1 pixel, mesmo sem a interação do usuário (mediante adição de elemento). Você pode definir scrollTop = scrollHeight - clientHeight, mas isso manterá a div na posição quando outro elemento for adicionado, também conhecido como o recurso "manter-se na parte inferior", não está mais funcionando.

Portanto, resumindo, adicionar uma pequena quantidade de Javascript (suspiro) corrigirá isso e atenderá a todos os requisitos:

Algo como https://codepen.io/anon/pen/pdrLEZ this (exemplo de Coo) e, após adicionar um elemento à lista, também o seguinte:

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

onde 29 é a altura de uma linha.

Portanto, quando o usuário rola meia linha (se isso é possível?), O Javascript o ignora e rola até o final. Mas acho que isso é negligenciável. E corrige a coisa de 1 px do Chrome.

phil294
fonte
2

Aqui está uma solução baseada em um post de Ryan Hunt . Depende da overflow-anchorpropriedade CSS, que fixa a posição de rolagem a um elemento na parte inferior do conteúdo rolado.

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>

Observe que overflow-anchoratualmente não funciona no Safari ou Edge, portanto, esta solução atualmente não funciona em todos os navegadores.

mfluehr
fonte
1

Você pode usar algo assim,

var element = document.getElementById("yourDivID");
window.scrollTo(0,element.offsetHeight);
Yashesh Chauhan
fonte
Explique por favor!
Szabolcs Páll
1.scrollTo é um método que rola a janela inteira para coordenadas específicas.2.offsetHeight fornecerá a altura do seu elemento, de modo que a segunda linha do código acima continue rolando a janela para baixo enquanto você estiver atribuindo algo.
Yashesh Chauhan 27/11/19
0

Aqui está como eu me aproximei. Minha altura div é de 650px. Decidi que, se a altura da rolagem estiver a 150px da parte inferior, role-a automaticamente. Senão, deixe para o usuário.

if (container_block.scrollHeight - container_block.scrollTop < 800) {
                    container_block.scrollTo(0, container_block.scrollHeight);
}
Marco
fonte