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

基于Handlebars与Ajax实现带偏移量分页的数据库内容查询需求

我帮你梳理一套完全符合需求的实现方案——用Handlebars配合Ajax做带偏移量的分页,只在需要的时候请求对应页的数据,而不是一次性拉全量:

核心逻辑梳理

核心就是按需请求:

  • 表单提交时,只请求第一页数据(偏移量offset=0,配合每页条数limit)
  • 点击页码、上/下一页时,根据当前页码计算新的偏移量,再向后端请求对应页的数据
  • 拿到数据后用Handlebars重新渲染列表和分页控件
前端实现步骤

1. 准备Handlebars模板

先在HTML里定义两个模板:一个渲染数据列表,一个渲染分页控件:

<!-- 数据列表模板 -->
<script id="data-list-tpl" type="text/x-handlebars-template">
  <div class="data-list">
    {{#each items}}
      <div class="item">
        <h3>{{title}}</h3>
        <p>{{content}}</p>
        <!-- 替换成你的实际数据字段 -->
      </div>
    {{else}}
      <p>暂无匹配数据</p>
    {{/each}}
  </div>
</script>

<!-- 分页控件模板 -->
<script id="pagination-tpl" type="text/x-handlebars-template">
  <div class="pagination-bar">
    <button class="page-btn prev" {{#if isFirstPage}}disabled{{/if}}>上一页</button>
    
    {{#each pageList}}
      <button class="page-btn number {{#if isActive}}active{{/if}}" data-page="{{pageNum}}">
        {{pageNum}}
      </button>
    {{/each}}
    
    <button class="page-btn next" {{#if isLastPage}}disabled{{/if}}>下一页</button>
  </div>
</script>

2. 初始化全局状态变量

用来保存当前页码、每页条数、搜索参数和总数据量:

let currentPage = 1;
const pageSize = 10; // 可根据业务需求调整
let searchParams = {}; // 存储表单提交的搜索条件
let totalItems = 0; // 总数据条数,由后端接口返回

3. 表单提交处理

拦截表单默认提交行为,发起第一页数据请求:

document.getElementById('search-form').addEventListener('submit', function(e) {
  e.preventDefault();
  
  // 收集表单参数
  const formData = new FormData(this);
  searchParams = Object.fromEntries(formData);
  
  // 重置页码到第一页,发起数据请求
  currentPage = 1;
  fetchPageData(currentPage);
});

4. 核心分页请求函数

负责向后端发起带偏移量的请求,并用Handlebars渲染数据和分页:

function fetchPageData(page) {
  // 计算偏移量:(当前页-1)*每页条数
  const offset = (page - 1) * pageSize;
  
  // 构造请求参数,包含搜索条件、offset和limit
  const requestUrl = new URL('/api/get-target-data', window.location.origin);
  Object.entries({...searchParams, offset, limit: pageSize}).forEach(([key, val]) => {
    requestUrl.searchParams.append(key, val);
  });
  
  // 发起Ajax请求(这里用原生fetch,也可以用jQuery.ajax)
  fetch(requestUrl)
    .then(res => {
      if (!res.ok) throw new Error('数据请求失败');
      return res.json();
    })
    .then(data => {
      totalItems = data.totalItems;
      
      // 渲染数据列表
      const listTemplate = Handlebars.compile(document.getElementById('data-list-tpl').innerHTML);
      document.getElementById('data-container').innerHTML = listTemplate({ items: data.items });
      
      // 渲染分页控件
      renderPagination(page);
    })
    .catch(err => {
      console.error('加载数据出错:', err);
      document.getElementById('data-container').innerHTML = '<p>加载失败,请稍后重试</p>';
    });
}

5. 渲染分页控件

根据当前页和总页数,动态生成分页按钮,并绑定点击事件:

function renderPagination(currentPage) {
  const totalPages = Math.ceil(totalItems / pageSize);
  
  // 生成页码列表(优化:只显示当前页前后2页,避免页码过多)
  const pageList = [];
  const startPage = Math.max(1, currentPage - 2);
  const endPage = Math.min(totalPages, currentPage + 2);
  
  for (let i = startPage; i <= endPage; i++) {
    pageList.push({
      pageNum: i,
      isActive: i === currentPage
    });
  }
  
  // 渲染分页按钮
  const paginationTemplate = Handlebars.compile(document.getElementById('pagination-tpl').innerHTML);
  document.getElementById('pagination-container').innerHTML = paginationTemplate({
    isFirstPage: currentPage === 1,
    isLastPage: currentPage === totalPages,
    pageList: pageList
  });
  
  // 绑定分页按钮点击事件
  bindPaginationEvents();
}

function bindPaginationEvents() {
  // 上一页按钮
  document.querySelector('.page-btn.prev')?.addEventListener('click', () => {
    if (currentPage > 1) {
      currentPage--;
      fetchPageData(currentPage);
    }
  });
  
  // 下一页按钮
  document.querySelector('.page-btn.next')?.addEventListener('click', () => {
    const totalPages = Math.ceil(totalItems / pageSize);
    if (currentPage < totalPages) {
      currentPage++;
      fetchPageData(currentPage);
    }
  });
  
  // 页码按钮
  document.querySelectorAll('.page-btn.number').forEach(btn => {
    btn.addEventListener('click', () => {
      const page = parseInt(btn.dataset.page);
      currentPage = page;
      fetchPageData(currentPage);
    });
  });
}
后端处理要点

后端需要接收三个关键参数:offset(偏移量)、limit(每页条数)、以及你的表单搜索条件,然后完成两个操作:

  1. 查询当前页的数据:以MySQL为例,用SELECT * FROM your_table WHERE [搜索条件] LIMIT limit OFFSET offset;
  2. 查询总数据条数:用SELECT COUNT(*) as totalItems FROM your_table WHERE [搜索条件];

最后返回JSON格式的响应,示例:

{
  "items": [
    {"id": 1, "title": "第一条数据", "content": "示例内容..."},
    {"id": 2, "title": "第二条数据", "content": "示例内容..."}
  ],
  "totalItems": 56
}
额外优化建议
  • 添加加载状态:请求数据时显示loading动画,避免用户重复点击
  • 分页按钮样式:给当前页按钮添加高亮样式,提升用户体验
  • 参数校验:后端要校验offset和limit的合法性,避免非法请求或SQL注入
  • 空状态处理:当没有数据时,在列表模板里给用户明确提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:29:13