kush
kush

Reputation: 16918

Scroll to bottom of div?

I am creating a chat using Ajax requests and I'm trying to get messages div to scroll to the bottom without much luck.

I am wrapping everything in this div:

#scroll {
    height:400px;
    overflow:scroll;
}

Is there a way to keep it scrolled to the bottom by default using JS?

Is there a way to keep it scrolled to the bottom after an ajax request?

Upvotes: 1094

Views: 1264640

Answers (30)

PanDe
PanDe

Reputation: 974

Why not use simple CSS to do this? The trick is to use this in your class:

display: flex;
flex-direction: column-reverse;

Here's a working example.

Upvotes: 4

Klaas Leussink
Klaas Leussink

Reputation: 2727

Method with no JavaScript required (2023)

Here's a method that doesn't require any JavaScript at all, and uses pure (Flexbox) CSS. I've explained the method in a bit more detail over here.

The trick is to put the items in a 'content' element, which is wrapped inside a column-reverse flexbox element, which acts as the 'scroller'. Because the items are in another ('content') container, they don't get 'flipped' but instead always line up to the bottom. This, in fact, makes the scroller scrolled to the bottom whenever stuff is added.

Advantages of this method

Aside from not relying on JavaScript, a big advantage of this method is that when the user has started scrolling the list, the scroll position remains fixed to the point where the user scrolled to. This prevents annoying content-jumping when new items are added. As soon as the user scrolls back to the bottom again, the list will stay scrolled to the bottom when updated.

Breakdown, click for demo

Demo

Note: the JavaScript in the below demo is only required for the demo itself (to add items to the list, and see what happens).

let scrollerContent = document.getElementById('scrollerContent');

document.getElementById('addItems').addEventListener('click', function() {
  let newChild = scrollerContent.lastElementChild.cloneNode(true);
  newChild.innerHTML = "Item " + (scrollerContent.children.length + 1);
  scrollerContent.appendChild(newChild);
});
.scroller {
  overflow: auto;
  height: 100px;
  display: flex;
  flex-direction: column-reverse;
}

.scroller .scroller-content .item {
  height: 20px;
  transform: translateZ(0); /* fixes a bug in Safari iOS where the scroller doesn't update */
}
<div class="scroller">
  <div class="scroller-content" id="scrollerContent">
    <div class="item">Item 1</div>
    <div class="item">Item 2</div>
    <div class="item">Item 3</div>
    <div class="item">Item 4</div>
    <div class="item">Item 5</div>
    <div class="item">Item 6</div>
    <div class="item">Item 7</div>
    <div class="item">Item 8</div>
    <div class="item">Item 9</div>
    <div class="item">Item 10</div>
  </div>
</div>
<br/><br/>
<button id="addItems">Add more items</button>

Upvotes: 16

Marcio Duarte
Marcio Duarte

Reputation: 771

If your project targets modern browsers, you can now use CSS Scroll Snap to control the scrolling behavior, such as keeping any dynamically generated element at the bottom.

    .wrapper > div {
        background-color: white;
        border-radius: 5px;
        padding: 5px 10px;
        text-align: center;
        font-family: system-ui, sans-serif;
    }

    .wrapper {
        display: flex;
        padding: 5px;
        background-color: #ccc;
        border-radius: 5px;
        flex-direction: column;
        gap: 5px;
        margin: 10px;
        max-height: 150px;

        /* Control snap from here */
        overflow-y: auto;
        overscroll-behavior-y: contain;
        scroll-snap-type: y mandatory;
    }

    .wrapper > div:last-child {
        scroll-snap-align: start;
    }
<div class="wrapper">
    <div>01</div>
    <div>02</div>
    <div>03</div>
    <div>04</div>
    <div>05</div>
    <div>06</div>
    <div>07</div>
    <div>08</div>
    <div>09</div>
    <div>10</div>
</div>

Upvotes: 12

Tho
Tho

Reputation: 25050

Try the code below:

const scrollToBottom = (id) => {
    const element = document.getElementById(id);
    element.scrollTop = element.scrollHeight;
}

You can also use Jquery to make the scroll smooth:

const scrollSmoothlyToBottom = (id) => {
    const element = $(`#${id}`);
    element.animate({
        scrollTop: element.prop("scrollHeight")
    }, 500);
}

Here is the demo

Here's how it works:

enter image description here

Ref: scrollTop, scrollHeight, clientHeight

Upvotes: 191

Spankied
Spankied

Reputation: 1875

Css only:

.scroll-container {
  overflow-anchor: none;
}

Makes it so the scroll bar doesn't stay anchored to the top when a child element is added. For example, when new message is added at the bottom of chat, scroll chat to new message.

Upvotes: 2

Ahmet Şimşek
Ahmet Şimşek

Reputation: 1531

alternative solution

function scrollToBottom(element) {
  element.scroll({ top: element.scrollHeight, behavior: 'smooth' });
}

Upvotes: 45

Adonis Gaitatzis
Adonis Gaitatzis

Reputation: 3729

You can use the Element.scrollTo() method.

