简体   繁体   English

除非用户向上滚动,否则保持溢出 div 滚动到底部

[英]Keep overflow div scrolled to bottom unless user scrolls up

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.我有一个只有 300 像素大的 div,我希望它在页面加载时滚动到内容的底部。 This div has content dynamically added to it and needs to stay scrolled all the way down.这个 div 有动态添加的内容,需要一直向下滚动。 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.是否有可能让一个 div 保持滚动到底部,除非用户向上滚动,并且当用户滚动回到底部时,即使添加了新的动态内容,它也需要将自己保持在底部。 How would I go bout creating this.我 go 如何创建它。

I was able to get this working with CSS only.我只能使用 CSS 来实现它。

The trick is to use display: flex;诀窍是使用display: flex; and flex-direction: column-reverse;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.假设您的目标浏览器支持flex-box ,唯一需要注意的是标记必须以相反的顺序排列。

Here is a working example.这是一个工作示例。 https://codepen.io/jimbol/pen/YVJzBg https://codepen.io/jimbol/pen/YVJzBg

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:每当添加内容时,调用函数 updateScroll(),或设置一个计时器:

//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:假设我们有以下 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. scrollHeight为您提供元素的高度,包括由于溢出而导致的任何不可见区域。 clientHeight gives you the CSS height or said in another way, the actual height of the element. clientHeight为您提供 CSS 高度,或者以另一种方式说,元素的实际高度。 Both methods returns the height without margin , so you needn't worry about that.两种方法都返回没有margin的高度,所以你不必担心。 scrollTop gives you the position of the vertical scroll. scrollTop为您提供垂直滚动的位置。 0 is top and max is the scrollHeight of the element minus the element height itself. 0 是顶部,最大值是元素的滚动高度减去元素本身的高度。 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.使用滚动条时,很难(对我来说是在 Chrome 中)将滚动条一直向下移动到底部。 so I threw in a 1px inaccuracy.所以我投入了 1px 的误差。 So isScrolledToBottom will be true even if the scrollbar is 1px from the bottom.因此即使滚动条距离底部 1px, isScrolledToBottom也会为真。 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.然后只需将元素的 scrollTop 设置到底部即可。

if(isScrolledToBottom)
    out.scrollTop = out.scrollHeight - out.clientHeight;

I have made a fiddle for you to show the concept: http://jsfiddle.net/dotnetCarpenter/KpM5j/我为你做了一个小提琴来展示这个概念:http: //jsfiddle.net/dotnetCarpenter/KpM5j/

EDIT: Added code snippet to clarify when isScrolledToBottom is true .编辑:添加代码片段以阐明isScrolledToBottom何时为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>

In 2020, you can use css snap , but before Chrome 81 the layout change will not trigger re-snap , a pure css chat ui works on Chrome 81, also you can check Can I use CSS snap . 2020 年可以使用css snap ,但在 Chrome 81 之前布局更改不会触发 re-snap纯 css 聊天 ui可以在 Chrome 81 上运行,您也可以检查Can I use CSS snap

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>

在此处输入图像描述

EDIT编辑

use scroll-snap-type: y proximity;使用scroll-snap-type: y proximity; , scroll up easier. , 向上滚动更容易。

$('#yourDiv').scrollTop($('#yourDiv')[0].scrollHeight);

现场演示: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>

  1. Run code snippet to see the effect. Run code snippet查看效果。 (PS: If Run code snippet is not working, try this: https://jsfiddle.net/Yeshen/xm2yLksu/3/ ) (PS:如果Run code snippet不起作用,试试这个: https ://jsfiddle.net/Yeshen/xm2yLksu/3/)

  2. How it work:它是如何工作的:

Default overflow is scroll from top to bottom.默认溢出是从上到下滚动。

transform: rotate(180deg) can make it scroll or load dynamic block from bottom to top. transform: rotate(180deg)可以使其从下到上滚动或加载动态块。

  1. Original idea:创见:

https://blog.csdn.net/yeshennet/article/details/88880252 https://blog.csdn.net/yeshennet/article/details/88880252

Based on Jim Halls solution and comments.基于 Jim Halls 的解决方案和评论。 https://stackoverflow.com/a/44051405/9208887 . 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.我还添加了一个带有flex 1 1 0%的元素,以确保文本在容器未满时从顶部开始。

 // 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 .这是基于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.它取决于overflow-anchor CSS 属性,它将滚动位置固定到滚动内容底部的元素。

 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.请注意, overflow-anchor目前在 Safari 中不起作用。

I couldn't get the top two answers to work, and none of the other answers were helpful to me.我无法得到前两个答案,其他答案都对我没有帮助。 So I paid three people $30 from Reddit r/forhire and Upwork and got some really good answers.所以我从 Reddit r/forhireUpwork向三个人支付了 30 美元,得到了一些非常好的答案。 This answer should save you $90.这个答案应该可以为您节省 90 美元。



Justin Hundley / The Site Bros' solution Justin Hundley / The Site Bros 的解决方案

HTML HTML

<div id="chatscreen">
  <div id="inner">
  
  </div>
</div>

CSS CSS

#chatscreen {
  width: 300px;
  overflow-y: scroll;
  max-height:100px;
}

Javascript 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预览网站兄弟的解决方案

portfolio文件夹



Lermex / Sviatoslav Chumakov's solution Lermex / Sviatoslav Chumakov 的解决方案

HTML HTML

<div id="chatscreen">

</div>

CSS CSS

