Scroll to bottom of div?
Asked Answered
O

35

1078

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?

Osswald answered 6/11, 2008 at 22:37 Comment(2)
In many cases this can be achieved with CSS-only. See this answer.Brink
You can simply use Jquery to achieve this: code $(document).ready(function() { var chatMessagesDiv = $('#scroll'); chatMessagesDiv.scrollTop(chatMessagesDiv[0].scrollHeight); });Griseldagriseldis
L
1734

Here's what I use on my site:

var objDiv = document.getElementById("your_div");
objDiv.scrollTop = objDiv.scrollHeight;
Letterperfect answered 6/11, 2008 at 22:42 Comment(11)
is this method ok with all browsers?Debouch
@PaulDinh: I just looked into this and there is an issue with IE7 and lower using scrollHeight. There does seem to be a work around for IE7 here.Barranca
why not scrollTopMax instead of scrollHeight?Liberality
This isn't working when I dynamically add images :(Superstar
The 'modern' way is to use Element.scrollIntoView() -- developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIncoordination
can you use var element = document.getElementById("elementToSelect"); element.scrollIntoView();Endothelioma
This didn't work for me, If you have query use @andsien.. Note an alternative animate can have unedited behavior with the scroll bar locking in safari <= 14.0.1 (latest)Dunson
@Liberality scrollTopMax is non standard, see caniuse.com/mdn-api_element_scrolltopmaxTanh
@Incoordination Only if you want to scroll to a specific element. But he just wants to go to the bottom (of another one), simply the plain bottom.Terisateriyaki
@johnktejik Maybe you aren't changing the html img size attributes? Otherwise that'd be weird.Terisateriyaki
It's 2024 and this answer still fits the bill.Mora
C
390

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

$("#mydiv").scrollTop($("#mydiv")[0].scrollHeight);
Crossstaff answered 19/4, 2010 at 2:17 Comment(2)
you need [0] to get dom element from jquery element to get scrollHeightKamin
Without repeating yourself: $("#mydiv").scrollTop(function() { return this.scrollHeight; });Gerta
S
187

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

Singer answered 18/10, 2015 at 2:27 Comment(0)
I
98

using jQuery animate:

$('#DebugContainer').stop().animate({
  scrollTop: $('#DebugContainer')[0].scrollHeight
}, 800);
Included answered 12/9, 2012 at 15:0 Comment(1)
Notice how this answer uses .stop(), which prevents issues with multiple animations.Alabama
N
73

Newer method that works on all current browsers:

this.scrollIntoView(false);
Noteworthy answered 10/10, 2014 at 7:8 Comment(6)
This should be the accepted answer as the older methods do not work anymoreWaddle
That would be the case if "this" is something in the div that can be scrolled toAreopagite
The method scrollIntoView is triggered on a child element inside a parent container. So how to use it to: scroll parent to bottom (as asked in the original question).Spun
You can use scrollIntoView({ behavior: "smooth", block: "end" });Philomenaphiloo
See which browsers are supported here caniuse.com/?search=scrollIntoViewGantt
This scrolls the whole page, not the targeted element (chrome)Aerology
B
41

alternative solution

function scrollToBottom(element) {
  element.scroll({ top: element.scrollHeight, behavior: 'smooth' });
}
Blow answered 19/12, 2020 at 15:6 Comment(0)
G
39
var mydiv = $("#scroll");
mydiv.scrollTop(mydiv.prop("scrollHeight"));

Works from jQuery 1.6

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

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

Gantt answered 10/1, 2014 at 15:53 Comment(0)
S
38

smooth scroll with Javascript:

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

Saddler answered 29/4, 2019 at 11:57 Comment(0)
I
15

If you don't want to rely on scrollHeight, the following code helps:

$('#scroll').scrollTop(1000000);
Inadequate answered 10/6, 2014 at 10:58 Comment(2)
looks like a hack. what does the 1000000 number mean?Breathtaking
Looks like the 1000000 is just a large number. By passing an extremely large number the browser should cap it to the height of the the div.Clemen
M
13

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>
Mauretta answered 20/3, 2023 at 10:22 Comment(2)
This was enlightening and really cool trick. works perfectlyCorrelative
I am using this trick for fun and profit but I don't know to have content jumping when I actually want to. This is a chat interface so a new message from someone should not scroll to bottom so this is all good but how do I do scroll to bottom when sending a message myself ?Jovanjove
F
12

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

Finkelstein answered 10/5, 2020 at 18:46 Comment(1)
with setTimeout(), its working fine. Worked with angular 9. Saved my day.Holocaine
S
11

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>
Sidra answered 19/4, 2022 at 19:36 Comment(1)
Note that this will keep the dynamically generated element at the bottom, so if you scroll up, the wrapper will automatically scroll back down to the bottom.Bellbottoms
U
10

Java Script:

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

Scrolls to the last line of the content present.

Ulm answered 24/10, 2017 at 16:25 Comment(0)
S
8

