我有一个只有300像素大的div,当页面加载滚动到内容的底部时我想要它。此div具有动态添加到其中的内容,需要始终保持滚动状态。现在,如果用户决定向上滚动,我不希望它跳回到底部,直到用户再次向下滚动
除非用户向上滚动,当用户向后滚动到底部时,即使添加了新的动态内容,也可以使div保持滚动到底部。我该如何创造这个呢。
答案 0 :(得分:135)
我刚刚实现了这个,也许你可以使用我的方法。
假设我们有以下HTML:
<div id="out" style="overflow:auto"></div>
然后我们可以检查它是否滚动到底部:
var out = document.getElementById("out");
// allow 1px inaccuracy by adding 1
var isScrolledToBottom = out.scrollHeight - out.clientHeight <= out.scrollTop + 1;
scrollHeight为您提供元素的高度,包括因溢出而导致的任何不可见区域。 clientHeight为您提供CSS高度或以另一种方式表示元素的实际高度。两种方法都返回不带margin
的高度,因此您无需担心。 scrollTop为您提供垂直滚动的位置。 0是top,max是元素的scrollHeight减去元素高度本身。使用滚动条时,将滚动条一直向下移动可能很困难(我在Chrome中)。所以我投入1px不准确。因此,即使滚动条距底部为1px,isScrolledToBottom
也会为真。您可以将此设置为适合您的任何内容。
然后,只需将元素的scrollTop设置为底部即可。
if(isScrolledToBottom)
out.scrollTop = out.scrollHeight - out.clientHeight;
我为你展示了这个概念:http://jsfiddle.net/dotnetCarpenter/KpM5j/
编辑:
添加了代码段,以便在isScrolledToBottom
为true
时澄清。
将滚动条粘贴到底部
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>
答案 1 :(得分:112)
我只能使用CSS。
诀窍是使用display: flex;
和flex-direction: column-reverse;
浏览器将底部视为顶部。假设您定位支持flex-box
的浏览器,唯一需要注意的是标记必须按相反的顺序排列。
这是一个工作示例。 https://codepen.io/jimbol/pen/YVJzBg
答案 2 :(得分:98)
这可能会对您有所帮助:
var element = document.getElementById("yourDivID");
element.scrollTop = element.scrollHeight;
[编辑],以匹配评论...
function updateScroll(){
var element = document.getElementById("yourDivID");
element.scrollTop = element.scrollHeight;
}
每当添加内容时,调用函数updateScroll()或设置计时器:
//once a second
setInterval(updateScroll,1000);
如果您想仅在用户未移动时更新:
var scrolled = false;
function updateScroll(){
if(!scrolled){
var element = document.getElementById("yourDivID");
element.scrollTop = element.scrollHeight;
}
}
$("#yourDivID").on('scroll', function(){
scrolled=true;
});
答案 3 :(得分:26)
在2020年,您可以使用css snap,但是在Chrome 81之前,布局更改将not trigger re-snap,pure css chat ui在Chrome 81上有效,您也可以选中Can I use CSS snap。
该演示将捕捉最后一个元素(如果可见),滚动到底部以查看效果。
.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>
编辑
使用scroll-snap-type: y proximity;
,向上滚动更容易。
答案 4 :(得分:23)
$('#yourDiv').scrollTop($('#yourDiv')[0].scrollHeight);
答案 5 :(得分:13)
$('#div1').scrollTop($('#div1')[0].scrollHeight);
Or animated:
$("#div1").animate({ scrollTop: $('#div1')[0].scrollHeight}, 1000);
答案 6 :(得分:3)
$('#yourDivID').animate({ scrollTop: $(document).height() }, "slow");
return false;
这将使用#yourDivID
属性从$(document).height()
的高度计算ScrollTop位置,这样即使将动态内容添加到div,滚动条也始终位于底部位置。希望这可以帮助。但它也有一个小错误,即使我们向上滚动并从滚动条离开鼠标指针它会自动到达底部位置。如果有人能够纠正它也会很好。
答案 7 :(得分:2)
//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;
这是我的版本基于dotnetCarpenter的答案。我的方法是一个纯粹的jQuery,我命名变量以使事情更清楚。发生的事情是,如果内容高度大于容器,我们向下滚动额外的距离以达到所需的结果。
适用于IE和Chrome ..
答案 8 :(得分:2)
Jim Hall的答案是可取的,因为当您向上滚动时确实不会滚动到底部,它也是纯CSS。
但是非常不幸的是,这不是一个稳定的解决方案:在chrome中(可能由于上述dotnetCarpenter所述的1px问题),scrollTop
的行为即使不进行用户交互也无法准确地显示1个像素。元素添加)。您可以设置scrollTop = scrollHeight - clientHeight
,但是当添加另一个元素时,它将保持div的位置,也就是“保持自身在底部”功能不再起作用。
因此,简而言之,添加少量Javascript(叹气)将解决此问题并满足所有要求:
类似https://codepen.io/anon/pen/pdrLEZ的事情(例如Coo的例子),并且在向列表中添加元素之后,还执行以下操作:
container = ...
if(container.scrollHeight - container.clientHeight - container.scrollTop <= 29) {
container.scrollTop = container.scrollHeight - container.clientHeight;
}
其中29是一行的高度。
因此,当用户向上滚动半行(是否还可以吗?)时,Javascript将忽略它并滚动到底部。但是我想这是可以忽略的。而且,它可以修复Chrome 1 px的问题。
答案 9 :(得分:1)
.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>
Run code snippet
以查看效果。 (PS:如果Run code snippet
不起作用,请尝试以下操作:https://jsfiddle.net/Yeshen/xm2yLksu/3/)
工作方式:
默认溢出是从上到下滚动。
transform: rotate(180deg)
可以使其滚动或从下至上加载动态块。
答案 10 :(得分:0)
这是基于a blog post by Ryan Hunt的解决方案。它取决于overflow-anchor
CSS属性,该属性将滚动位置固定到滚动内容底部的元素。
const messages = [
'Expect rain today.',
'Tomorrow will be sunny.',
'Snow is coming next week.',
'Hailstorms are imminent.',
];
function addMessage() {
const $message = document.createElement('div');
$message.className = 'message';
$message.innerText = messages[(Math.random() * messages.length) | 0];
$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>
请注意,overflow-anchor
当前不适用于Safari或Edge,因此该解决方案当前不适用于所有浏览器。
答案 11 :(得分:0)
这就是我的处理方法。我的div高度是650px。我决定,如果滚动高度在底部的150px之内,则自动滚动它。否则,留给用户。
scripts
答案 12 :(得分:0)
您可以使用类似的东西
var element = document.getElementById("yourDivID");
window.scrollTo(0,element.offsetHeight);
答案 13 :(得分:0)
我无法获得最有效的两个答案,其他答案都对我没有帮助。因此,我向Reddit r/forhire和Upwork的三个人支付了30美元,并得到了一些非常好的答案。这个答案应该为您节省$ 90。
HTML
<div id="chatscreen">
<div id="inner">
</div>
</div>
CSS
#chatscreen {
width: 300px;
overflow-y: scroll;
max-height:100px;
}
JavaScript
$(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);
});
Preview the site bros' solution
HTML
<div id="chatscreen">
</div>
CSS
#chatscreen {
height: 300px;
border: 1px solid purple;
overflow: scroll;
}
JavaScript
$(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);
});
HTML
<div id="chatscreen"></div>
CSS
#chatscreen {
height: 100px;
overflow: scroll;
border: 1px solid #000;
}
JavaScript
$(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);
});
答案 14 :(得分:0)
以下可以满足您的需求(我尽力了,一路上进行了大量谷歌搜索):
<html>
<head>
<script>
// 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++;
}
}
</script>
<style>
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;
}
</style>
</head>
<body>
<div id="container" class="bordered inline-block centered">
<div class="inline-block">My Chat</div>
<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>
</body>
</html>
注意:您可能需要调整 nposx
和 nposy
中的 alert 位置(scrollToBottom
和 scrollMessages
)以满足您的需要...
以及指向我自己的工作示例的链接,该示例托管在我的服务器上:https://night-stand.ca/jaretts_tests/chat_scroll.html
答案 15 :(得分:0)
我设法让这个工作。诀窍是计算:(a) 当前 div 用户滚动位置和 (b) div 滚动高度,两者都在附加新元素之前。
如果 a === b,我们知道用户在追加新元素之前位于底部。
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 });
}