【问题标题】:Prevent div scrolling up when bumped by div below it?防止 div 被其下方的 div 碰撞时向上滚动?
【发布时间】:2020-04-22 23:47:24
【问题描述】:

我昨天问了a similar question,但解释得很糟糕,并没有说明我对纯 CSS 解决方案的渴望,我认为这应该是可能的,所以我再试一次。

基本上,我有一个问题,我有一个可滚动消息的 div 和它下面的输入字段。当我单击一个按钮时,我希望输入字段向上增加 100 像素,而消息的 div 也不滚动。

Here is a fiddle that demonstrates the problem in its entirety

如您所见,当您单击“添加边距”按钮时,消息 div 也会向上滚动。我希望它留在原来的位置。同样,如果您稍微向上滚动,只能看到倒数第二条消息,单击按钮应该同样在单击时保留该位置。

有趣的是,这种行为“有时”被保留了下来。例如,在某些情况下(我不能完全推断)滚动位置被保留。我只是希望它始终如一地表现。

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
};

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
     width: 400px;
     height: 300px;
     border: 1px solid #333;
     display: flex;
     flex-direction: column;
}
 .messages {
     overflow-y: auto;
     height: 100%;
}
 .send-message {
     width: 100%;
     display: flex;
     flex-direction: column;
}
 .some-margin {
     margin-bottom: 100px;
}
<div class="container">
   <div class="messages">
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
   </div>
   <div class="send-message">
      <input />
   </div>
</div>
<button onclick="test()">add margin</button>

【问题讨论】:

  • 你好,我是用JS回答你问题的人。我想告诉你一件你可能误解的事情。您提到:messages div 也会向上滚动。实际上,向上滚动的不是 div,而是 div 的高度在减少。那么这有什么关系呢?嗯,这意味着scrollbar 没有调整它的位置。滚动条仅记住其相对于其容器顶部的位置。当 div 的高度减小时,滚动条似乎在向上滚动,但实际上并非如此。
  • 您的帖子非常有意义(从某种意义上说,人们知道您想要什么结果),但我只是分享一些东西,以防您不了解知识(以防它可能对您有所帮助提出解决上述问题的想法);-)
  • 是的,非常正确,感谢您的澄清。它只是“看起来”像是在向上滚动,因为您看不到曾经可以看到的较低内容。实际上,滚动位置没有改变。有点错觉。是的,这可能有助于提出解决方案。

标签: javascript html css scroll scrollbar


【解决方案1】:

这是一个有趣的解决方案,您可能会喜欢。

我们对 div 的了解是它只保留滚动条的顶部位置,因此如果高度因任何原因发生变化,滚动条将保持不变,这就是导致您出现问题的原因。

作为解决方法,您可以使用 transform: rotate(180deg) scaleX(-1);.messages 翻转 180 度并向后翻转 .message 以取消翻转内容,然后 div 将自动保持底部滚动条(即顶部)。

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin")
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
  transform: rotate(180deg) scaleX(-1);
}

.message
{
  transform: rotate(180deg) scaleX(-1);
}
.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello1</div>
  <div class="message">hello2</div>
  <div class="message">hello3</div>
  <div class="message">hello4</div>
  <div class="message">hello5</div>
  <div class="message">hello6</div>
  <div class="message">hello7</div>
  <div class="message">hello8</div>
  <div class="message">hello9</div>
  <div class="message">hello10</div>
  <div class="message">hello11</div>
  <div class="message">hello12</div>
  <div class="message">hello13</div>
  <div class="message">hello14</div>
  <div class="message">hello15</div>
  <div class="message">hello16</div>
  <div class="message">hello17</div>
  <div class="message">hello18</div>
  <div class="message">hello19</div>
  <div class="message">hello20</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>

【讨论】:

  • 这有鼠标滚轮向后滚动的问题(向上滚动向下滚动向下滚动向上)
  • 好的,我在这方面花了很多时间,但现在其他开发人员仍有 3 个您不想要的答案 :) 我希望能帮助您祝您好运。但作为一个建议,谁花时间试图回答你的问题,你不会因为这样做而被收费。
  • 当然。还是谢谢
  • 这很聪明 ;-) 不幸的是,反向滚动有点令人反感。
【解决方案2】:

滚动条的正常行为是位于顶部,因此当您在页面加载时将其设置为底部时,您必须自己维护它,因为当下面的内容推动它时,div 滚动将移动到顶。

所以我有两个解决方案给你:

  1. 反转消息 div 中的消息,这样最后一条消息将是第一条消息,因此滚动条将始终位于顶部。

  2. 我创建了一个 javascript 函数来滚动到任何元素的底部,因此您只需在想要滚动到底部时调用它。

