如何通过XMLHttpRequest动态更新Bootstrap进度条?
实现进度条同步更新的三种方案
方案一:前端本地维护统计值(适合单人场景)
页面加载时把初始统计数据存在DOM的data-*属性里,勾选/取消复选框并成功更新后端状态后,直接修改前端统计值并重新计算进度:
- 进度条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>
- 复选框事件与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'}`); }); });
注意:这种方式无需额外请求,但如果有多个用户同时操作,前端统计值会和后端不一致,仅适合单人使用的场景。
方案二:更新状态后请求统计接口(多用户场景保证一致性)
如果是多用户协作的系统,前端本地维护数据容易出错,建议在更新任务状态成功后,单独请求后端的统计接口获取最新数据:
- 后端统计接口(
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]); ?>
- 前端封装进度条更新函数,复用初始化与回调逻辑:
// 封装更新进度条的通用函数 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请求:
- 修改更新状态接口(
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 ]); ?>
- 前端直接用返回的统计数据更新进度条:
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
相关产品推荐
相关产品推荐

