Scrollable div to stick to bottom, when outer div changes in size

sticky div on scroll css
make div fixed after scrolling
fixed div on scroll jquery example
fixed div on scroll jquery example codepen
sticky div on scroll codepen
css position: sticky; bottom
css position: fixed inside scrollable div
position: sticky horizontal scroll

Here is an example chat app ->

The idea here is to have the .messages-container take up as much of the screen as it can. Within .messages-container, .scroll holds the list of messages, and in case there are more messages then the size of the screen, scrolls.

Now, consider this case:

  1. The user scrolls to the bottom of the conversation
  2. The .text-input, dynamically gets bigger

Now, instead of the user staying scrolled to the bottom of the conversation, the text-input increases, and they no longer see the bottom.

One way to fix it, if we are using react, calculate the height of text-input, and if anything changes, let .messages-container know

componentDidUpdate() {
  window.setTimeout(_ => {
    const newHeight = this.calcHeight();
    if (newHeight !== this._oldHeight) {
      this.props.onResize();
    }
    this._oldHeight = newHeight;
  });
}

But, this causes visible performance issues, and it's sad to be passing messages around like this.

Is there a better way? Could I use css in such a way, to express that when .text-input-increases, I want to essentially shift up all of .messages-container

2:nd revision of this answer

Your friend here is flex-direction: column-reverse; which does all you ask while align the messages at the bottom of the message container, just like for example Skype and many other chat apps do.

.chat-window{
  display:flex;
  flex-direction:column;
  height:100%;
}
.chat-messages{
  flex: 1;
  height:100%;
  overflow: auto;
  display: flex;
  flex-direction: column-reverse;
}

.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }

The downside with flex-direction: column-reverse; is a bug in IE/Edge/Firefox, where the scrollbar doesn't show, which your can read more about here: Flexbox column-reverse and overflow in Firefox/IE

The upside is you have ~ 90% browser support on mobile/tablets and ~ 65% for desktop, and counting as the bug gets fixed, ...and there is a workaround.

// scroll to bottom
function updateScroll(el){
  el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
  return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}

In the below code snippet I've added the 2 functions from above, to make IE/Edge/Firefox behave in the same way flex-direction: column-reverse; does.

function addContent () {
  var msgdiv = document.getElementById('messages');
  var msgtxt = document.getElementById('inputs');
  var atbottom = scrollAtBottom(msgdiv);

  if (msgtxt.value.length > 0) {
    msgdiv.innerHTML += msgtxt.value + '<br/>';
    msgtxt.value = "";
  } else {
    msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
  }
  
  /* if at bottom and is IE/Edge/Firefox */
  if (atbottom && (!isWebkit || isEdge)) {
    updateScroll(msgdiv);
  }
}

function resizeInput () {
  var msgdiv = document.getElementById('messages');
  var msgtxt = document.getElementById('inputs');
  var atbottom = scrollAtBottom(msgdiv);

  if (msgtxt.style.height == '120px') {
    msgtxt.style.height = 'auto';
  } else {
    msgtxt.style.height = '120px';
  }
  
  /* if at bottom and is IE/Edge/Firefox */
  if (atbottom && (!isWebkit || isEdge)) {
    updateScroll(msgdiv);
  }
}


/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;

function updateScroll(el){
  el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
  return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }

.chat-window{
  display:flex;
  flex-direction:column;
  height:100%;
}
.chat-messages{
  flex: 1;
  height:100%;
  overflow: auto;
  display: flex;
  flex-direction: column-reverse;
}

.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }


/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }

/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
@media screen and (-webkit-min-device-pixel-ratio:0) {
  .chat-messages-text{ overflow: visible; }
  /*  reset Edge as it identifies itself as webkit  */
  @supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
@-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
  <div class="chat-messages">
    <div class="chat-messages-text" id="messages">
      Long long content 1!<br/>
      Long long content 2!<br/>
      Long long content 3!<br/>
      Long long content 4!<br/>
      Long long content 5!<br/>
    </div>
  </div>
  <div class="chat-input">
    <textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
    <button onclick="addContent();">Add msg</button>
    <button onclick="resizeInput();">Resize input</button>
  </div>
</div>

Creating sliding effects using sticky positioning, <div id="sticky"> Scroll to see the sticky element sticking A sticky element remains confined to the parent container it is in. runs from top to bottom, starting with 50px of transparency and a color change with this your own and change it up, like the dimensions, number of elements, and type of content. Scrollable div to stick to bottom, when outer div changes in size. The idea here is to have the .messages-container take up as much of the screen as it can. Within .messages-container, .scroll holds the list of messages, and in case there are more messages then the size of the screen, scrolls.

You just need one CSS rule set:

.messages-container, .scroll {transform: scale(1,-1);}

That's it, you're done!

How it works: First, it vertically flips the container element so that the top becomes the bottom (giving us the desired scroll orientation), then it flips the content element so that the messages won't be upside down.

This approach works in all modern browsers. It does have a strange side effect, though: when you use a mouse wheel in the message box, the scroll direction is reversed. This can be fixed with a few lines of JavaScript, as shown below.

Here's a demo and a fiddle to play with:

//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
  if(e.deltaY) {
    e.preventDefault();
    e.currentTarget.scrollTop -= parseFloat(getComputedStyle(e.currentTarget).getPropertyValue('font-size')) * (e.deltaY < 0 ? -1 : 1) * 2;
  }
});

