
本文介绍如何通过 ajax + php 实现数据库评论的懒加载,初始仅显示前 3 条主评论,点击“加载更多”按钮后按批次(每批 3 条)动态加载剩余评论,并兼容嵌套回复结构。
本文介绍如何通过 ajax + php 实现数据库评论的懒加载,初始仅显示前 3 条主评论,点击“加载更多”按钮后按批次(每批 3 条)动态加载剩余评论,并兼容嵌套回复结构。
要为现有评论系统添加“加载更多(Load More)”功能,核心在于服务端支持分页查询与前端按需触发异步加载。当前代码已能渲染首屏 3 条评论,但缺少分页参数传递和增量加载逻辑。下面提供一套完整、安全、可扩展的实现方案。
✅ 后端:增强 fetch_comment.php 支持分页
首先修改 PHP 脚本,使其支持接收起始偏移量(start)并使用预处理语句防止 SQL 注入:
<?php $connect = new PDO('mysql:host=localhost;dbname=testing', 'root', '');
$connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 配置每页数量 & 默认起始位置
$per_page = 3;
$start = (int)($_GET['start'] ?? 0); // 强制转为整型,防御注入
// ✅ 使用标准 LIMIT offset, row_count 语法(兼容 MySQL 5.7+)
$query = "
SELECT * FROM comments
WHERE parent_comment_id = 0
ORDER BY comment_id DESC
LIMIT :start, :per_page
";
$statement = $connect->prepare($query);
$statement->bindValue(':start', $start, PDO::PARAM_INT);
$statement->bindValue(':per_page', $per_page, PDO::PARAM_INT);
$statement->execute();
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
$output = '';
foreach ($result as $row) {
$output .= '
<div class="panel panel-default">
<div class="panel-heading">Par <b>' . htmlspecialchars($row["comment_sender_name"]) . '</b> le <i>' . htmlspecialchars($row["date"]) . '</i>
</div>
<div class="panel-body">' . nl2br(htmlspecialchars($row["comment"])) . '</div>
<div class="panel-footer" align="right">
<button type="button" class="btn btn-default reply" style="font-size:1rem;" id="' . (int)$row[" comment_id .>Répondre</button>
</div>
</div>
';
$output .= get_reply_comment($connect, (int)$row["comment_id"]);
}
echo $output;
function get_reply_comment($connect, $parent_id = 0, $marginleft = 0) {
$query = "SELECT * FROM comments WHERE parent_comment_id = :parent_id";
$stmt = $connect->prepare($query);
$stmt->bindValue(':parent_id', $parent_id, PDO::PARAM_INT);
$stmt->execute();
$replies = $stmt->fetchAll(PDO::FETCH_ASSOC);
$output = '';
$marginleft = $parent_id === 0 ? 0 : $marginleft + 48;
foreach ($replies as $row) {
$output .= '
<div class="panel panel-default" style="margin-left:' . $marginleft . 'px">
<div class="panel-heading">Par <b>' . htmlspecialchars($row["comment_sender_name"]) . '</b> le <i>' . htmlspecialchars($row["date"]) . '</i>
</div>
<div class="panel-body">' . nl2br(htmlspecialchars($row["comment"])) . '</div>
<div class="panel-footer" align="right">
<button type="button" class="btn btn-default reply" style="font-size:1rem;" id="' . (int)$row[" comment_id .>Répondre</button>
</div>
</div>
';
$output .= get_reply_comment($connect, (int)$row["comment_id"], $marginleft);
}
return $output;
}
?>
⚠️ 注意事项:
- 使用
bindValue()替代字符串拼接,彻底杜绝 SQL 注入;- 所有输出内容经
htmlspecialchars()和nl2br()处理,防止 XSS;- 递归调用中对
$parent_id始终强转为(int),提升健壮性;- 添加
PDO::ATTR_ERRMODE提升错误可调试性。
✅ 前端:添加“加载更多”按钮与 AJAX 加载逻辑
在 HTML 的 .all-comments 区域下方插入按钮,并引入简洁 JS 控制加载行为:
<!-- 在 <div class="all-comments" id="all"> 后添加 --> <div class="text-center mt-4"> <button id="load-more-btn" class="btn btn-primary btn-lg">Charger plus de témoignages</button> <div id="loading-indicator" class="mt-3 d-none">Chargement...</div> <div id="no-more-comments" class="text-muted mt-3 d-none">Tous les témoignages sont affichés.</div> </div>
然后添加以下 JavaScript(建议置于页面底部或 DOMContentLoaded 中):
<script>
let currentPage = 1; // 当前已加载页数(第1页 = 前3条,已由初始加载完成)
const perPage = 3;
const loadBtn = document.getElementById('load-more-btn');
const displayContainer = document.getElementById('display_comment');
const loadingEl = document.getElementById('loading-indicator');
const noMoreEl = document.getElementById('no-more-comments');
// 初始加载完成后,设置 currentPage = 1(因首屏数据已由 PHP 直出)
document.addEventListener('DOMContentLoaded', () => {
// 若后端首次渲染时未包含“加载更多”,此处可忽略;否则确保按钮状态一致
});
loadBtn.addEventListener('click', function () {
loadingEl.classList.remove('d-none');
loadBtn.disabled = true;
fetch(`fetch_comment.php?start=${currentPage * perPage}`)
.then(response => {
if (!response.ok) throw new Error('Network failed');
return response.text();
})
.then(html => {
if (html.trim() === '') {
noMoreEl.classList.remove('d-none');
loadBtn.classList.add('d-none');
return;
}
displayContainer.insertAdjacentHTML('beforeend', html);
currentPage++;
})
.catch(err => {
console.error('Erreur lors du chargement :', err);
alert('Une erreur est survenue. Veuillez réessayer.');
})
.finally(() => {
loadingEl.classList.add('d-none');
loadBtn.disabled = false;
});
});
</script>
✅ 可选增强:自动检测滚动到底部(替代手动点击)
如需更现代体验,可替换为滚动监听:
window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 300) {
if (!loadBtn.disabled && !noMoreEl.classList.contains('d-none')) {
loadBtn.click();
}
}
});
✅ 总结
- “加载更多”本质是客户端分页请求 + 服务端 LIMIT 分片查询;
- 必须使用预处理语句与类型校验,拒绝裸字符串拼接;
- 前端需管理加载状态(禁用按钮、显示 Loading、提示“无更多”);
- 输出内容务必转义,兼顾安全与可读性(如换行符转
); - 此方案天然兼容嵌套回复(
get_reply_comment逻辑不变),无需额外改造。
该实现轻量、安全、符合现代 Web 最佳实践,可直接集成到你的评论模块中。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











