admin管理员组文章数量:1125929
I have a div that is only 300 pixels big and I want it to when the page loads scroll to the bottom of the content. This div has content dynamically added to it and needs to stay scrolled all the way down. Now if the user decides to scroll up I don't want it to jump back to the bottom until the user scrolls all the way down again.
Is it possible to have a div that will stay scrolled to the bottom unless the user scrolls up and when the user scrolls back to the bottom it needs to keep itself at the bottom even when new dynamic content is added. How would I go about creating this?
I have a div that is only 300 pixels big and I want it to when the page loads scroll to the bottom of the content. This div has content dynamically added to it and needs to stay scrolled all the way down. Now if the user decides to scroll up I don't want it to jump back to the bottom until the user scrolls all the way down again.
Is it possible to have a div that will stay scrolled to the bottom unless the user scrolls up and when the user scrolls back to the bottom it needs to keep itself at the bottom even when new dynamic content is added. How would I go about creating this?
Share Improve this question edited Apr 6, 2023 at 18:39 TylerH 21.2k76 gold badges79 silver badges110 bronze badges asked Sep 4, 2013 at 12:49 Robert E. McIntoshRobert E. McIntosh 6,1055 gold badges28 silver badges38 bronze badges 024 Answers
Reset to default 671I was able to get this working with CSS only.
The trick is to use flex-direction: column-reverse
The browser treats the bottom like its the top. Assuming the browsers you're targeting support flex-box
, the only caveat is that the markup has to be in reverse order.
Here is a working example:
.container {
height: 100px;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
<div class="container">
<div>Bottom</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Hi</div>
<div>Top</div>
</div>
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;
});
I just implemented this and perhaps you can use my approach.
Say we have the following HTML:
<div id="out" style="overflow:auto"></div>
Then we can check if it scrolled to the bottom with:
var out = document.getElementById("out");
// allow 1px inaccuracy by adding 1
var isScrolledToBottom = out.scrollHeight - out.clientHeight <= out.scrollTop + 1;
scrollHeight gives you the height of the element, including any non visible area due to overflow. clientHeight gives you the CSS height or said in another way, the actual height of the element. Both methods returns the height without margin
, so you needn't worry about that. scrollTop gives you the position of the vertical scroll. 0 is top and max is the scrollHeight of the element minus the element height itself. When using the scrollbar it can be difficult (it was in Chrome for me) to get the scrollbar all the way down to the bottom. so I threw in a 1px inaccuracy. So isScrolledToBottom
will be true even if the scrollbar is 1px from the bottom. You can set this to whatever feels right to you.
Then it's simply a matter of setting the scrollTop of the element to the bottom.
if(isScrolledToBottom)
out.scrollTop = out.scrollHeight - out.clientHeight;
I have made a fiddle for you to show the concept: http://jsfiddle.net/dotnetCarpenter/KpM5j/
EDIT:
Added code snippet to clarify when isScrolledToBottom
is true
.
Stick scrollbar to bottom
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>
You can use CSS Scroll Snap; check Can I Use for support.
This demo will snap the last element if visible, scroll to bottom to see the effect.
.container {
overflow-y: scroll;
overscroll-behavior-y: contain;
scroll-snap-type: y proximity;
}
.container > div > div:last-child {
scroll-snap-align: end;
}
.container > div > div {
background: lightgray;
height: 3rem;
font-size: 1.5rem;
}
.container > div > div:nth-child(2n) {
background: gray;
}
<div class="container" style="height:6rem">
<div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
</div>
$('#yourDiv').scrollTop($('#yourDiv')[0].scrollHeight);
Live demo: http://jsfiddle.net/KGfG2/
$('#div1').scrollTop($('#div1')[0].scrollHeight);
Or animated:
$("#div1").animate({ scrollTop: $('#div1')[0].scrollHeight}, 1000);
.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>
https://jsfiddle.net/Yeshen/xm2yLksu/3/
How it works: By default overflow scrolling is from top to bottom. transform: rotate(180deg)
reverses this, so that scrolling or loading dynamic blocks is from bottom to top.
Original idea: https://blog.csdn.net/yeshennet/article/details/88880252
Based on Jim Halls solution and comments. https://stackoverflow.com/a/44051405/9208887.
I added additionally an element with flex 1 1 0%
to ensure the text starts at the top of the container when it's not full.
// just to add some numbers, so we can see the effect
// the actual solution requires no javascript
let num = 1001;
const container = document.getElementById("scroll-container");
document.getElementById("adder").onclick = () =>
container.append(
Object.assign(document.createElement("div"), {
textContent: num++
})
);
.scroll-wrapper {
height: 100px;
overflow: auto;
display: flex;
flex-direction: column-reverse;
border: 1px solid black;
}
.scroll-start-at-top {
flex: 1 1 0%;
}
<div class="scroll-wrapper">
<span class="scroll-start-at-top"></span>
<div id="scroll-container">
<div>1000</div>
</div>
</div>
<button id="adder">Add Text</button>
Here's a solution based on a blog post by Ryan Hunt. It depends on the overflow-anchor
CSS property, which pins the scrolling position to an element at the bottom of the scrolled content.
function addMessage() {
const $message = document.createElement('div');
$message.className = 'message';
$message.innerText = `Random number = ${Math.ceil(Math.random() * 1000)}`;
$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>
Note that overflow-anchor
doesn't currently work in Safari.
I couldn't get the top two answers to work, and none of the other answers were helpful to me. So here are 3 solutions:
Solution 1:
$(function(){
var scrolled = false;
var lastScroll = 0;
var count = 0;
$("#chatscreen").on("scroll", function() {
var nextScroll = $(this).scrollTop();
if (nextScroll <= lastScroll) {
scrolled = true;
}
lastScroll = nextScroll;
console.log(nextScroll, $("#inner").height())
if ((nextScroll + 100) == $("#inner").height()) {
scrolled = false;
}
});
function updateScroll(){
if(!scrolled){
var element = document.getElementById("chatscreen");
var inner = document.getElementById("inner");
element.scrollTop = inner.scrollHeight;
}
}
// Now let's load our messages
function load_messages(){
$( "#inner" ).append( "Test" + count + "<br/>" );
count = count + 1;
updateScroll();
}
setInterval(load_messages,300);
});
#chatscreen {
width: 300px;
overflow-y: scroll;
max-height:100px;
}
<div id="chatscreen">
<div id="inner">
</div>
</div>
Solution 2:
$(function(){
var isScrolledToBottom = false;
// Now let's load our messages
function load_messages(){
$( "#chatscreen" ).append( "<br>Test" );
updateScr();
}
var out = document.getElementById("chatscreen");
var c = 0;
$("#chatscreen").on('scroll', function(){
console.log(out.scrollHeight);
isScrolledToBottom = out.scrollHeight - out.clientHeight <= out.scrollTop + 10;
});
function updateScr() {
// allow 1px inaccuracy by adding 1
//console.log(out.scrollHeight - out.clientHeight, out.scrollTop + 1);
var newElement = document.createElement("div");
newElement.innerHTML = c++;
out.appendChild(newElement);
console.log(isScrolledToBottom);
// scroll to bottom if isScrolledToBotto
if(isScrolledToBottom) {out.scrollTop = out.scrollHeight - out.clientHeight; }
}
var add = setInterval(updateScr, 1000);
setInterval(load_messages,300); // change to 300 to show the latest message you sent after pressing enter // comment this line and it works, uncomment and it fails
// leaving it on 1000 shows the second to last message
setInterval(updateScroll,30);
});
#chatscreen {
height: 300px;
border: 1px solid purple;
overflow: scroll;
}
<div id="chatscreen">
</div>
Solution 3:
$(function(){
// Now let's load our messages
function load_messages(){
$( "#chatscreen" ).append( "<br>Test" );
}
var out = document.getElementById("chatscreen");
var c = 0;
var add = setInterval(function() {
// allow 1px inaccuracy by adding 1
var isScrolledToBottom = out.scrollHeight - out.clientHeight <= out.scrollTop + 1;
load_messages();
// scroll to bottom if isScrolledToBotto
if(isScrolledToBottom) {out.scrollTop = out.scrollHeight - out.clientHeight; }
}, 1000);
setInterval(updateScroll,30);
});
#chatscreen {
height: 100px;
overflow: scroll;
border: 1px solid #000;
}
<div id="chatscreen"></div>
//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;
Here is my version based on dotnetCarpenter's answer. My approach is a pure jQuery and I named the variables to make things a bit clearer.. What is happening is if the content height is greater then the container we scroll the extra distance down to achieve the desired result.
Works in IE and chrome..
$('#yourDivID').animate({ scrollTop: $(document).height() }, "slow");
return false;
This will calculate the ScrollTop Position from the height of #yourDivID
using the $(document).height()
property so that even if dynamic contents are added to the div the scroller will always be at the bottom position. Hope this helps. But it also has a small bug even if we scroll up and leaves the mouse pointer from the scroller it will automatically come to the bottom position. If somebody could correct that also it will be nice.
Jim Hall's answer is preferrable because while it indeed does not scroll to the bottom when you're scrolled up, it is also pure CSS.
Very much unfortunately however, this is not a stable solution: In chrome (possibly due to the 1-px-issue described by dotnetCarpenter above), scrollTop
behaves inaccurately by 1 pixel, even without user interaction (upon element add). You can set scrollTop = scrollHeight - clientHeight
, but that will keep the div in position when another element is added, aka the "keep itself at bottom" feature is not working anymore.
So, in short, adding a small amount of Javascript (sigh) will fix this and fulfill all requirements:
Something like https://codepen.io/anon/pen/pdrLEZ this (example by Coo), and after adding an element to the list, also the following:
container = ...
if(container.scrollHeight - container.clientHeight - container.scrollTop <= 29) {
container.scrollTop = container.scrollHeight - container.clientHeight;
}
where 29 is the height of one line.
So, when the user scrolls up half a line (if that is even possible?), the Javascript will ignore it and scroll to the bottom. But I guess this is neglectible. And, it fixes the Chrome 1 px thingy.
You can use something like this,
var element = document.getElementById("yourDivID");
window.scrollTo(0,element.offsetHeight);
Another solution is to use overflow-anchor
.
Full details here https://css-tricks.com/books/greatest-css-tricks/pin-scrolling-to-bottom/
TL;DR
<div id="scroller">
<!-- new content dynamically inserted here -->
<div id="anchor"></div>
</div>
#scroller * {
overflow-anchor: none;
}
#anchor {
overflow-anchor: auto;
height: 1px;
}
Caveats:
- No Safari support yet (https://caniuse.com/css-overflow-anchor)
- In order for scroll anchoring to work, the page must be scrolled at least once to begin with. (Has a simple JS workaround)
The solution I've found most user-friendly is combining the scroll-snap-align
approach with a little bit of Javascript. The problem with the former solution by itself is that the snap is too strong and you have to scroll far to get out of it.
Instead of that, we can use the snapping dynamic while the container is scrolled to the bottom and then disable it when the user scrolls up past a certain threshold.
This solution has the added benefit that it's a progressive enhancement: if the user has Javascript disabled, it will fall back to the CSS-only approach.
const container = document.getElementById("container");
const snap = document.getElementById("snap");
// Scroll the view to the bottom once initially
container.scrollTop = container.scrollHeight;
container.addEventListener("scroll", (event) => {
const target = event.currentTarget;
const scroll = target.scrollTop;
const maxScroll = target.scrollHeight - target.clientHeight;
const threshold = 50; // px
isScrollBottomedOut = maxScroll - scroll < threshold;
// If the user scrolls up more than the threshold, disable snapping
// If the user scrolls down again, reenable snapping
snap.style.display = isScrollBottomedOut ? "block" : "none";
});
#container {
width: 200px;
height: 500px;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
-ms-scroll-chaining: none;
overscroll-behavior: contain;
-ms-scroll-snap-type: y proximity;
scroll-snap-type: y proximity;
border: 2px solid black;
}
#snap {
scroll-snap-align: end;
}
<div id="container">
<ol>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ol>
<!-- This is the snapping target, if visible -->
<div id="snap"></div>
</div>
The following does what you need (I did my best, with loads of google searches along the way):
// no jquery, or other craziness. just
// straight up vanilla javascript functions
// to scroll a div's content to the bottom
// if the user has not scrolled up. Includes
// a clickable "alert" for when "content" is
// changed.
// this should work for any kind of content
// be it images, or links, or plain text
// simply "append" the new element to the
// div, and this will handle the rest as
// proscribed.
let scrolled = false; // at bottom?
let scrolling = false; // scrolling in next msg?
let listener = false; // does element have content changed listener?
let contentChanged = false; // kind of obvious
let alerted = false; // less obvious
function innerHTMLChanged() {
// this is here in case we want to
// customize what goes on in here.
// for now, just:
contentChanged = true;
}
function scrollToBottom(id) {
if (!id) { id = "scrollable_element"; }
let DEBUG = 0; // change to 1 and open console
let dstr = "";
let e = document.getElementById(id);
if (e) {
if (!listener) {
dstr += "content changed listener not active\n";
e.addEventListener("DOMSubtreeModified", innerHTMLChanged);
listener = true;
} else {
dstr += "content changed listener active\n";
}
let height = (e.scrollHeight - e.offsetHeight); // this isn't perfect
let offset = (e.offsetHeight - e.clientHeight); // and does this fix it? seems to...
let scrollMax = height + offset;
dstr += "offsetHeight: " + e.offsetHeight + "\n";
dstr += "clientHeight: " + e.clientHeight + "\n";
dstr += "scrollHeight: " + e.scrollHeight + "\n";
dstr += "scrollTop: " + e.scrollTop + "\n";
dstr += "scrollMax: " + scrollMax + "\n";
dstr += "offset: " + offset + "\n";
dstr += "height: " + height + "\n";
dstr += "contentChanged: " + contentChanged + "\n";
if (!scrolled && !scrolling) {
dstr += "user has not scrolled\n";
if (e.scrollTop != scrollMax) {
dstr += "scroll not at bottom\n";
e.scroll({
top: scrollMax,
left: 0,
behavior: "auto"
})
e.scrollTop = scrollMax;
scrolling = true;
} else {
if (alerted) {
dstr += "alert exists\n";
} else {
dstr += "alert does not exist\n";
}
if (contentChanged) { contentChanged = false; }
}
} else {
dstr += "user scrolled away from bottom\n";
if (!scrolling) {
dstr += "not auto-scrolling\n";
if (e.scrollTop >= scrollMax) {
dstr += "scroll at bottom\n";
scrolled = false;
if (alerted) {
dstr += "alert exists\n";
let n = document.getElementById("alert");
n.remove();
alerted = false;
contentChanged = false;
scrolled = false;
}
} else {
dstr += "scroll not at bottom\n";
if (contentChanged) {
dstr += "content changed\n";
if (!alerted) {
dstr += "alert not displaying\n";
let n = document.createElement("div");
e.append(n);
n.id = "alert";
n.style.position = "absolute";
n.classList.add("normal-panel");
n.classList.add("clickable");
n.classList.add("blink");
n.innerHTML = "new content!";
let nposy = parseFloat(getComputedStyle(e).height) + 18;
let nposx = 18 + (parseFloat(getComputedStyle(e).width) / 2) - (parseFloat(getComputedStyle(n).width) / 2);
dstr += "nposx: " + nposx + "\n";
dstr += "nposy: " + nposy + "\n";
n.style.left = nposx;
n.style.top = nposy;
n.addEventListener("click", () => {
dstr += "clearing alert\n";
scrolled = false;
alerted = false;
contentChanged = false;
n.remove();
});
alerted = true;
} else {
dstr += "alert already displayed\n";
}
} else {
alerted = false;
}
}
} else {
dstr += "auto-scrolling\n";
if (e.scrollTop >= scrollMax) {
dstr += "done scrolling";
scrolling = false;
scrolled = false;
} else {
dstr += "still scrolling...\n";
}
}
}
}
if (DEBUG && dstr) console.log("stb:\n" + dstr);
setTimeout(() => { scrollToBottom(id); }, 50);
}
function scrollMessages(id) {
if (!id) { id = "scrollable_element"; }
let DEBUG = 1;
let dstr = "";
if (scrolled) {
dstr += "already scrolled";
} else {
dstr += "got scrolled";
scrolled = true;
}
dstr += "\n";
if (contentChanged && alerted) {
dstr += "content changed, and alerted\n";
let n = document.getElementById("alert");
if (n) {
dstr += "alert div exists\n";
let e = document.getElementById(id);
let nposy = parseFloat(getComputedStyle(e).height) + 18;
dstr += "nposy: " + nposy + "\n";
n.style.top = nposy;
} else {
dstr += "alert div does not exist!\n";
}
} else {
dstr += "content NOT changed, and not alerted";
}
if (DEBUG && dstr) console.log("sm: " + dstr);
}
setTimeout(() => { scrollToBottom("messages"); }, 1000);
/////////////////////
// HELPER FUNCTION
// simulates adding dynamic content to "chat" div
let count = 0;
function addContent() {
let e = document.getElementById("messages");
if (e) {
let br = document.createElement("br");
e.append("test " + count);
e.append(br);
count++;
}
}
button {
border-radius: 5px;
}
#container {
padding: 5px;
}
#messages {
background-color: blue;
border: 1px inset black;
border-radius: 3px;
color: white;
padding: 5px;
overflow-x: none;
overflow-y: auto;
max-height: 100px;
width: 100px;
margin-bottom: 5px;
text-align: left;
}
.bordered {
border: 1px solid black;
border-radius: 5px;
}
.inline-block {
display: inline-block;
}
.centered {
text-align: center;
}
.normal-panel {
background-color: #888888;
border: 1px solid black;
border-radius: 5px;
padding: 2px;
}
.clickable {
cursor: pointer;
}
<div id="container" class="bordered inline-block centered">
<div id="messages" onscroll="scrollMessages('messages')">
test<br>
test<br>
test<br>
test<br>
test<br>
test<br>
test<br>
test<br>
test<br>
test<br>
</div>
<button onclick="addContent();">Add Content</button>
</div>
Note: You may have to adjust the alert position (nposx
and nposy
) in both scrollToBottom
and scrollMessages
to match your needs...
Easiest solution, css only
.container {
max-height: 100px;
overflow-y: scroll;
display: flex;
flex-direction: column-reverse; // stick scroll to bottom
// > ._wrapper_hack // cancel item shift ;)
}
<div class="container">
<div class="_wrapper_hack">
<div>Top</div>
<div>Hi</div>
<div>Hi</div>
<div>...</div>
<div>Hi</div>
<div>Hi</div>
<div>Bottom</div>
</div>
</div>
@thanks to Jim Hall's upvoted solution
but hope to highlight Nathan Arthur's comment
Here is how I approached it. My div height is 650px. I decided that if the scroll height is within 150px of the bottom then auto scroll it. Else, leave it for the user.
if (container_block.scrollHeight - container_block.scrollTop < 800) {
container_block.scrollTo(0, container_block.scrollHeight);
}
I managed to get this working. The trick is to calculate: (a) current div user scroll position and (b) div scroll height, both BEFORE appending the new element.
If a === b, we know the user is at the bottom before appending the new element.
let div = document.querySelector('div.scrollableBox');
let span = document.createElement('span');
span.textContent = 'Hello';
let divCurrentUserScrollPosition = div.scrollTop + div.offsetHeight;
let divScrollHeight = div.scrollHeight;
// We have the current scroll positions saved in
// variables, so now we can append the new element.
div.append(span);
if ((divScrollHeight === divCurrentUserScrollPosition)) {
// Scroll to bottom of div
div.scrollTo({ left: 0, top: div.scrollHeight });
}
I combined the solutions from dotnetCarpenter and Mr.Manhattan. But I was also having trouble with the scrollbar moving by itself when the user zooms or resizes the window due to changes in text-wrapping. For example if the div is responsive. So this is the solution I came up with to keep the scrollbar at the bottom when the window resizes. Also scrollTop + 1 was still not entirely correcting for floating point imprecision in some zoom levels, so changed it to scrollTop + 1.5
HTML
<div id="scroll-div" style="overflow: auto; height: 100%;"></div>
Javascript
var scrolledToBottom = true;
var lastWidth, lastHeight;
$('#scroll-div').on('scroll', (e) => {
var div = e.target;
if(div.scrollHeight - div.clientHeight < div.scrollTop + 1.5)
scrolledToBottom = true;
// Only assume user moved scrollbar if window was not resized/zoomed
else if(lastWidth === div.clientWidth && lastHeight === div.clientHeight)
scrolledToBottom = false;
else if(scrolledToBottom)
$(div).scrollTop(div.scrollHeight); // Scrollbar moved due to resizing, move it back to the bottom
lastWidth = div.clientWidth;
lastHeight = div.clientHeight;
});
$(window).on('resize', () => {
$('#scroll-div').trigger('scroll'); // Trigger scroll in case scrollbar appears/disappears
});
function addContent() {
// append stuff here
if(scrolledToBottom)
$('#scroll-div').scrollTop($('#scroll-div')[0].scrollHeight);
}
Add up to @Jim Hall Answer. If you want the new text to be inserted at the bottom instead of at the top, do like this:
correct: el.innerHTML = str + el.innerHTML;
wrong: el.innerHTML = el.innerHTML + str;
let i = 0
function addDivs(){
var cont = document.getElementsByClassName('container')[0];
var str = `<div class="inner">Hi'${i}'</div>`
cont.innerHTML = str + cont.innerHTML;
}
setInterval(function(){
i++
addDivs();
}, 2000);
.container {
height: 100px;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
<div class="container">
</div>
I was trying to to the same with Bootstrap 5. The page I'm writing is a single-window html tool and I wanted two columns to have scrollable contents, and one needs to be in reverse as it's a log (the other is unlikely to scroll unless done on purpose). The lists and their headers are also bottom-anchored and I was having difficulty getting the header to remain right on top of a flex scrollable list.
Thanks to the examples above I could figure out what I was missing and get the right class types to make it work.
Here is my full example. In my actual app there is a 3rd column left of the other two with class mh-100 col overflow-auto
and no need for an inner row/column as there is no title to stick on top (it will just scroll normally if viewport is too small). The lists have an ID I use to select and prepend to them or remove the top element (which is the bottom <li>
item on the reversed list).
A smaller version is provided here:
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<div class="vh-100 w-75 container-fluid">
<h1>2nd Level Scrolling Example</h1>
<div class="h-75 row align-items-end">
<div class="mh-100 col d-flex flex-column">
<div class="row align-items-end">
<div class="col"><h3>Normal scroll list, grow on top</h3></div>
</div>
<div class="row align-items-end overflow-auto">
<div class="mh-100 col">
<ul class="list-group">
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ut</li>
<li>tortor eu ex tincidunt pretium non eu nisl. Ut eu libero ac velit</li>
<li>ultricies dapibus. Donec id augue scelerisque, gravida est ut,</li>
<li>commodo sapien. Interdum et malesuada fames ac ante ipsum primis</li>
<li>in faucibus. Suspendisse volutpat fermentum finibus. Cras egestas</li>
<li>tempor tempor. Suspendisse potenti. Mauris ac tellus ultrices lectus</li>
<li>accumsan pellentesque. Nullam semper, nisi nec euismod ultrices, leo</li>
<li>sem bibendum sapien, in rutrum sapien massa id mi.</li>
</ul>
</div>
</div>
</div>
<div class="mh-100 col d-flex flex-column">
<div class="row align-items-end">
<div class="col"><h3>Reverse scroll list, grow on bottom</h3></div>
</div>
<div class="row align-items-end d-flex flex-column-reverse overflow-auto">
<div class="mh-100 col">
<ul class="list-group">
<li>sem bibendum sapien, in rutrum sapien massa id mi.</li>
<li>accumsan pellentesque. Nullam semper, nisi nec euismod ultrices, leo</li>
<li>tempor tempor. Suspendisse potenti. Mauris ac tellus ultrices lectus</li>
<li>in faucibus. Suspendisse volutpat fermentum finibus. Cras egestas</li>
<li>commodo sapien. Interdum et malesuada fames ac ante ipsum primis</li>
<li>ultricies dapibus. Donec id augue scelerisque, gravida est ut,</li>
<li>tortor eu ex tincidunt pretium non eu nisl. Ut eu libero ac velit</li>
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ut</li>
</ul>
</div>
</div>
</div>
</div>
</div>
If your viewport height is less than the overall content, the title should sit on top of the list, and everything on the bottom of the page (actually 75% of the viewport height, but in this example the title isn't taking the space it was designed for).
NB: I'm not really a web dev, just writing some handy html-based tools for day to day work, so comments are very welcome.
There is native support for this problem.
There is a method called *.scrollIntoView
.
After running this method once, it keeps the container scroll at the bottom.
Even after adding new content to the container, it scrolls to the bottom.
import {
AfterViewInit,
Directive,
ElementRef,
} from '@angular/core';
@Directive({
selector: '[aeScrollIntoView]',
})
export class ScrollIntoViewDirective implements AfterViewInit {
constructor(private readonly el: ElementRef<HTMLDivElement>) {}
ngAfterViewInit(): void {
this.el.nativeElement.scrollIntoView({ behavior: 'smooth' });
}
}
<div aeScrollIntoView>
Your long and dynamic content.
Whenever new content is added to this container, it scrolls to the bottom.
<div>
本文标签: javascriptKeep overflow div scrolled to bottom unless user scrolls upStack Overflow
版权声明:本文标题:javascript - Keep overflow div scrolled to bottom unless user scrolls up - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736678692a1947314.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论