It can be animated using the built-in browser/OS animation, so it's super smooth.

function scrollToBottom() {
    const scrollContainer = document.getElementById('container');
    scrollContainer.scrollTo({
        top: scrollContainer.scrollHeight,
        left: 0,
        behavior: 'smooth'
    });
}

// initialize dummy content
const scrollContainer = document.getElementById('container');
const numCards = 100;
let contentInnerHtml = '';
for (let i=0; i<numCards; i++) {
  contentInnerHtml += `<div class="card mb-2"><div class="card-body">Card ${i + 1}</div></div>`;
}
scrollContainer.innerHTML = contentInnerHtml;
.overflow-y-scroll {
  overflow-y: scroll;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/>

<div class="d-flex flex-column vh-100">
  <div id="container" class="overflow-y-scroll flex-grow-1"></div>
  <div>
    <button class="btn btn-primary" onclick="scrollToBottom()">Scroll to bottom</button>
  </div>
</div>

Upvotes: 4

jocassid
jocassid

Reputation: 4889

I use the difference between the Y coordinate of the first item div and the Y coordinate of the selected item div. Here is the JavaScript/JQuery code and the html:

function scrollTo(event){
        // In my proof of concept, I had a few <button>s with value 
        // attributes containing strings with id selector expressions
        // like "#item1".
        let selectItem = $($(event.target).attr('value'));
        let selectedDivTop = selectItem.offset().top;

        let scrollingDiv = selectItem.parent();

        let firstItem = scrollingDiv.children('div').first();
        let firstItemTop = firstItem.offset().top;

        let newScrollValue = selectedDivTop - firstItemTop;
        scrollingDiv.scrollTop(newScrollValue);
    }
<div id="scrolling" style="height: 2rem; overflow-y: scroll">
  <div id="item1">One</div>
  <div id="item2">Two</div>
  <div id="item3">Three</div>
  <div id="item4">Four</div>
  <div id="item5">Five</div>
</div>

Upvotes: 1

Mike Taverne
Mike Taverne

Reputation: 9352

Like you, I'm building a chat app and want the most recent message to scroll into view. This ultimately worked well for me:

//get the div that contains all the messages
let div = document.getElementById('message-container');

//make the last element (a message) to scroll into view, smoothly!
div.lastElementChild.scrollIntoView({ behavior: 'smooth' });

Upvotes: 6

ngShravil.py
ngShravil.py

Reputation: 5048

My Scenario: I had an list of string, in which I had to append a string given by a user and scroll to the end of the list automatically. I had fixed height of the display of the list, after which it should overflow.

I tried @Jeremy Ruten's answer, it worked, but it was scrolling to the (n-1)th element. If anybody is facing this type of issue, you can use setTimeOut() method workaround. You need to modify the code to below:

setTimeout(() => {
    var objDiv = document.getElementById('div_id');
    objDiv.scrollTop = objDiv.scrollHeight
}, 0)

Here is the StcakBlitz link I have created which shows the problem and its solution : https://stackblitz.com/edit/angular-ivy-x9esw8

Upvotes: 12

Anatol Zakrividoroga
Anatol Zakrividoroga

Reputation: 4518

You can use the HTML DOM scrollIntoView Method like this:

var element = document.getElementById("scroll");
element.scrollIntoView();

Upvotes: 9

moreirapontocom
moreirapontocom

Reputation: 458

On my Angular 6 application I just did this:

postMessage() {
  // post functions here
  let history = document.getElementById('history')
  let interval    
  interval = setInterval(function() {
    history.scrollTop = history.scrollHeight
    clearInterval(interval)
  }, 1)
}

The clearInterval(interval) function will stop the timer to allow manual scroll top / bottom.

Upvotes: 0

user2341537
user2341537

Reputation: 25

Set the distance from the top of the scrollable element to be the total height of the element.

const element = this.shadowRoot.getElementById('my-scrollable-div')
element.scrollTop = element.scrollHeight

Upvotes: -2

SeekLoad
SeekLoad

Reputation: 1089

Sometimes the most simple is the best solution: I do not know if this will help, it helped me to scroll it were ever I wanted too. The higher the "y=" is,the more down it scrolls and of course "0" means top, so there for example "1000" could be bottom, or "2000" or "3000" and so on, depending how long your page is. This usually works in a button with onclick or onmouseover.

window.scrollTo(x=0,y=150);

Upvotes: -2

veritas
veritas

Reputation: 432

If this is being done for scrolling to the bottom of chat window, do the following

The idea of scrolling to a particular div in the chat was the following

1) Each chat div consisting of Person, time and message is run in a for loop with class chatContentbox

2) querySelectorAll finds all such arrays. It could be 400 nodes (400 chats)

3) go to the last one

4) scrollIntoView()

let lastChatBox = document.querySelectorAll('.chatContentBox'); 
lastChatBox = lastChatBox[lastChatBox.length-1]; 
lastChatBox.scrollIntoView(); 

Upvotes: -1

use :

var element= $('element');
var maxScrollTop = element[0].scrollHeight - element.outerHeight();
element.scrollTop(maxScrollTop);

