Scrollable Div to Stick to Bottom, When Outer Div Changes in Size

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

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>

height: 100% foils flexbox stick scrollbar to bottom unless moved

I couldn't get this to work using the method I describe in the question. See @kukkuz's answer that "you can't do that... in flexbox", that the fixed size is basically a requirement for it to work. Not only that, but even keeping fixed size I found that the basic example seems to lose its ability to stick to the bottom if you deviate slightly from the code in the snippet I quote.

What I found that did work was to flip it upside down (text upside down, everything), then flip it back in an outer container. The method was suggested by this answer:

https://stackoverflow.com/a/34345634

This transformation makes the browser think the scroll bottom is actually the top. Pinning to the top is easier for it to do than getting pinned to the bottom (since it can be modeled as the constant value 0, as opposed to an ever-growing number).

Pretty weird, but it appears to work in the browsers I've tried!

Keep overflow div scrolled to bottom unless user scrolls up

This might help you:

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

[EDIT], to match the comment...

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

whenever content is added, call the function updateScroll(), or set a timer:

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

if you want to update ONLY if the user didn't move:

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

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

Position sticky to bottom of scrollable element

You need to change the display of the .container to flex

display: flex;
flex-direction: column;

And the .footer needs to have a margin-top: auto;

See code here:

.container {
position: relative;
overflow-y: scroll;
height: 150px;
border: solid 1px black;
display: flex;
flex-direction: column;
}

.footer {
position: sticky;
right: 0;
bottom: 0;
float: right;
background-color: lightblue;
margin-top: auto;
}
<div class="container">
<table>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
</table>
<div class="footer">Footer</div>
</div>
<br/>
<div class="container">
<table>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
<tr><td>Hello</td></tr>
</table>
<div class="footer">Footer</div>
</div>

how do I always keep my html element stick to bottom of parent element,I am using overflow:hidden and giving height implicitly to parent element?

Wrap the note div in another div. Give the new div (parent) a relative positioning. Give the button in the child div an absolute position.

THis should solve your problem.

I made a JSFiddle to illustrate this: http://jsfiddle.net/koder613/18dt3yhL/8/

HTML(JSX):

<div className="noteParent">
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button onClick={deleteNoteInNote}>
{" "}
<DeleteIcon />{" "}
</button>
</div>
</div>

CSS:

.noteParent {
width:200px;
height: 150px;
position:relative;
}
.note {
height:100%;
overflow: auto;

}
.note button{
position:absolute;
bottom:0;
right:1rem;
}

How to change a scrolling element behavior on resize? (HTML, CSS, JavaScript)

This can be done using CSS, as I showed in this answer.

And here is the code part which make it happen.

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;}
/* 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; } }}
<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/>      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/>      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/>      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/>      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>

How to make the scrollbar always stick to the bottom when new data is added

[Edit]: I just found this codePen. It does exactly what you are looking for :)


I guess what you are looking for is :

window.scrollTo(0,document.body.scrollHeight);

instead of :

myRef.current.scrollTop = myRef.current.scrollHeight;

Of course, you need to change document.body.scrollHeight by the scrollable block. You can probably clean up a bit of code if your refs aren't used for anything else.

Here is the repro on Stackblitz. I only changed the line above from your own code.

P.S. : I got this answer from this SO thread :)

Make div stay at bottom of page's content all the time even when there are scrollbars

This is precisely what position: fixed was designed for:

#footer {
position: fixed;
bottom: 0;
width: 100%;
}

Here's the fiddle: http://jsfiddle.net/uw8f9/



Related Topics



Leave a reply



Submit