web-dev-qa-db-fra.com

Gardez le débordement div défilé vers le bas, sauf si l'utilisateur fait défiler

J'ai un div qui ne fait que 300 pixels de large et que je souhaite utiliser lorsque la page se charge défile jusqu'au bas du contenu. Du contenu a été ajouté dynamiquement à cette div et doit rester déroulé jusqu'au bout. Maintenant, si l'utilisateur décide de faire défiler vers le haut, je ne veux pas qu'il revienne en bas jusqu'à ce que l'utilisateur défile à nouveau

Est-il possible d'avoir une div qui restera défilée vers le bas à moins que l'utilisateur ne défile vers le haut et que lorsque l'utilisateur remonte au bas de la page, il doit rester au bas de la liste même lorsqu'un nouveau contenu dynamique est ajouté. Comment pourrais-je créer cela?.

130
Robert E. McIntosh

bien que cela puisse vous aider:

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

[EDIT], pour correspondre au commentaire ...

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

chaque fois que du contenu est ajouté, appelez la fonction updateScroll () ou définissez une minuterie:

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

si vous souhaitez mettre à jour UNIQUEMENT si l'utilisateur ne s'est pas déplacé:

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

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

Je viens de mettre en œuvre cela et peut-être que vous pouvez utiliser mon approche.

Disons que nous avons le code HTML suivant:

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

Ensuite, nous pouvons vérifier s'il a fait défiler vers le bas avec:

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

scrollHeight vous donne la hauteur de l'élément, y compris toute zone non visible due à un débordement. clientHeight vous donne la hauteur CSS ou autrement dit, la hauteur réelle de l'élément. Les deux méthodes retournent la hauteur sans margin, vous n'avez donc pas à vous en préoccuper. scrollTop vous donne la position du défilement vertical. 0 est top et max est scrollHeight de l'élément moins la hauteur de l'élément lui-même. Lorsque vous utilisez la barre de défilement, il peut être difficile (c’était dans Chrome pour moi) d’obtenir la barre de défilement tout en bas. alors j'ai ajouté une inexactitude de 1px. Donc isScrolledToBottom sera vrai même si la barre de défilement est à 1 pixel du bas. Vous pouvez définir ce paramètre selon vos désirs.

Ensuite, il suffit de placer le scrollTop de l’élément en bas.

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

J'ai fait un violon pour que vous montriez le concept: http://jsfiddle.net/dotnetCarpenter/KpM5j/

EDIT: Extrait de code ajouté pour préciser quand isScrolledToBottom est true.

Collez la barre de défilement vers le bas

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>

123
dotnetCarpenter

J'ai été capable de travailler avec CSS uniquement.

L'astuce consiste à utiliser display: flex; et flex-direction: column-reverse;

Le navigateur traite le bas comme son haut. En supposant que les navigateurs que vous ciblez prennent en charge flex-box, le seul inconvénient est que le balisage doit être dans l'ordre inverse.

Voici un exemple de travail. https://codepen.io/jimbol/pen/YVJzBg

96
Jim Hall
$('#yourDiv').scrollTop($('#yourDiv')[0].scrollHeight);

Démo en direct: http://jsfiddle.net/KGfG2/

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

Or animated:

$("#div1").animate({ scrollTop: $('#div1')[0].scrollHeight}, 1000);
13
Sasidharan
$('#yourDivID').animate({ scrollTop: $(document).height() }, "slow");
return false;

Cela calculera la position ScrollTop à partir de la hauteur de #yourDivID à l'aide de la propriété $(document).height() de sorte que, même si un contenu dynamique soit ajouté à la div, le défilement sera toujours en position inférieure. J'espère que cela t'aides. Mais il y a aussi un petit bug même si on fait défiler vers le haut et laisse le pointeur de la souris dans la molette, il se placera automatiquement en bas de la liste. Si quelqu'un peut corriger cela aussi ce sera Nice.

3
Krishnadas PC
//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;

Voici ma version basée sur la réponse de dotnetCarpenter. Mon approche est purement jQuery et j'ai nommé les variables pour rendre les choses un peu plus claires. Ce qui se passe est que si la hauteur du contenu est plus grande que le conteneur, nous parcourons la distance supplémentaire pour atteindre le résultat souhaité. 

Fonctionne en IE et en chrome ..

2
KingOfDaNorth

La réponse de Jim Hall est préférable, car si elle ne fait pas défiler l'écran vers le bas, il s'agit également de CSS pur.

Malheureusement, ce n'est malheureusement pas une solution stable: en chrome (probablement en raison du problème de 1-px décrit par dotnetCarpenter ci-dessus), scrollTop se comporte de manière incorrecte de 1 pixel, même sans intervention de l'utilisateur (lors de l'ajout d'élément). Vous pouvez définir scrollTop = scrollHeight - clientHeight, mais cela maintiendra la div en position lorsqu'un autre élément est ajouté, c'est-à-dire que la fonctionnalité "se garder en bas" ne fonctionne plus.

En bref, ajouter une petite quantité de Javascript (soupir) résoudra ce problème et répondra à toutes les exigences:

Quelque chose comme https://codepen.io/anon/pen/pdrLEZ this (exemple de Coo), et après l'ajout d'un élément à la liste, les éléments suivants:

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

où 29 est la hauteur d'une ligne.

Ainsi, lorsque l'utilisateur fait défiler une demi-ligne vers le haut (si cela est même possible?), Le Javascript l'ignorera et fera défiler l'écran vers le bas. Mais je suppose que c'est négligeable. Et, il corrige le truc Chrome 1 px.

1
Blauhirn
.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 snippet pour voir l'effet. (PS: si Run code snippet ne fonctionne pas, essayez ceci: https://jsfiddle.net/Yeshen/xm2yLksu/3/ )

  2. Comment ça marche:

Le débordement par défaut est un défilement de haut en bas.

transform: rotate(180deg) peut le faire défiler ou charger un bloc dynamique de bas en haut.

  1. Idée originale:

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

1
yisheng wu

Voici une solution basée sur n blog de Ryan Hunt . Cela dépend de la propriété CSS _overflow-anchor_, qui associe la position de défilement à un élément situé au bas du contenu défilant.

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

Notez que overflow-anchor ne fonctionne pas actuellement dans Safari ou Edge, cette solution ne fonctionne donc pas actuellement dans tous les navigateurs.

0
mfluehr

Voici comment je l'ai abordé. Ma hauteur de div est 650px. J'ai décidé que si la hauteur de défilement est à moins de 150 pixels du bas, faites-le défiler automatiquement. Sinon, laissez-le à l'utilisateur.

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