//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
  var inp = document.querySelector('.text-input');
  document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
  inp.value = '';
  inp.focus();
}
resize = function() {
  var inp = document.querySelector('.text-input');
  inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.messages-container {
  flex-shrink: 10;
  height: 100%;
  overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
  <div class="messages-container">
    <div class="scroll">
      <p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
      <p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10
    </div>
  </div>
  <textarea class="text-input" autofocus>Your message</textarea>
  <div>
    <button id="send" onclick="send();">Send input</button>
    <button id="resize" onclick="resize();">Resize input box</button>
  </div>
</div>

Scroll-Then-Fix Content, Assuming we've put a class of “fix-search” on a parent element. In this demo, if that centered image in the header changed height, it would  Scroll to bottom of div - Here's what WE use on my site (WE didn't write it, WE just found it somewhere since WE don't know Javascript too well. Otherwise Zenscroll will try to make both top & bottom of element visible, if possible. But In some cases there is a requirement when we have to scroll to div’s bottom content. all. Events.

Please try the following fiddle - https://jsfiddle.net/Hazardous/bypxg25c/. Although the fiddle is currently using jQuery to grow/resize the text area, the crux is in the flex related styles used for the messages-container and input-container classes -

.messages-container{
  order:1;
  flex:0.9 1 auto;
  overflow-y:auto;
  display:flex;
  flex-direction:row;
  flex-wrap:nowrap;
  justify-content:flex-start;
  align-items:stretch;
  align-content:stretch;
}

.input-container{
  order:2;
  flex:0.1 0 auto;
}

The flex-shrink value is set to 1 for .messages-container and 0 for .input-container. This ensures that messages-container shrinks when there is a reallocation of size.

Element size and scrolling, div > < style > #example { width : 300 px ; height : 200 px ; border : 25 px then browsers show the “overflowing” text at padding-bottom , that's normal. CSS-​positioned ( position is absolute , relative , fixed or sticky ), or but scrollLeft/​scrollTop can be changed, and the browser will scroll the element. If the original div is 200px and then the first daughter div is 100px, the remaining space of the second daughter div will be 100px. So, if you set the min-height and max-height each to 100px, then the div will fill the remaining space and display a scroll bar if the content is greater than the max-height.

I've moved text-input within messages, absolute positioned it to the bottom of the container and given messages enough bottom padding to space accordingly.

Run some code to add a class to conversation, which changes the height of text-input and bottom padding of messages using a nice CSS transition animation.

The JavaScript runs a "scrollTo" function at the same time as the CSS transition is running to keep the scroll at the bottom.

When the scroll comes off the bottom again, we remove the class from conversation

Hope this helps.

https://jsfiddle.net/cnvzLfso/5/

var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');

function scrollTo(element, to, duration) {
  if (duration <= 0) {
    doScollCheck = true;
    return;
  }
  var difference = to - element.scrollTop;
  var perTick = difference / duration * 10;

  setTimeout(function() {
    element.scrollTop = element.scrollTop + perTick;
    if (element.scrollTop === to) {
      doScollCheck = true;
      return;
    }
    scrollTo(element, to, duration - 10);
  }, 10);
}

function resizeInput(atBottom) {
  var className = 'bigger',
    hasClass;
  if (objConv.classList) {
    hasClass = objConv.classList.contains(className);
  } else {
    hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
  }
  if (atBottom) {
    if (!hasClass) {
      doScollCheck = false;
      if (objConv.classList) {
        objConv.classList.add(className);
      } else {
        objConv.className += ' ' + className;
      }
      scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
    }
  } else {
    if (hasClass) {
      if (objConv.classList) {
        objConv.classList.remove(className);
      } else {
        objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
      }
    }
  }
}

objMessages.addEventListener('scroll', function() {
  if (doScollCheck) {
    var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
    resizeInput(isBottom);
  }
});
html,
body {
  height: 100%;
  width: 100%;
  background: white;
}
body {
  margin: 0;
  padding: 0;
}
.conversation {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  position: relative;
}
.messages {
  overflow-y: scroll;
  padding: 10px 10px 60px 10px;
  -webkit-transition: padding .5s;
  -moz-transition: padding .5s;
  transition: padding .5s;
}
.text-input {
  padding: 10px;
  -webkit-transition: height .5s;
  -moz-transition: height .5s;
  transition: height .5s;
  position: absolute;
  bottom: 0;
  height: 50px;
  background: white;
}
.conversation.bigger .messages {
  padding-bottom: 110px;
}
.conversation.bigger .text-input {
  height: 100px;
}
.text-input input {
  height: 100%;
}
<div class="conversation">
  <div class="messages">
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is the last message
    </p>
    <div class="text-input">
      <input type="text" />
    </div>
  </div>
</div>

position: sticky is Amazing, what you can build with CSS position:sticky, how to use it, and what to watch out for. gets to a specific distance from the top/bottom/left/right egde of the scrolling viewport. It's like a position:fixed element that's a sleeper agent spy. font-size: 30px; div[item]{. display: flex;. } div[description] span{. display: inline-block;. So, in order to avoid overlapping them during scroll, the title gets a right margin that’s equal to the image’s width, which is 50px (plus an additional 2px for a cleaner line in Chrome). Here’s what’s going on: as we scroll horizontally, the title sticks 20px from the screen’s left edge and the image sticks 150px from the same.

You write;

Now, consider this case:

    The user scrolls to the bottom of the conversation
    The .text-input, dynamically gets bigger

CSS Tricks: Expanding Beyond a Parent div, Setting overflow property for main content area (full screen width) to hidden to stop horizontal scroll; Setting overflow property for inner content div (inside  Setting scrollTop to 0 or Infinity will make the element scroll to the very top/bottom respectively. Don’t take width/height from CSS We’ve just covered geometry properties of DOM elements, that can be used to get widths, heights and calculate distances.

How to position a div at the bottom of its container using CSS , Change an HTML5 input placeholder color with CSS · How to disable text selection How to align content of a div to the bottom using CSS ? is used when position of a division is relative to its parent (used in this example). height​: 200px; CSS | Scroll Padding bottom · CSS | border-bottom-right-radius Property · CSS  Test your JavaScript, CSS, HTML or CoffeeScript online with JSFiddle code editor. Stick div at top after scrolling example - JSFiddle - Code Playground Close

Making Elements Stick in CSS Using position: sticky ← Alligator.io, It allows us to make elements stick when the scroll reaches a certain point. We'​ll have a div container that will be a flex container and then 4 additional div elements pirate"> <img src="/images/pirate.svg" width="100" alt="Item 1"> </div​> <div position: -webkit-sticky; position: sticky; bottom: 1rem; align-self: flex-end​; }. When there is more than a page of content the container div becomes larger and extends down below the bottom of the viewport - the footer is still positioned at the bottom of the container div but this time you need to scroll down to the end of the page to see it. The footer is also set to width:100%; so it stretches across the whole page.

Absolute positioning - position, The position CSS property sets how an element is positioned in a document. The top, right, bottom, and left properties determine the final location of To see the effect of sticky positioning, select the position: sticky option and scroll this default we span 100% of the width of our parent element, and we are  Additionally, with position:sticky you aren't writing to the DOM during scrolling, so you won't be causing any forced layouts & layout recalculations. As a result, the browser is able to move this operation to the GPU and you get very smooth scrolling even when sticky elements are in play. It's especially smooth in mobile Sarari.

Comments
  • Please elaborate the css/js used to grow the text-input.
  • Can you make a fiddle to better elaborate your problem?
  • About resizing the input text, may I ask why a user should be able to do that? ... Most chat apps don't, and if they do, they don't step up the messages when a user resize so users a aware of this behavior (Skype for example).
  • @stepan: Why do you want to resize the text input?
  • The chat app we use at work does allow the input box to be resized, and it's a very handy feature (it doesn't keep the conversation scrolled to the bottom, but it would be nice if it did). Normally, I size it to just one or two lines so that I can see more of the conversation, but sometimes I need to make lengthier comments that may include copy/pasted content, code snippets, or even bulleted lists. In those cases I like to be able to see my whole comment before I send it.
  • How come both you and @HaZardouS have used the same exact examples?
  • @AhmadBaktashHayeri Simple, I checked his solution/fiddle before I made mine, to see if it worked well, and then I saw a better way then the one he suggested, so I decided to fix his in the way the OP asked, to make it easy to compare the two.
  • I'd prefer you credited me when you posted your solution.
  • I will credit you for borrowing your html structure, the important part of the CSS is completely rewritten, though I reused the same class names. This is not uncommon that some uses parts of your already posted code, some even make a full copy of it, change 1 line and post it as their own (which I didn't and would never do). If you like, just parse through my older answers and you will find some of them proving I didn't steal your idea here.
  • @LGSon I suggested ways for you to improve your answer, which is what comments are for. Don't take it so personally. Your answer is better as a result. I've made my point, so I'll stop bugging you now. (We can discuss what qualifies as a "hack" elsewhere if you like).
  • @DoctorDestructo good idea here! I awarded the answer to the other one, mainly because it was possible to not have any js, if done in webkit. This is a great solution though, and I'll remember how nifty it is to use scale for the future
  • @StepanParunashvili Thanks, glad you like it! And no hard feelings re. your acceptance of LGSon's answer. I'd upvote it myself if you mentioned your browser preference in your question. Otherwise, compatibility is king as far as I'm concerned (but I don't expect everyone to think that way). If you're going with his solution, you might want to check out the bug reports in the SO post he linked. I get the impression that another CSS property (either justify-content or align-content) will eventually be required if Webkit follows the latest spec, but I could be wrong.
  • @DoctorDestructo The flex-direction:column; justify-content:flex-end does the same as flex-direction: column-reverse; justify-content:flex-start though the scrollbar is missing in webkit as well, so the missing scroll is an issue that webkit got fixed for the ´column-reverse´ property and the other yet to come.