You can use the HTML DOM scrollIntoView Method like this:

var element = document.getElementById("scroll");
element.scrollIntoView();
Sheers answered 9/3, 2020 at 18:58 Comment(0)
B
7

Javascript or jquery:

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

Css:

 .messages
 {
      height: 100%;
      overflow: auto;
  }
Borodino answered 18/7, 2016 at 18:44 Comment(0)
R
6

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.

Robot answered 15/9, 2015 at 17:56 Comment(0)
H
6

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' });
Hobard answered 8/7, 2020 at 7:48 Comment(1)
Beautiful and simple to implement, exactly what I wanted. Thanks.Courlan
A
5

Using jQuery, scrollTop is used to set the vertical position of scollbar for any given element. there is also a nice jquery scrollTo plugin used to scroll with animation and different options (demos)

var myDiv = $("#div_id").get(0);
myDiv.scrollTop = myDiv.scrollHeight;

if you want to use jQuery's animate method to add animation while scrolling down, check the following snippet:

var myDiv = $("#div_id").get(0);
myDiv.animate({
    scrollTop: myDiv.scrollHeight
  }, 500);
Aplanatic answered 19/11, 2014 at 19:40 Comment(0)
G
5

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/

Gesticulate answered 9/10, 2015 at 6:59 Comment(2)
how to get dynamic id? like in <div class="abc"><div data-bind=attr : {'id': myId } ></div></div> In this code myId is a variable. How can I access this id in script.Brucebrucellosis
I'm not quite sure I understand your question. In my example, "myId" is the id of the scroll container. Do you want to create more than one area where the user can scroll ?Gesticulate
L
4

small addendum: scrolls only, if last line is already visible. if scrolled a tiny bit, leaves the content where it is (attention: not tested with different font sizes. this may need some adjustments inside ">= comparison"):

var objDiv = document.getElementById(id);
var doScroll=objDiv.scrollTop>=(objDiv.scrollHeight-objDiv.clientHeight);                   

// add new content to div
$('#' + id ).append("new line at end<br>"); // this is jquery!

// doScroll is true, if we the bottom line is already visible
if( doScroll) objDiv.scrollTop = objDiv.scrollHeight;
Lomasi answered 3/7, 2014 at 13:58 Comment(0)
F
4

Just as a bonus snippet. I'm using angular and was trying to scroll a message thread to the bottom when a user selected different conversations with users. In order to make sure that the scroll works after the new data had been loaded into the div with the ng-repeat for messages, just wrap the scroll snippet in a timeout.

$timeout(function(){
    var messageThread = document.getElementById('message-thread-div-id');
    messageThread.scrollTop = messageThread.scrollHeight;
},0)

That will make sure that the scroll event is fired after the data has been inserted into the DOM.

Flor answered 21/8, 2014 at 12:41 Comment(3)
I suspect $scope.$apply(callback) would work as well as this forces a digest and re-evaluation of the view.Gustavo
Thank you! I was really wondering why I couldn't get it to work and the $timeout was the issue.Tipsy
@Tipsy same setTimeout(function() { ... }, n)Tangency
B
4

This will let you scroll all the way down regards the document height

$('html, body').animate({scrollTop:$(document).height()}, 1000);
Bega answered 13/8, 2015 at 3:41 Comment(0)
M
4

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

Mica answered 28/10, 2015 at 18:17 Comment(0)
G
4

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>
Gilleod answered 5/11, 2020 at 17:2 Comment(0)
S
3

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.

Stereotomy answered 6/5, 2022 at 5:17 Comment(1)
The problem with column-reverse is app logic that could append data now has to prepend data. That can get very messy very fast. For example, in a chat program, try collapsing all of the messages from a single user to show the user icon only once until a different user sends a message. Very easy with append logic. Much harder with prepend logic.Sulphurate
B
2

Scroll to the last element inside the div:

myDiv.scrollTop = myDiv.lastChild.offsetTop
Benzvi answered 3/2, 2019 at 8:50 Comment(0)
P
2

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.

Preciado answered 30/7, 2021 at 4:24 Comment(1)
should it be "overflow-anchor: auto" instead?Contraction
Z
1

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);
Zahara answered 26/10, 2017 at 15:56 Comment(0)
M
1

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>
Merrile answered 6/10, 2020 at 17:11 Comment(0)
H
0

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.

Heretical answered 12/12, 2016 at 20:9 Comment(0)
K
0

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.

Kattegat answered 17/2, 2020 at 0:21 Comment(0)
A
-1

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(); 
Alicyclic answered 3/10, 2019 at 12:52 Comment(0)
M
-2

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');
        }
    });
Marshamarshal answered 4/7, 2019 at 4:56 Comment(1)
change scrollTopMax to var maxScrollTop = element[0].scrollHeight - element.outerHeight();Marshamarshal
A
-2

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);
Accelerator answered 5/1, 2020 at 22:0 Comment(0)
T
-2

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
Trussell answered 6/2, 2020 at 22:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.