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

如何通过XMLHttpRequest动态更新Bootstrap进度条?

实现进度条同步更新的三种方案

方案一:前端本地维护统计值(适合单人场景)

页面加载时把初始统计数据存在DOM的data-*属性里,勾选/取消复选框并成功更新后端状态后,直接修改前端统计值并重新计算进度:

  1. 进度条DOM结构(嵌入初始统计数据):
<div class="progress" data-total="10" data-completed="3">
  <div class="progress-bar" role="progressbar" style="width: 30%" aria-valuenow="3" aria-valuemin="0" aria-valuemax="10">3/10</div>
</div>
  1. 复选框事件与XHR回调逻辑:
// 绑定所有任务复选框的变更事件
document.querySelectorAll('.task-checkbox').forEach(checkbox => {
  checkbox.addEventListener('change', function() {
    const taskId = this.dataset.taskId;
    const isChecked = this.checked;
    
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/update-task-status.php', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        // 从DOM获取当前统计值
        const progressContainer = document.querySelector('.progress');
        let total = parseInt(progressContainer.dataset.total);
        let completed = parseInt(progressContainer.dataset.completed);
        
        // 根据勾选状态更新已完成数
        completed += isChecked ? 1 : -1;
        
        // 更新DOM属性与进度条
        progressContainer.dataset.completed = completed;
        const percentage = Math.round((completed / total) * 100);
        const progressBar = progressContainer.querySelector('.progress-bar');
        
        progressBar.style.width = `${percentage}%`;
        progressBar.textContent = `${completed}/${total}`;
        progressBar.setAttribute('aria-valuenow', completed);
      }
    };
    
    xhr.send(`task_id=${taskId}&status=${isChecked ? 'completed' : 'pending'}`);
  });
});

注意:这种方式无需额外请求,但如果有多个用户同时操作,前端统计值会和后端不一致,仅适合单人使用的场景。

方案二:更新状态后请求统计接口(多用户场景保证一致性)

如果是多用户协作的系统,前端本地维护数据容易出错,建议在更新任务状态成功后,单独请求后端的统计接口获取最新数据:

  1. 后端统计接口(get-task-stats.php):
<?php
// 数据库连接逻辑(替换为你的实际配置)
$conn = new mysqli('localhost', '你的用户名', '你的密码', '你的数据库名');

// 查询已完成任务数与总任务数
$completedRes = $conn->query("SELECT COUNT(*) FROM tasks WHERE status = 'completed'");
$completed = (int)$completedRes->fetch_row()[0];

$totalRes = $conn->query("SELECT COUNT(*) FROM tasks");
$total = (int)$totalRes->fetch_row()[0];

$conn->close();

// 返回JSON格式的统计数据
echo json_encode(['completed' => $completed, 'total' => $total]);
?>
  1. 前端封装进度条更新函数,复用初始化与回调逻辑:
// 封装更新进度条的通用函数
function updateProgress() {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', '/get-task-stats.php', true);
  
  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      const stats = JSON.parse(xhr.responseText);
      const percentage = stats.total > 0 ? Math.round((stats.completed / stats.total) * 100) : 0;
      
      const progressBar = document.querySelector('.progress-bar');
      progressBar.style.width = `${percentage}%`;
      progressBar.textContent = `${stats.completed}/${stats.total}`;
      progressBar.setAttribute('aria-valuenow', stats.completed);
      progressBar.setAttribute('aria-valuemax', stats.total);
      
      // 更新容器的data属性,方便后续使用
      const progressContainer = document.querySelector('.progress');
      progressContainer.dataset.completed = stats.completed;
      progressContainer.dataset.total = stats.total;
    }
  };
  
  xhr.send();
}

// 页面加载时初始化进度条
window.addEventListener('load', updateProgress);

// 复选框变更事件
document.querySelectorAll('.task-checkbox').forEach(checkbox => {
  checkbox.addEventListener('change', function() {
    const taskId = this.dataset.taskId;
    const isChecked = this.checked;
    
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/update-task-status.php', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        // 状态更新成功后,调用进度条更新函数
        updateProgress();
      }
    };
    
    xhr.send(`task_id=${taskId}&status=${isChecked ? 'completed' : 'pending'}`);
  });
});

这种方式每次都从后端拉取最新数据,能确保多用户场景下进度条的准确性。

方案三:更新状态接口直接返回统计数据(优化请求次数)

可以合并请求,在更新任务状态的PHP接口中,处理完状态变更后直接返回最新的统计数据,避免额外的HTTP请求:

  1. 修改更新状态接口(update-task-status.php):
<?php
$conn = new mysqli('localhost', '你的用户名', '你的密码', '你的数据库名');

// 获取请求参数
$taskId = $_POST['task_id'];
$status = $_POST['status'];

// 更新任务状态
$conn->query("UPDATE tasks SET status = '$status' WHERE id = $taskId");

// 查询最新统计数据
$completedRes = $conn->query("SELECT COUNT(*) FROM tasks WHERE status = 'completed'");
$completed = (int)$completedRes->fetch_row()[0];

$totalRes = $conn->query("SELECT COUNT(*) FROM tasks");
$total = (int)$totalRes->fetch_row()[0];

$conn->close();

// 返回更新结果与统计数据
echo json_encode([
  'success' => true,
  'completed' => $completed,
  'total' => $total
]);
?>
  1. 前端直接用返回的统计数据更新进度条:
document.querySelectorAll('.task-checkbox').forEach(checkbox => {
  checkbox.addEventListener('change', function() {
    const taskId = this.dataset.taskId;
    const isChecked = this.checked;
    
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/update-task-status.php', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        const res = JSON.parse(xhr.responseText);
        if (res.success) {
          const percentage = res.total > 0 ? Math.round((res.completed / res.total) * 100) : 0;
          
          const progressBar = document.querySelector('.progress-bar');
          progressBar.style.width = `${percentage}%`;
          progressBar.textContent = `${res.completed}/${res.total}`;
          progressBar.setAttribute('aria-valuenow', res.completed);
          progressBar.setAttribute('aria-valuemax', res.total);
        }
      }
    };
    
    xhr.send(`task_id=${taskId}&status=${isChecked ? 'completed' : 'pending'}`);
  });
});

这种方式减少了一次HTTP请求,性能更优,同时也能保证数据一致性。

内容的提问来源于stack exchange,提问作者Lucas Macedo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 15:30:23