or check scroll to bottom :

    var element = $(element);
    var maxScrollTop = element[0].scrollHeight - element.outerHeight();
    element.on('scroll', function() {
        if ( element.scrollTop() >= maxScrollTop ) {
            alert('scroll to bottom');
        }
    });

Upvotes: -2

andsien
andsien

Reputation: 4317

This is much easier if you're using jQuery scrollTop:

$("#mydiv").scrollTop($("#mydiv")[0].scrollHeight);

Upvotes: 394

adl
adl

Reputation: 2035

smooth scroll with Javascript:

document.getElementById('messages').scrollIntoView({ behavior: 'smooth', block: 'end' });

Upvotes: 39

mjaque
mjaque

Reputation: 502

Scroll to the last element inside the div:

myDiv.scrollTop = myDiv.lastChild.offsetTop

Upvotes: 2

tnt-rox
tnt-rox

Reputation: 5548

Newer method that works on all current browsers:

this.scrollIntoView(false);

Upvotes: 77

Akira Yamamoto
Akira Yamamoto

Reputation: 4935

var mydiv = $("#scroll");
mydiv.scrollTop(mydiv.prop("scrollHeight"));

Works from jQuery 1.6

https://api.jquery.com/scrollTop/

http://api.jquery.com/prop/

Upvotes: 39

Paige Ruten
Paige Ruten

Reputation: 176645

Here's what I use on my site:

var objDiv = document.getElementById("your_div");
objDiv.scrollTop = objDiv.scrollHeight;

Upvotes: 1761

devonj
devonj

Reputation: 1238

Found this really helpful, thank you.

For the Angular 1.X folks out there:

angular.module('myApp').controller('myController', ['$scope', '$document',
  function($scope, $document) {

    var overflowScrollElement = $document[0].getElementById('your_overflow_scroll_div');
    overflowScrollElement[0].scrollTop = overflowScrollElement[0].scrollHeight;

  }
]);

Just because the wrapping in jQuery elements versus HTML DOM elements gets a little confusing with angular.

Also for a chat application, I found making this assignment after your chats were loaded to be useful, you also might need to slap on short timeout as well.

Upvotes: 6

aravk33
aravk33

Reputation: 489

A very simple method to this is to set the scroll to to the height of the div.

var myDiv = document.getElementById("myDiv");
window.scrollTo(0, myDiv.innerHeight);

Upvotes: 1

Barath Sankar
Barath Sankar

Reputation: 383

Java Script:

document.getElementById('messages').scrollIntoView(false);

Scrolls to the last line of the content present.

Upvotes: 10

BrianLegg
BrianLegg

Reputation: 1700

I know this is an old question, but none of these solutions worked out for me. I ended up using offset().top to get the desired results. Here's what I used to gently scroll the screen down to the last message in my chat application:

$("#html, body").stop().animate({
     scrollTop: $("#last-message").offset().top
}, 2000);

I hope this helps someone else.

Upvotes: 0

Lay Leangsros
Lay Leangsros

Reputation: 9296

Javascript or jquery:

var scroll = document.getElementById('messages');
   scroll.scrollTop = scroll.scrollHeight;
   scroll.animate({scrollTop: scroll.scrollHeight});

Css:

 .messages
 {
      height: 100%;
      overflow: auto;
  }

Upvotes: 7

DadViegas
DadViegas

Reputation: 2291

using jQuery animate:

$('#DebugContainer').stop().animate({
  scrollTop: $('#DebugContainer')[0].scrollHeight
}, 800);

Upvotes: 97

John Dunne
John Dunne

Reputation: 397

You can also, using jQuery, attach an animation to html,body of the document via:

$("html,body").animate({scrollTop:$("#div-id")[0].offsetTop}, 1000);

which will result in a smooth scroll to the top of the div with id "div-id".

Upvotes: 4

Benkinass
Benkinass

Reputation: 181

I have encountered the same problem, but with an additional constraint: I had no control over the code that appended new elements to the scroll container. None of the examples I found here allowed me to do just that. Here is the solution I ended up with .

It uses Mutation Observers (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) which makes it usable only on modern browsers (though polyfills exist)

So basically the code does just that :

var scrollContainer = document.getElementById("myId");

// Define the Mutation Observer
var observer = new MutationObserver(function(mutations) {

  // Compute sum of the heights of added Nodes
  var newNodesHeight = mutations.reduce(function(sum, mutation) {
      return sum + [].slice.call(mutation.addedNodes)
        .map(function (node) { return node.scrollHeight || 0; })
        .reduce(function(sum, height) {return sum + height});
  }, 0);

  // Scroll to bottom if it was already scrolled to bottom
  if (scrollContainer.clientHeight + scrollContainer.scrollTop + newNodesHeight + 10 >= scrollContainer.scrollHeight) {
    scrollContainer.scrollTop = scrollContainer.scrollHeight;
  }

});

// Observe the DOM Element
observer.observe(scrollContainer, {childList: true});

I made a fiddle to demonstrate the concept : https://jsfiddle.net/j17r4bnk/

Upvotes: 5

Related Questions