我有一个div,只有300像素大,我希望它在页面加载时滚动到内容的底部。这个div有动态添加的内容,需要一直向下滚动。现在如果用户决定向上滚动,我不希望它跳回底部,直到用户再次向下滚动

是否有可能有一个div,将保持滚动到底部,除非用户向上滚动,当用户滚动回底部时,它需要保持自己在底部,即使添加了新的动态内容。我该怎么做呢。


当前回答

这里有一个基于Ryan Hunt博客文章的解决方案。它取决于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>

注意,overflow-anchor目前在Safari中不起作用。

其他回答

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

以下是你所需要的(我尽了最大的努力,一路上进行了大量的谷歌搜索):

<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

我不能让前两个答案工作,其他答案都对我没有帮助。所以我从Reddit r/forhire和Upwork上给了三个人30美元,得到了一些非常好的答案。这个答案可以帮你省下90美元。



Justin Hundley / The Site Bros的解决方案

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

预览网站兄弟的解决方案

投资组合



莱尔梅克斯/斯维亚托斯拉夫·丘马科夫的解决方案

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

预览Sviatoslav的解决方案

投资组合



伊戈尔·鲁西诺夫的解决方案

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

预览Igor的解决方案

投资组合

与Bootstrap 5:

< span style=" font - family:宋体;max-height: 300 px;" > < div > <!——你这里的内容——> < / div > < / div >

这里有一个基于Ryan Hunt博客文章的解决方案。它取决于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>

注意,overflow-anchor目前在Safari中不起作用。