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

纯TypeScript硬编码表格:无插件实现分页与搜索功能逻辑咨询

嘿,这事儿不难!纯原生TypeScript就能搞定表格的搜索和分页,完全不用任何插件。我给你拆解下实现逻辑,再附上完整的代码示例,你照着改就行~

整体核心思路

我们需要维护三组数据/状态:

  • 原始硬编码数据:作为数据源,永远不动,保证搜索和分页的基础数据完整
  • 过滤后的数据:用户搜索后,从原始数据中筛选出的匹配结果
  • 当前分页数据:根据当前页码和每页条数,从过滤后的数据中截取的要显示的条目

每次搜索或者切换页码时,我们只需要更新对应的状态,然后重新渲染表格和分页控件即可。

搜索功能实现逻辑
  1. 添加搜索输入框:在HTML里加一个文本输入框,用于接收用户的搜索关键词
  2. 监听输入事件:给输入框绑定input事件,实时响应用户的输入
  3. 过滤数据:把用户输入的关键词转成小写(忽略大小写),遍历原始数据,匹配你指定的列(比如姓名、邮箱等),得到过滤后的数据集
  4. 重置分页状态:搜索完成后,把当前页码重置为1,避免用户在第3页搜索后还停留在第3页(可能没有数据)
  5. 重新渲染:根据过滤后的数据重新计算分页并渲染表格
分页功能实现逻辑
  1. 定义分页参数:设置每页显示的条数(比如pageSize = 5),维护当前页码(currentPage = 1)
  2. 计算总页数:用过滤后的数据总条数除以每页条数,向上取整得到总页数(Math.ceil(filteredData.length / pageSize))
  3. 渲染分页控件:生成上一页按钮、页码按钮组、下一页按钮,给按钮绑定点击事件
  4. 处理分页切换:点击页码或上下页时,更新currentPage,然后从过滤后的数据中截取对应区间的条目(slice((currentPage-1)*pageSize, currentPage*pageSize))
  5. 边界处理:当前页是第一页时禁用上一页按钮,当前页是最后一页时禁用下一页按钮;如果没有数据或者只有一页,隐藏分页控件
完整代码示例

HTML部分

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>原生TS表格分页搜索</title>
  <style>
    .table-controls {
      margin: 1rem 0;
    }
    #searchInput {
      padding: 0.5rem;
      width: 300px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    table {
      border-collapse: collapse;
      width: 100%;
      margin-top: 1rem;
    }
    th, td {
      border: 1px solid #ddd;
      padding: 0.8rem;
      text-align: left;
    }
    th {
      background-color: #f5f5f5;
      font-weight: 600;
    }
    .pagination {
      margin-top: 1rem;
      display: flex;
      gap: 0.3rem;
      align-items: center;
    }
    .pagination button {
      padding: 0.5rem 1rem;
      border: 1px solid #ddd;
      border-radius: 4px;
      cursor: pointer;
      background-color: white;
    }
    .pagination button:disabled {
      cursor: not-allowed;
      opacity: 0.6;
    }
    .page-btn.active {
      background-color: #007bff;
      color: white;
      border-color: #007bff;
    }
  </style>
</head>
<body>
  <div class="table-controls">
    <input type="text" id="searchInput" placeholder="搜索姓名或邮箱..." />
  </div>
  <table id="dataTable">
    <thead>
      <tr>
        <th>ID</th>
        <th>姓名</th>
        <th>邮箱</th>
      </tr>
    </thead>
    <tbody id="tableBody"></tbody>
  </table>
  <div id="paginationControls" class="pagination"></div>

  <script src="./table.ts"></script>
</body>
</html>

TypeScript部分(table.ts)

// 定义表格数据类型
interface TableRow {
  id: number;
  name: string;
  email: string;
}

// 硬编码的原始数据
const originalRows: TableRow[] = [
  { id: 1, name: "张三", email: "zhangsan@example.com" },
  { id: 2, name: "李四", email: "lisi@example.com" },
  { id: 3, name: "王五", email: "wangwu@example.com" },
  { id: 4, name: "赵六", email: "zhaoliu@example.com" },
  { id: 5, name: "孙七", email: "sunqi@example.com" },
  { id: 6, name: "周八", email: "zhouba@example.com" },
  { id: 7, name: "吴九", email: "wujiu@example.com" },
  { id: 8, name: "郑十", email: "zhengshi@example.com" },
  { id: 9, name: "冯十一", email: "fengshiyi@example.com" },
  { id: 10, name: "陈十二", email: "chenshier@example.com" },
];

