我有一个div,只有300像素大,我希望它在页面加载时滚动到内容的底部。这个div有动态添加的内容,需要一直向下滚动。现在如果用户决定向上滚动,我不希望它跳回底部,直到用户再次向下滚动
是否有可能有一个div,将保持滚动到底部,除非用户向上滚动,当用户滚动回底部时,它需要保持自己在底部,即使添加了新的动态内容。我该怎么做呢。
我有一个div,只有300像素大,我希望它在页面加载时滚动到内容的底部。这个div有动态添加的内容,需要一直向下滚动。现在如果用户决定向上滚动,我不希望它跳回底部,直到用户再次向下滚动
是否有可能有一个div,将保持滚动到底部,除非用户向上滚动,当用户滚动回底部时,它需要保持自己在底部,即使添加了新的动态内容。我该怎么做呢。
当前回答
在2020年,你可以使用css snap,但在Chrome 81之前,布局变化不会触发重新snap,纯css聊天ui在Chrome 81上工作,也可以检查我可以使用css snap。
这个演示将捕捉最后一个元素(如果可见),滚动到底部查看效果。
.container { overflow-y:滚动; overscroll-behavior-y:包含; 滚动抓拍型:y接近度; } .container > div > div:last child { scroll-snap-align:结束; } .container > div > div { 背景:浅灰色; 高度:3快速眼动; 字体大小:1.5快速眼动; } .container > div > div:n -child(2n) { 背景:灰色; } <div class="container" style="height:6rem"> < div > < div > < / div > 1 2 < div > < / div > < div > 3 < / div > 4 < div > < / div > 5 < div > < / div > < / div > < / div >
EDIT
使用scroll-snap类型:y接近度;,向上滚动更容易。
其他回答
我能够得到这个工作与CSS只。
诀窍在于:
display: flex;
flex-direction: column-reverse;
浏览器将底部视为顶部。假设您的目标浏览器支持灵活框,唯一需要注意的是标记必须以相反的顺序排列。
下面是一个工作示例。https://codepen.io/jimbol/pen/YVJzBg
以下是你所需要的(我尽了最大的努力,一路上进行了大量的谷歌搜索):
<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>
注意:你可能需要在scrollToBottom和scrollMessages中调整警报位置(nposx和nposy)来匹配你的需求…
还有一个链接到我自己的工作示例,托管在我的服务器上:https://night-stand.ca/jaretts_tests/chat_scroll.html
这个问题有原生的支持。
有一个叫做*. scrollintoview的方法。 在运行此方法一次之后,它将容器滚动保持在底部。 即使在容器中添加了新内容,它也会滚动到底部。
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>
这可能对你有帮助:
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;
});
我发现最友好的解决方案是将滚动-快照-对齐方法与少量Javascript结合起来。前一种解决方案本身的问题是,抓拍太强了,你必须滚动很远才能摆脱它。
相反,我们可以在容器滚动到底部时使用捕捉动态,然后在用户向上滚动超过某个阈值时禁用它。
这个解决方案还有一个额外的好处,那就是它是一个渐进的增强:如果用户禁用了Javascript,它就会退回到只使用css的方法。
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>