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

如何用纯JavaScript实现HTML表格按行/列双向排序?

纯JavaScript实现表格行/列双向排序方案

我刚好折腾过类似的需求,纯JS完全能搞定,不用依赖任何库。咱们先把核心逻辑拆解清楚,再给你上完整可运行的代码:

核心思路

  • 列排序(点击表头):点击任意列标题时,提取该列所有行的单元格值,根据值的类型(字符串/数字/日期)对表格行进行升序/降序排序,然后重新渲染表格。
  • 行排序(点击第一列单元格):点击第一列的普通单元格时,提取该行所有列的单元格值,把表格的列转换成可排序的数组,根据该行的值对列进行排序,再重构表格结构。
  • 排序状态切换:给表头和第一列单元格添加状态标记,记录当前排序方向,点击时自动切换升序/降序。

完整实现代码

<!DOCTYPE html>
<html>
<head>
    <style>
        table { border-collapse: collapse; width: 80%; margin: 20px auto; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; cursor: pointer; }
        td:first-child { cursor: pointer; background-color: #f9f9f9; }
        .sort-asc::after { content: " ↑"; }
        .sort-desc::after { content: " ↓"; }
    </style>
</head>
<body>
    <table id="sortableTable">
        <thead>
            <tr>
                <th>姓名</th>
                <th>年龄</th>
                <th>薪资</th>
                <th>入职日期</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>张三</td>
                <td>28</td>
                <td>8500</td>
                <td>2021-03-15</td>
            </tr>
            <tr>
                <td>李四</td>
                <td>35</td>
                <td>12000</td>
                <td>2019-07-22</td>
            </tr>
            <tr>
                <td>王五</td>
                <td>22</td>
                <td>6000</td>
                <td>2022-11-01</td>
            </tr>
        </tbody>
    </table>

    <script>
        const table = document.getElementById('sortableTable');
        const thead = table.querySelector('thead');
        const tbody = table.querySelector('tbody');

        // 列排序:点击表头触发
        thead.addEventListener('click', (e) => {
            if (e.target.tagName !== 'TH') return;
            
            const th = e.target;
            const columnIndex = Array.from(thead.querySelectorAll('th')).indexOf(th);
            const isAsc = !th.classList.contains('sort-asc');
            
            // 清除其他表头的排序状态
            thead.querySelectorAll('th').forEach(t => t.classList.remove('sort-asc', 'sort-desc'));
            th.classList.add(isAsc ? 'sort-asc' : 'sort-desc');

            // 获取所有行并排序
            const rows = Array.from(tbody.querySelectorAll('tr'));
            rows.sort((a, b) => {
                const valA = a.querySelectorAll('td')[columnIndex].textContent.trim();
                const valB = b.querySelectorAll('td')[columnIndex].textContent.trim();
                
                // 判断数据类型,数字/日期转成对应类型比较
                if (!isNaN(valA) && !isNaN(valB)) {
                    return isAsc ? Number(valA) - Number(valB) : Number(valB) - Number(valA);
                } else if (!isNaN(Date.parse(valA)) && !isNaN(Date.parse(valB))) {
                    return isAsc ? new Date(valA) - new Date(valB) : new Date(valB) - new Date(valA);
                } else {
                    // 字符串按字典序比较
                    return isAsc ? valA.localeCompare(valB) : valB.localeCompare(valA);
                }
            });

            // 重新插入排序后的行
            tbody.innerHTML = '';
            rows.forEach(row => tbody.appendChild(row));
        });

        // 行排序:点击第一列单元格触发
        tbody.addEventListener('click', (e) => {
            if (e.target.tagName !== 'TD' || e.target.cellIndex !== 0) return;
            
            const td = e.target;
            const rowIndex = Array.from(tbody.querySelectorAll('tr')).indexOf(td.parentElement);
            const isAsc = !td.classList.contains('sort-asc');
            
            // 清除第一列其他单元格的排序状态
            tbody.querySelectorAll('td:first-child').forEach(t => t.classList.remove('sort-asc', 'sort-desc'));
            td.classList.add(isAsc ? 'sort-asc' : 'sort-desc');

            // 把表格转换成列数组:[[列1所有值], [列2所有值], ...]
            const columns = [];
            const headerCells = Array.from(thead.querySelectorAll('th'));
            const bodyRows = Array.from(tbody.querySelectorAll('tr'));
            
            // 初始化列数组,包含表头
            headerCells.forEach((th, idx) => {
                columns.push([th.textContent.trim()]);
            });
            
            // 填充每行的单元格到对应列
            bodyRows.forEach(row => {
                Array.from(row.querySelectorAll('td')).forEach((cell, idx) => {
                    columns[idx].push(cell.textContent.trim());
                });
            });

            // 根据目标行的值排序列
            columns.sort((colA, colB) => {
                const valA = colA[rowIndex + 1]; // +1是因为第一元素是表头
                const valB = colB[rowIndex + 1];
                
                // 同样处理数据类型
                if (!isNaN(valA) && !isNaN(valB)) {
                    return isAsc ? Number(valA) - Number(valB) : Number(valB) - Number(valA);
                } else if (!isNaN(Date.parse(valA)) && !isNaN(Date.parse(valB))) {
                    return isAsc ? new Date(valA) - new Date(valB) : new Date(valB) - new Date(valA);
                } else {
                    return isAsc ? valA.localeCompare(valB) : valB.localeCompare(valA);
                }
            });

            // 重构表格:先清空,再生成新的表头和行
            thead.innerHTML = '';
            tbody.innerHTML = '';
            
            // 生成新表头
            const newHeaderRow = document.createElement('tr');
            columns.forEach(col => {
                const th = document.createElement('th');
                th.textContent = col[0];
                newHeaderRow.appendChild(th);
            });
            thead.appendChild(newHeaderRow);
            
            // 生成新行(从第1个索引开始,跳过表头)
            for (let i = 1; i < columns[0].length; i++) {
                const newRow = document.createElement('tr');
                columns.forEach(col => {
                    const td = document.createElement('td');
                    td.textContent = col[i];
                    newRow.appendChild(td);
                });
                tbody.appendChild(newRow);
            }
        });
    </script>
</body>
</html>

关键细节说明

  1. 数据类型自动识别:排序时会自动判断值是数字、日期还是字符串,用对应的规则比较,避免字符串数字排序出错(比如"12000"和"8500"的字符串排序会有问题,转成数字就正常了)。
  2. 排序状态可视化:通过sort-asc和sort-desc类给表头/第一列单元格添加箭头标记,用户能直观看到当前排序方向。
  3. 事件委托:用事件委托代替给每个单元格绑定事件,性能更好,而且动态生成的行/列也能触发排序。
  4. 双向切换:每次点击都会切换升序/降序,不用额外按钮。

你可以直接把这段代码复制到HTML文件里运行,点击表头就能按列排序,点击第一列的姓名单元格就能按该行的值对所有列排序,完全符合你的需求~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:53:24