#chatscreen {
  height: 300px;
  border: 1px solid purple;
  overflow: scroll;
}

Javascript 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);
});

Preview Sviatoslav's solution预览 Sviatoslav 的解决方案

portfolio文件夹



Igor Rusinov's Solution Igor Rusinov 的解决方案

HTML HTML

<div id="chatscreen"></div>

CSS CSS

#chatscreen {
  height: 100px;
  overflow: scroll;
  border: 1px solid #000;
}

Javascript 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);
});

Preview Igor's solution预览 Igor 的解决方案

portfolio文件夹

$('#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.这将使用$(document).height()属性从#yourDivID的高度计算 ScrollTop Position,这样即使将动态内容添加到 div,滚动条也将始终位于底部位置。 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.如果有人可以纠正这一点,那就太好了。

//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.这是我基于 dotnetCarpenter 的回答的版本。 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.我的方法是纯 jQuery,我为变量命名以使事情更清晰。发生的情况是,如果内容高度大于容器,我们向下滚动额外的距离以获得所需的结果。

Works in IE and chrome..适用于 IE 和 chrome..

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.吉姆霍尔的答案是可取的,因为当你向上滚动时它确实不会滚动到底部,它也是纯 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).然而,非常不幸的是,这不是一个稳定的解决方案:在 chrome 中(可能是由于上面 dotnetCarpenter 描述的 1-px 问题),即使没有用户交互(在添加元素时), scrollTop的行为也会不准确 1 个像素。 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.您可以设置scrollTop = scrollHeight - clientHeight ,但是当添加另一个元素时,这将使 div 保持在适当的位置,即“将自身保持在底部”功能不再起作用。

So, in short, adding a small amount of Javascript (sigh) will fix this and fulfill all requirements:因此,简而言之,添加少量 Javascript(叹气)将解决此问题并满足所有要求:

Something like https://codepen.io/anon/pen/pdrLEZ this (example by Coo), and after adding an element to the list, also the following:https://codepen.io/anon/pen/pdrLEZ这样的东西(Coo 的例子),在向列表中添加一个元素之后,还有以下内容:

container = ...
if(container.scrollHeight - container.clientHeight - container.scrollTop <= 29) {
    container.scrollTop = container.scrollHeight - container.clientHeight;
}

where 29 is the height of one line.其中 29 是一条线的高度。

So, when the user scrolls up half a line (if that is even possible?), the Javascript will ignore it and scroll to the bottom.因此,当用户向上滚动半行时(如果可能的话?),Javascript 将忽略它并滚动到底部。 But I guess this is neglectible.但我想这是可以忽略的。 And, it fixes the Chrome 1 px thingy.而且,它修复了 Chrome 1 px 的东西。

You can use something like this,你可以使用这样的东西,

var element = document.getElementById("yourDivID");
window.scrollTo(0,element.offsetHeight);

The following does what you need (I did my best, with loads of google searches along the way):以下是您需要的(我尽了最大努力,一路上进行了大量的谷歌搜索):

<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>

Note: You may have to adjust the alert position ( nposx and nposy ) in both scrollToBottom and scrollMessages to match your needs...注意:您可能需要调整scrollToBottomscrollMessages中的警报位置( nposxnposy )以满足您的需要......

And a link to my own working example, hosted on my server: https://night-stand.ca/jaretts_tests/chat_scroll.html还有一个链接到我自己的工作示例,托管在我的服务器上: https ://night-stand.ca/jaretts_tests/chat_scroll.html

Here is how I approached it.这是我的处理方式。 My div height is 650px.我的 div 高度是 650 像素。 I decided that if the scroll height is within 150px of the bottom then auto scroll it.我决定如果滚动高度在底部的 150px 范围内,则自动滚动它。 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.诀窍是计算:(a)当前 div 用户滚动位置和(b)div 滚动高度,两者都在添加新元素之前。

If a === b, we know the user is at the bottom before appending the new element.如果 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 });
    }

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).我试图对 Bootstrap 5 做同样的事情。我正在编写的页面是一个单窗口 html 工具,我希望两列具有可滚动的内容,并且一个需要反转,因为它是一个日志(另一个不太可能滚动,除非故意这样做)。 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).在我的实际应用程序中,另外两列左侧的第三列具有mh-100 col overflow-auto类,并且不需要内部行/列,因为没有标题可以粘贴在顶部(如果视口是,它只会正常滚动太小)。 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).列表有一个 ID,我用来选择和添加它们或删除顶部元素(这是反向列表中底部的<li>项)。

A smaller version is provided here:这里提供了一个较小的版本:

 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/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).如果您的视口高度小于整体内容,则标题应位于列表顶部,页面底部的所有内容(实际上是视口高度的 75%,但在本例中,标题不占用空间它是为)而设计的。

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.注意:我并不是真正的网络开发人员,只是为日常工作编写一些方便的基于 html 的工具,因此非常欢迎发表评论。

The solution I've found most user-friendly is combining the scroll-snap-align approach with a little bit of Javascript.我发现最用户友好的解决方案是将scroll-snap-align方法与一点点 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.该解决方案还有一个额外的好处,即它是一种渐进式增强:如果用户禁用了 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>

There is native support for this problem.这个问题有原生支持。

There is a method called *.scrollIntoView .有一个名为*.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>

With Bootstrap 5:使用 Bootstrap 5:

 <div class="overflow-auto d-flex flex-column-reverse" style="max-width: 300px; max-height: 300px;"> <div> <!-- Your content here --> </div> </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM