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

PHP+AJAX实现MySQL实时表格展示:解决JS计时器不重置及语法错误

解决PHP+AJAX实时展示MySQL数据并维持独立计时器的问题

你的需求回顾

你想要实现的是:

  • 在PHP页面的表格里展示MySQL查询结果,第一列是数据库数据,第二列是独立的JavaScript计时器
  • 数据库新增数据时自动在表格中显示,数据超过60秒未更新(不再出现在查询结果里)时自动移除
  • 每次查询数据库时,已存在的行计时器不重置

你当前遇到的问题

前端AJAX代码里直接在JavaScript函数中插入HTML和PHP代码,导致语法错误(unexpected token <),而且逻辑上也有问题——前端无法解析PHP代码,必须通过AJAX拿到JSON数据后用JavaScript动态生成DOM元素。


一、先修正PHP后端代码

你的PHP代码有几个小问题,先调整一下:

<?php
header('Content-type: application/json');
$servername = "localhost";
$username = "user";
$password = "password";
$dbname = "test";

// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查连接
if ($conn->connect_error) {
    die(json_encode(['error' => "Connection failed: " . $conn->connect_error]));
}

$sql = "SELECT * FROM (
    SELECT beacon, location, COUNT(location) AS counter 
    FROM `track` 
    WHERE `date` = CURDATE() AND `time` > NOW() - INTERVAL 60 SECOND 
    GROUP BY beacon
) AS SubQueryTable 
ORDER BY beacon + 0 ASC;";

// 只执行一次查询即可,不要重复赋值
$result = mysqli_query($conn, $sql);
$rows = array();
while($r = mysqli_fetch_assoc($result)) {
    $rows[] = $r;
}
echo json_encode($rows);
$conn->close();
?>

这里主要修正了:

  • 移除了重复的查询语句
  • 把连接错误也用JSON格式返回,方便前端处理

二、重构前端HTML/JavaScript代码

核心思路是:

  1. 定时通过AJAX获取最新的JSON数据
  2. 对比当前表格中的数据和新数据,新增的行添加到表格,消失的行从表格移除
  3. 为每个行维护独立的计时器,存储每个beacon对应的计时状态,避免全局计时器导致所有行同步更新

完整的前端代码:

<!DOCTYPE html>
<html>
<head>
    <title>实时数据追踪</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <style>
        table { border-collapse: collapse; width: 500px; margin: 20px auto; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: center; }
        th { background-color: #f2f2f2; }
    </style>
</head>
<body>
    <table id="dataTable">
        <thead>
            <tr>
                <th>Beacon</th>
                <th>计时器</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>

    <script>
        // 存储每个beacon的计时器状态:key是beacon,value是{ timerId: 计时器ID, seconds: 当前秒数 }
        const timerMap = {};
        // 存储当前表格中存在的beacon集合,用于对比更新
        let currentBeacons = new Set();

        // 格式化数字为两位(补0)
        function pad(val) {
            return val > 9 ? val : `0${val}`;
        }

        // 更新单个行的计时器
        function updateTimer(beacon) {
            const timerData = timerMap[beacon];
            if (!timerData) return;
            
            timerData.seconds++;
            const minutes = pad(Math.floor(timerData.seconds / 60));
            const seconds = pad(timerData.seconds % 60);
            
            $(`#row-${beacon} .minutes`).text(minutes);
            $(`#row-${beacon} .seconds`).text(seconds);
        }

        // 渲染表格数据
        function renderTable(newData) {
            const newBeacons = new Set(newData.map(item => item.beacon));
            
            // 1. 移除不再存在的行
            currentBeacons.forEach(beacon => {
                if (!newBeacons.has(beacon)) {
                    // 清除对应的计时器
                    clearInterval(timerMap[beacon].timerId);
                    delete timerMap[beacon];
                    // 删除表格行
                    $(`#row-${beacon}`).remove();
                }
            });
            
            // 2. 添加新增的行
            newData.forEach(item => {
                if (!currentBeacons.has(item.beacon)) {
                    // 创建表格行
                    const row = `
                        <tr id="row-${item.beacon}" style="background-color: ${item.location}">
                            <td>${item.beacon}</td>
                            <td>
                                <span class='minutes'>00</span>:<span class='seconds'>00</span>
                            </td>
                        </tr>
                    `;
                    $('#dataTable tbody').append(row);
                    
                    // 初始化计时器
                    const seconds = 0;
                    const timerId = setInterval(() => updateTimer(item.beacon), 1000);
                    timerMap[item.beacon] = { timerId, seconds };
                }
            });
            
            // 更新当前存在的beacon集合
            currentBeacons = newBeacons;
        }

        // 定时获取数据并更新表格
        function fetchAndUpdate() {
            $.get('vendor/fetch.php', function(response) {
                // 处理可能的错误
                if (response.error) {
                    console.error('获取数据失败:', response.error);
                    return;
                }
                renderTable(response);
            }).fail(function() {
                console.error('AJAX请求失败');
            });
        }

        // 初始化:第一次加载数据,然后每1秒刷新一次(不要太频繁,避免给数据库太大压力)
        fetchAndUpdate();
        setInterval(fetchAndUpdate, 1000);
    </script>
</body>
</html>

关键逻辑说明

  1. 计时器独立维护:用timerMap对象存储每个beacon的计时器ID和当前秒数,确保每个行的计时器独立运行,不会因为查询刷新而重置。
  2. 数据对比更新:通过currentBeacons集合对比新旧数据,只增删变化的行,而不是每次都清空表格重新渲染,提升性能。
  3. 错误处理:添加了AJAX请求失败和后端返回错误的处理逻辑,方便调试。

内容的提问来源于stack exchange,提问作者Avalon Parkes-barton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:59:43