# 前置

  1. 我们都知道,聊天记录是滚动的,所以,我们需要知道滚动条的位置,然后,通过滚动条的位置,来定位到聊天记录的位置。
  2. 聊天记录的 flex-directioncolumn-reverse 的,所以最底下的 container.scrollTop 为 0,上滑动减少
  3. getBoundingClientRect 可以获取到元素的位置,包括 topbottomleftright 等。这些都是相对于视口的位置。

# 代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Scroll to Message Example</title>
    <style>
      body {
        margin: 0;
        font-family: Arial, sans-serif;
      }
      #message-container {
        display: flex;
        flex-direction: column-reverse; /* 反向排列 */
        height: 400px; /* 固定高度 */
        overflow-y: auto; /* 垂直滚动 */
        border: 1px solid #ccc;
        padding: 16px;
        box-sizing: border-box;
      }
      .message {
        background-color: #f0f0f0;
        padding: 8px 12px;
        margin-bottom: 8px;
        border-radius: 4px;
      }
      .highlight-message {
        background-color: yellow !important; /* 高亮样式 */
      }
    </style>
  </head>
  <body>
    <h1>Scroll to Message Example</h1>
    <div id="message-container">
      <!-- 消息列表 -->
      <div class="message" data-message-id="1">Message 1 (Oldest)</div>
      <div class="message" data-message-id="2">Message 2</div>
      <div class="message" data-message-id="3">Message 3</div>
      <div class="message" data-message-id="4">Message 4</div>
      <div class="message" data-message-id="5">Message 5</div>
      <div class="message" data-message-id="6">Message 6</div>
      <div class="message" data-message-id="7">Message 7</div>
      <div class="message" data-message-id="8">Message 8</div>
      <div class="message" data-message-id="9">Message 9</div>
      <div class="message" data-message-id="10">Message 10 (Newest)</div>
      <div class="message" data-message-id="11">Message 11</div>
      <div class="message" data-message-id="12">Message 12</div>
      <div class="message" data-message-id="13">Message 13</div>
      <div class="message" data-message-id="14">Message 14</div>
      <div class="message" data-message-id="15">Message 15</div>
      <div class="message" data-message-id="16">Message 16</div>
      <div class="message" data-message-id="17">Message 17</div>
      <div class="message" data-message-id="18">Message 18</div>
    </div>
    <button onclick="scrollToMessage(18)">Scroll to Message 18</button>
    <script>
      function scrollToMessage(messageId) {
        const container = document.getElementById("message-container");
        const targetElement = container.querySelector(
          `[data-message-id="${messageId}"]`
        );
        if (targetElement) {
          // 获取容器和目标元素的位置信息
          const containerRect = container.getBoundingClientRect(); // 文本矩形相对于视口的信息
          const elementRect = targetElement.getBoundingClientRect(); // 目标元素相对于视口的矩形信息
          console.log(containerRect.top, elementRect.top);
          // 计算需要滚动的距离(考虑反向滚动)
          const scrollOffset = elementRect.top - containerRect.top;
          const containerCenter = containerRect.height / 2;
          console.log(containerRect.height);
          console.log(container.scrollTop);
          const targetScrollTop =
            container.scrollTop + scrollOffset - containerCenter;
          // 执行平滑滚动
          container.scrollTo({
            top: targetScrollTop,
            behavior: "smooth",
          });
          // 高亮逻辑
          targetElement.classList.add("highlight-message");
          setTimeout(
            () => targetElement.classList.remove("highlight-message"),
            2000
          );
        } else {
          console.error(`Message with ID ${messageId} not found.`);
        }
      }
    </script>
  </body>
</html>

# 图解

[目标消息]                    target


+-----------------------------+
| 视口顶部                     |
|                             |
| [旧消息] <-- 容器顶部   container    |
| [消息2]                     |
| [消息3]                     |
| ...                         |
| [目标消息]                target |
| ...                         |
| [新消息] <-- 容器底部        |
|                             |
| 视口底部                     |
+-----------------------------+

这里的 targetcontainer 是容器和消息相对于视口顶部的距离,也就是 container.getBoundingClientRect();targetElement.getBoundingClientRect();top 值。
用目标的值减去容器的值,就是需要滚动的距离。
container.scrollTop + target - container 就可以得到把目标元素滚进容器内的滚动条的位置。
但是我们需要放在中间,所以,需要减去容器的中间位置。也就是减去 containerRect.height / 2;
当然,目标消息在视口上方时, getBoundingClientRecttop 值是负数,操作也是一样的。
由此可得,这个解具有普遍性

更新于 阅读次数