首页 > 解决方案 > 如何使移动键盘外观撞到 div 而不是绝对定位在它上面?

问题描述

我正在开发一个简单的聊天组件,我遇到了一个问题,在移动设备上,如果我单击文本框发送消息,而不是在其上方弹出消息列表,虚拟键盘反而绝对将自己定位在它。

这是不可取的,因为我希望用户在输入消息时能够看到最新消息。但是,我无法弄清楚如何纠正这种行为。(而且我不能将文本框放在messagesdiv 中,因为它应该始终位于底部)

我创建了一个示例片段来演示此处的问题(添加底部边距演示的问题与单击文本框并弹出移动键盘相同)

基本上,如果您滚动到底部并单击“添加边距”按钮,您会看到不是将内容推到它上面,这样您仍然可以看到最新消息,而是向上滚动。有没有办法避免这种情况?

这是代码的副本,以防小提琴出现故障:

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

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

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

标签: javascriptandroidhtmlcss

解决方案


幸运的是,有。这里的问题是您没有处理div 扩展.messages时的滚动位置(即获得更多边距)。.send-message我假设您想要调整它,以便滚动位置将最后一个可见消息作为其枢轴(即 div 展开之前的最后一个可见消息必须在 div 展开之后出现,反之亦然)。要调整所述滚动位置,这是一个最小的工作示例(我稍微更改了您的 HTML 内容,以便您可以指出最后一条消息是什么,并且我调整了您的 JS 代码):

function test() {
  let messageBox = document.querySelector('.messages')
  let beforeMessageBoxHeight = messageBox.clientHeight
  let afterMessageBoxHeight
  let messageBoxHeightDifference
  let beforeScrollTop = messageBox.scrollTop
  let afterScrollTop
  document.querySelector(".send-message").classList.toggle("some-margin")
  afterMessageBoxHeight = messageBox.clientHeight
  messageBoxHeightDifference = beforeMessageBoxHeight - afterMessageBoxHeight
  afterScrollTop = beforeScrollTop + messageBoxHeightDifference
  messageBox.scrollTop = afterScrollTop
}
.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">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 class="send-message">
    <input />
  </div>
</div>

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

这个想法是:

  • 在 div 展开之前获取clientHeight(可见高度,阅读 MDN 文档了解更多详细信息)
  • 获取scrollTop值(从最顶部可见/不可见元素到最顶部可见元素测量的像素数)
  • 当 div ( .send-message) 展开时,可见高度 ( clientHeight) 会自动缩小。该scrollTop值仍然相同,这意味着div 展开之前最顶部的可见元素仍然可见。然而,这不是我们想要的:我们希望在 div 展开之前最底部的可见元素保持可见
  • 我们测量 div 展开后和 div 展开前的高度差。从逻辑上讲,高度差是使可见消息的底部(在 div 展开之前)看起来不可见(由于溢出)的原因。
  • 要解决该问题,请将高度差添加到先前的scrollTop值,以便它在 div 展开之前很好地滚动到最底部的可见消息。
  • 瞧,它有效。当 div 缩回时,您可以应用相同的逻辑。

推荐阅读