function scrollbottom(e)
    {
      e.scrollTop = e.clientHeight;
    }

检查sn-p

var elem = document.querySelector(".messages");

window.onload = function(e){ 
  scrollbottom(elem);
}

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
  scrollbottom(elem);
}

function scrollbottom(e)
{
  e.scrollTop = e.clientHeight;
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>

【讨论】:

  • 不幸的是,我不想滚动到只滚动到底部。我想保持它的位置。此外,这很可能可以在没有 Javascript 的情况下完成。来自 OP:“同样,如果您稍微向上滚动,因此您只能看到倒数第二条消息,单击按钮应该同样在单击时保留该位置。”
【解决方案3】:

你可以反过来做,把高度给.messages而不是给.container,在这种情况下它不会影响消息div但是如果你把它给.container 它将推动您的 div,因为边距位于具有高度的主 div 内。

检查这个sn-p

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
  width: 400px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  height: 300px;
  overflow-y: auto;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 50px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello1</div>
  <div class="message">hello2</div>
  <div class="message">hello3</div>
  <div class="message">hello4</div>
  <div class="message">hello5</div>
  <div class="message">hello6</div>
  <div class="message">hello7</div>
  <div class="message">hello8</div>
  <div class="message">hello9</div>
  <div class="message">hello10</div>
  <div class="message">hello11</div>
  <div class="message">hello12</div>
  <div class="message">hello13</div>
  <div class="message">hello14</div>
  <div class="message">hello15</div>
  <div class="message">hello16</div>
  <div class="message">hello17</div>
  <div class="message">hello18</div>
  <div class="message">hello19</div>
  <div class="message">hello20</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>

【讨论】:

  • 这不起作用,因为边距需要应用于.send-message。如果你看看我的prior question,这是因为它类似于在移动设备上打开虚拟键盘的边距凸起(边距底部只是 1:1 替换以调试解决方案)
【解决方案4】:

简单的解决方法是将之前的scrollTop 设置为切换。这里我使用dataset来存储之前的scrollTop值,你也可以使用变量。

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
}