// 状态变量
let filteredRows: TableRow[] = [...originalRows];
let currentPage = 1;
const pageSize = 3; // 每页显示3条

// 渲染表格内容
function renderTable(data: TableRow[]) {
  const tableBody = document.getElementById("tableBody");
  if (!tableBody) return;

  tableBody.innerHTML = "";
  data.forEach(row => {
    const tr = document.createElement("tr");
    tr.innerHTML = `
      <td>${row.id}</td>
      <td>${row.name}</td>
      <td>${row.email}</td>
    `;
    tableBody.appendChild(tr);
  });
}

// 设置搜索功能
function setupSearch() {
  const searchInput = document.getElementById("searchInput") as HTMLInputElement;
  if (!searchInput) return;

  searchInput.addEventListener("input", (e) => {
    const keyword = (e.target as HTMLInputElement).value.toLowerCase().trim();
    // 过滤匹配姓名或邮箱
    filteredRows = originalRows.filter(row => 
      row.name.toLowerCase().includes(keyword) || 
      row.email.toLowerCase().includes(keyword)
    );
    currentPage = 1; // 搜索后回到第一页
    renderPagination();
    renderCurrentPageData();
  });
}

// 获取当前页要显示的数据
function getCurrentPageData(): TableRow[] {
  const startIdx = (currentPage - 1) * pageSize;
  const endIdx = startIdx + pageSize;
  return filteredRows.slice(startIdx, endIdx);
}

// 渲染当前页数据
function renderCurrentPageData() {
  const pageData = getCurrentPageData();
  renderTable(pageData);
}

// 渲染分页控件
function renderPagination() {
  const paginationContainer = document.getElementById("paginationControls");
  if (!paginationContainer) return;

  const totalPages = Math.ceil(filteredRows.length / pageSize);

  // 没有数据或只有一页时隐藏分页
  if (totalPages <= 1) {
    paginationContainer.innerHTML = "";
    return;
  }

  let paginationHTML = `
    <button id="prevBtn" ${currentPage === 1 ? "disabled" : ""}>上一页</button>
  `;

  // 渲染页码按钮(可以优化成只显示当前页前后的页码,避免太多按钮)
  for (let i = 1; i <= totalPages; i++) {
    paginationHTML += `
      <button class="page-btn ${i === currentPage ? "active" : ""}" data-page="${i}">${i}</button>
    `;
  }

  paginationHTML += `
    <button id="nextBtn" ${currentPage === totalPages ? "disabled" : ""}>下一页</button>
  `;

  paginationContainer.innerHTML = paginationHTML;

  // 绑定上一页点击事件
  document.getElementById("prevBtn")?.addEventListener("click", () => {
    if (currentPage > 1) {
      currentPage--;
      renderPagination();
      renderCurrentPageData();
    }
  });

  // 绑定下一页点击事件
  document.getElementById("nextBtn")?.addEventListener("click", () => {
    if (currentPage < totalPages) {
      currentPage++;
      renderPagination();
      renderCurrentPageData();
    }
  });

  // 绑定页码按钮点击事件
  document.querySelectorAll(".page-btn").forEach(btn => {
    btn.addEventListener("click", (e) => {
      const pageNum = parseInt((e.target as HTMLElement).dataset.page || "1");
      currentPage = pageNum;
      renderPagination();
      renderCurrentPageData();
    });
  });
}

// 初始化页面
function init() {
  setupSearch();
  renderPagination();
  renderCurrentPageData();
}

// 页面加载完成后执行初始化
window.addEventListener("DOMContentLoaded", init);
额外优化建议
  • 如果你表格列很多,可以把搜索匹配的列做成可配置的数组,比如searchableColumns = ["name", "email"],然后遍历这个数组进行匹配,更灵活
  • 当页码很多时(比如几十页),不要全部渲染页码按钮,可以只显示当前页的前2页、后2页,加上首尾页按钮,避免分页控件太长
  • 可以给搜索加个防抖,避免用户每输入一个字符就触发一次过滤,提升性能(比如设置300ms延迟)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:06:10