You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP+AJAX实现滚动到顶部加载历史消息的技术问询

实现聊天应用的反向无限滚动(滚动顶部加载历史消息)

需求说明

开发聊天应用时,为降低服务器负载,默认仅加载最后20条消息,用户打开聊天框后默认定位到消息底部。需要实现反向无限滚动:当用户滚动到消息顶部时,加载更早的20条历史消息,直到所有消息加载完毕。

当前代码情况

后端SQL查询(PHP)

当前查询仅能获取最后20条消息:

$stmt = $this->pdo->prepare(
    "SELECT * 
    FROM (
            SELECT * 
            FROM messages 
            ORDER BY id DESC 
            LIMIT 20
        ) m
    WHERE (
            (msg_to = :omsg AND msg_from = :imsg) 
            OR 
            (msg_to = :imsg AND msg_from = :omsg)
        ) 
    AND (clear_by = 0 OR clear_by != :omsg)
    ORDER BY id ASC");

前端滚动监听(jQuery)

当前仅监听滚动到顶部触发请求,但存在重复请求、未传递分页标识等问题:

$('.chat-box').scroll(function (event) {
    var st = $(this).scrollTop();

    // 滚动到顶部时前置旧消息
    if(st <= 10){
        $.ajax({
            url: 'processes/chat.php',
            type: 'POST',
            data: 'type=loadmore&to='+to,
            dataType: 'JSON',
            success: function(data){
                $(".retrive-chat").prepend(data.html);
            }
        });
    }
});

解决方案

1. 后端修改:支持分页加载历史消息

需要接收当前已加载的最小消息ID(即最早的那条消息ID),以此为条件查询更早的消息:

// 加载更多历史消息的查询语句
$stmt = $this->pdo->prepare(
    "SELECT * 
    FROM messages 
    WHERE (
            (msg_to = :omsg AND msg_from = :imsg) 
            OR 
            (msg_to = :imsg AND msg_from = :omsg)
        ) 
    AND (clear_by = 0 OR clear_by != :omsg)
    AND id < :min_id  -- 筛选比当前最早消息ID更小的历史消息
    ORDER BY id DESC 
    LIMIT 20");

// 绑定参数:新增最小消息ID参数
$stmt->bindParam(':min_id', $minId, PDO::PARAM_INT);
$stmt->bindParam(':omsg', $omsg, PDO::PARAM_INT);
$stmt->bindParam(':imsg', $imsg, PDO::PARAM_INT);
$stmt->execute();

// 将结果按ID升序排列,保证消息顺序正确
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
$messages = array_reverse($messages);

2. 前端优化:避免重复请求+维持滚动位置

  • 添加加载状态锁,防止滚动时重复触发AJAX请求
  • 记录当前已加载的最小消息ID,传递给后端
  • 加载历史消息后,计算滚动偏移量,避免用户浏览位置跳动

修改后的前端代码:

// 全局变量:加载状态锁、当前已加载的最小消息ID、是否还有更多消息
let isLoading = false;
let minMsgId = null;
let hasMore = true;

$('.chat-box').scroll(function (event) {
    const $chatBox = $(this);
    const st = $chatBox.scrollTop();

    // 仅在未加载、还有更多消息、滚动到顶部附近时触发请求
    if (st <= 10 && !isLoading && hasMore) {
        isLoading = true;
        
        // 首次加载后,从DOM中获取当前已加载的最小消息ID
        if (!minMsgId) {
            minMsgId = parseInt($(".retrive-chat .message-item:first").data('msg-id'));
        }

        $.ajax({
            url: 'processes/chat.php',
            type: 'POST',
            data: {
                type: 'loadmore',
                to: to,
                min_id: minMsgId  // 传递最小消息ID给后端
            },
            dataType: 'JSON',
            success: function(data){
                if (data.html && data.html.length > 0) {
                    // 更新当前已加载的最小消息ID
                    minMsgId = parseInt(data.new_min_id);

                    // 计算加载前后的消息高度差,维持用户滚动位置
                    const prevHeight = $(".retrive-chat").height();
                    $(".retrive-chat").prepend(data.html);
                    const newHeight = $(".retrive-chat").height();
                    $chatBox.scrollTop(newHeight - prevHeight);
                } else {
                    // 没有更多历史消息,停止触发请求
                    hasMore = false;
                }
                isLoading = false;
            },
            error: function(){
                isLoading = false;
            }
        });
    }
});

3. 额外注意事项

  • 每条消息的DOM元素需要添加data-msg-id属性,存储消息ID,方便前端获取最小ID
  • 后端返回数据时,除了消息HTML片段,还要返回本次加载的消息中的最小ID(new_min_id)
  • 当后端查询不到更多消息时,返回空HTML,前端设置hasMore = false终止请求触发

内容的提问来源于stack exchange,提问作者Super Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 12:50:19