function test() {
  let state = document.querySelector(".send-message").classList.toggle("some-margin")
  let div = document.querySelector(".messages");

  if (state) {
    div.dataset.top = div.scrollTop;
    div.scrollTop += 100 // same as margin-bottom of .some-margin
  } else {
    div.scrollTop = div.dataset.top;
  }
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
    <div class="message">hello1</div>
    <div class="message">hello2</div>
    <div class="message">hello3</div>
    <div class="message">hello4</div>
    <div class="message">hello5</div>
    <div class="message">hello6</div>
    <div class="message">hello7</div>
    <div class="message">hello8</div>
    <div class="message">hello9</div>
    <div class="message">hello10</div>
    <div class="message">hello11</div>
    <div class="message">hello12</div>
    <div class="message">hello13</div>
    <div class="message">hello14</div>
    <div class="message">hello15</div>
    <div class="message">hello16</div>
    <div class="message">hello17</div>
    <div class="message">hello18</div>
    <div class="message">hello19</div>
    <div class="message">hello20</div>
  </div>
  <div class="send-message">
    <input />
  </div>
</div>

<button onclick="test()">add margin</button>

【讨论】:

    【解决方案5】:

    我的意思是,以下是完全解决您的示例的纯 CSS 解决方案:

    .some-margin{margin-bottom:100px;}
    
    .messages{margin-top:-100px;}
    

    但我认为它不会帮助您解决虚拟键盘的原始问题。但也许这可以激发其他人的解决方案。

    主要问题似乎是滚动条已经在做你想要的:

    保持其位置,以便最近阅读的内容可见。

    除了滚动条决定你想查看当前可见内容的顶部,而不是底部。 (如果使用数字更容易查看:https://jsfiddle.net/1co3x48n/

    如果你愿意使用 javascript,有各种各样的答案: https://www.google.com/search?q=scrollbar+always+on+bottom+css+site:stackoverflow.com

    老实说,您可以浏览大约 3 多页并仅找到 Javascript 答案这一事实告诉我,您不会找到仅 CSS 的答案,当然也不会找到与浏览器广泛兼容的答案。

    【讨论】:

      【解决方案6】:

      像上面的例子一样创建一个容器,但是在你开始输入的时候改变 CSS。

      我没有设置滚动条位置,只是向上移动整个消息容器。因此,无论您的滚动位置是什么,都将保持不变。至少往下看,你当然看不到顶部的线条。

      您应该能够根据需要调整 CSS。如果你使用 :focus 或稍微不同的 CSS 设置,你甚至可以不用 JS,要有创意:)

      function activate(){
          document.querySelector("#container").classList.toggle("active");
        }
      #container{
          position:relative;
          width:300px;
          height:100vh;
          max-height:230px;
          overflow:hidden;
        }
        #messages{
          position:absolute;
          bottom:30px;
          overflow:auto;
          height:200px;
          width:100%;
          background:#eee;
        }
        #container.active #messages {
          bottom:100px;
        }
        #send-message{
          position:absolute;
          border:1px solid #ccc;
          bottom:0;
          height:28px;
        }
        #container.active #send-message{
          height:100px;
        }
      <div id="container">
        <div id="messages">
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        <div class="message">hello</div>
        </div>
        <div id="send-message">
          <input id="newmessage" onclick="activate()" type="text" />
        </div>
      </div>

      【讨论】:

        【解决方案7】:

        您必须在添加边距时将document.querySelector(".messages").scrollTop = 10000; 添加到test 函数然后scrollTop 转到结尾 在加载时您设置scrolltop 并且当您添加边距或删除边距时scrolltop 没有再次设置 把你的脚本改成这样

        <script>
            window.onload = function(e){ 
            document.querySelector(".messages").scrollTop = 10000;
        }
        
        function test() {
            document.querySelector(".send-message").classList.toggle("some-margin")
                    document.querySelector(".messages").scrollTop = 10000;
        }
            </script>
        

        【讨论】:

          【解决方案8】:

          js切换功能好像有问题。我已将其更改为简单的隐藏和显示。我还在输入标签上添加了一个宽度。您还需要在发送消息 div 和 bottom: 0 上添加一个 position: absolute ,使其保持在底部。然后将 position: relative 放在容器上,以便发送消息 div 留在容器内。这意味着消息容器不会影响消息本身并将它们向上推送。

          window.onload = function(e) {
            document.querySelector(".messages").scrollTop = 10000;
          };
          
          function test() {
            var x = document.querySelector(".send-message");
          
            if (x.style.display === "none") {
              x.style.display = "block";
            } else {
              x.style.display = "none";
            }
          }
          .container {
               width: 400px;
               height: 300px;
               border: 1px solid #333;
               display: flex;
               flex-direction: column;
               position: relative;
          }
           .messages {
               overflow-y: auto;
               height: 100%;
          }
           .send-message {
               width: 100%;
               display: flex;
               flex-direction: column;
               position: absolute;
               bottom: 0;
          }
           .send-message input {
               width:100%;
          }
           .some-margin {
               margin-bottom: 100px;
          }
          
           
           
          <div class="container">
             <div class="messages">
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">test</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
                <div class="message">hello</div>
             </div>
             <div class="send-message">
                <input />
             </div>
          </div>
          <button onclick="test()">add margin</button>

          【讨论】:

          • 我不明白这个解决方案。单击添加边距时似乎隐藏了发送消息文本框?
          【解决方案9】:

          不幸的是,所提供的解决方案似乎都不起作用。将onFocus 用于文本框的问题在于,如果文本框已经具有焦点,它将不适用。到目前为止,我想出的最接近的解决方案是:

          componentDidMount() {
            this.screenHeight = window.innerHeight;
          
            let chatElem = document.querySelector(".conversations-chat");
          
            window.addEventListener('resize', () => {
              let diff = this.screenHeight - window.innerHeight;
          
              chatElem.scrollTop += diff;
          
              this.screenHeight = window.innerHeight;      
            });
          }
          

          但是,这似乎只是有时有效。单击文本框时它可以 100% 工作,但由于某种原因,在关闭虚拟键盘时,它只有在足够向上滚动时才有效。否则每次都会重置到相同的位置(靠近底部,但不完全是底部)。

          不确定是什么原因造成的。我想明天将不得不进行更多调查。这是迄今为止最接近的。

          【讨论】:

          • 就像我们在回答中所说的那样,仅使用 CSS 似乎是不可能的。 (你的JS代码有问题,就是chatElem.scrollTop不能小于0,所以如果太靠近顶部,它会尝试变成负数,改为0,然后当你再次放大屏幕时,会出现一个以前没有的滚动条。)
          • 同理,chatElem.scrollTop 不能超过 chatElem.scrollHeight - chatElem.offsetHeight,所以它会在之前没有的地方创建一个额外的滚动如果你尝试这样做。
          【解决方案10】:

          这可能是我们可以探索的另一种方法。如果我们不想使用滚动位置,并且如果我们在不失去可用性的情况下缩小消息容器是可以的。然后我们可以尝试下面的方法。为了更好地理解,请查看整页输出。

          window.onload = function(e) {
            document.querySelector(".messages").scrollTop = 10000;
          };
          
          function test() {
            document.querySelector(".send-message").classList.toggle("some-margin");
            document.querySelector(".messages").classList.toggle("scale");
          }
          .container {
            width: 400px;
            height: 300px;
            border: 1px solid #333;
            position: relative;
          }
          
          .messages {
            position: absolute;
            top: 0px;
            left: 0px;
            overflow-y: auto;
            /* leave 1.4 rem space for input */
            height: calc(300px - 1.4rem);
            width: 100%;
          }
          
          .scale {
            /*half of the 100 margin */
            top: -50px;
            /* scale  = (totalHeight - 100px)/totalHeight */
            transform: scaleY(0.64);
          }
          
          .send-message {
            position: absolute;
            bottom: 0;
            width: 100%;
            overflow: hidden;
          }
          
          .some-margin {
            margin-bottom: 100px;
          }
          <div class="container">
            <div class="messages">
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello</div>
              <div class="message">hello4</div>
              <div class="message">hello3</div>
              <div class="message">hello2</div>
              <div class="message">hello1</div>
            </div>
            <div class="send-message">
              <input style="width:100%" />
            </div>
          </div>
          <button onclick="test()">add margin</button>

          【讨论】:

            【解决方案11】:

            我能想到的一个纯粹的不完美的 CSS 方法如下:

            window.onload = function(e){ 
              document.querySelector(".messages").scrollTop = 10000;
            }
            
            document.querySelector('button').addEventListener('click', test)
            
            function test() {
              document.querySelector(".send-message").classList.toggle("some-margin")
              document.querySelector('.wrapper').classList.toggle('wrapper--transformed')
            }
            .container {
              width: 400px;
              height: 300px;
              border: 1px solid #333;
              display: flex;
              flex-direction: column;
            }
            
            .messages {
              position: relative;
              overflow-y: auto;
              height: 100%;
            }
            
            .wrapper {
              display: block;
            }
            
            .wrapper--transformed {
              transform: translateY(-100px);
              margin-bottom: -100px;
            }
            
            .send-message {
              width: 100%;
              display: flex;
              flex-direction: column;
            }
            
            .some-margin {
              margin-bottom: 100px;
            }
            <div class="container">
              <div class="messages">
                <div class = "wrapper">
                  <div class="message">hello1</div>
                  <div class="message">hello2</div>
                  <div class="message">hello3</div>
                  <div class="message">hello4</div>
                  <div class="message">hello5</div>
                  <div class="message">hello6</div>
                  <div class="message">hello7</div>
                  <div class="message">hello8</div>
                  <div class="message">hello9</div>
                  <div class="message">hello1</div>
                  <div class="message">hello2</div>
                  <div class="message">hello3</div>
                  <div class="message">hello4</div>
                  <div class="message">hello5</div>
                  <div class="message">hello6</div>
                  <div class="message">hello7</div>
                  <div class="message">hello8</div>
                  <div class="message">hello9</div>
                  <div class="message">hello1</div>
                  <div class="message">hello2</div>
                </div>
              </div>
              <div class="send-message">
                <input />
              </div>
            </div>
            <button>add margin</button>

            当有边距时,最上面的部分有一点问题。这里的技巧是使用负边距并使用 translate 来“向上滚动”(不是真正的滚动,只是一种错觉)wrapper div。

            【讨论】:

            • 最上面的部分问题有点问题,是的。此外,这是否假定知道移动键盘的确切尺寸以知道补偿多少?我不确定我是否可以访问这些信息。
            • 回答您的问题:是的。上述技术必须知道键盘的确切高度。但是,有一些方法可以使用 JS 来确定元素的高度并更改 CSS。
            • 如何确定移动虚拟键盘的高度?
            • @RyanPeschel 那个虚拟键盘是 HTML 的一个元素吗(例如 div)?
            • @RyanPeschel 哦。那么假设您所指的虚拟键盘是移动设备具有的本机键盘是否正确?在桌面呢?那个虚拟键盘是怎么生成的?
            【解决方案12】:

            它可以很简单地用锚名称来解决。看看https://jsfiddle.net/gvbz93sx/的JS小提琴

            HTML 更改 &lt;a name="bottom"&gt;&lt;/a&gt;

            JS 变化 document.location.hash = "#bottom";

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-12-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多