JavaScript实现表格分页并添加行操作链接

上一篇文章介绍了如何使用JavaScript代码在客户端实现列表的分页浏览功能,本文将提高一些难度,会实现表格的分页,并可以在每一行添加数据的操作链接。

首先来看数据表格的未分页状态,如下图所示。

表格未分页状态

在实现表格分页浏览及添加行数据操作链接之前,约定如下:

  • 表格元素(table)的id属性值应在页面中唯一。
  • 表格的第一行为列的名称,不属于数据行,并且总是会显示。
  • 如果需要添加行记录的操作链接,则数据的每一行,即tr元素的rowdata属性中应包含记录的主数据。请注意,rowdata为自定义属性。

下面是完整的页面代码。

HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <style>
        #result {
            min-height:300px;
        }
        .table_data {
            width:100%;
        }
        .table_data tr:nth-child(odd) {
            background-color: lightblue;
        }
        .table_data tr:nth-child(even) {
            background-color: lightyellow;
        }
        .table_data tr th{
            background-color:#ccc;
        }
        .table_data tr td {
            text-align: center;
        }
        .table_data tr td a{
            font-size:0.9em;
            margin-left:0.5em;
        }
        .tablePageNavi {
            text-align: center;
            margin-top: 2em;
        }
        .tablePageNavi a {
            font-size: 0.9em;
            margin-left: 0.5em;
            margin-right: 0.5em;
        }
    </style>
</head>
<body>
<div id="result">
    <table id="table1" class="table_data">
        <tr><th>ID</th><th>用户名</th><th>姓名</th><th>性别</th><th>状态</th></tr>
        <tr rowdata="1"><td>1</td><td>user01</td><td>测试一</td><td>1</td><td>0</td></tr>
        <tr rowdata="2"><td>2</td><td>user02</td><td>测试二</td><td>1</td><td>0</td></tr>
        <tr rowdata="3"><td>3</td><td>user03</td><td>测试三</td><td>1</td><td>1</td></tr>
        <tr rowdata="4"><td>4</td><td>user04</td><td>测试四</td><td>1</td><td>0</td></tr>
        <tr rowdata="5"><td>5</td><td>user05</td><td>测试五</td><td>2</td><td>0</td></tr>
        <tr rowdata="6"><td>6</td><td>user06</td><td>测试六</td><td>2</td><td>1</td></tr>
        <tr rowdata="7"><td>7</td><td>user07</td><td>测试七</td><td>2</td><td>0</td></tr>
        <tr rowdata="8"><td>8</td><td>user08</td><td>测试八</td><td>2</td><td>0</td></tr>
        <tr rowdata="9"><td>9</td><td>user09</td><td>测试九</td><td>0</td><td>1</td></tr>
        <tr rowdata="10"><td>10</td><td>user10</td><td>测试十</td><td>0</td><td>1</td></tr>
    </table>
</div>
</body>
</html>
<script>
    // 初始化
    function tablePageInit(id, rowPerPage) {
        let tbl = document.getElementById(id);
        if (tbl === undefined) return false;
        let rows = tableRows(id);
        if (rows === null) return false;
        rowPerPage = parseInt(rowPerPage);
        if (isNaN(rowPerPage) || rowPerPage < 1) return false;
        // 计算总页数,不包含第一行的列名
        let dataRowCount = rows.length - 1;
        let pageCount = Math.floor(dataRowCount / rowPerPage);
        if (dataRowCount % rowPerPage > 0) pageCount++;
        // 显示导航和数据
        let s = [];
        s.push("<div id='tablePageNavi' class='tablePageNavi'>");
        // 首页
        s.push(`<a href="javascript:tableShowPage('${id}',1);">首页</a>`);
        // 上一页
        s.push(`<a href="javascript:tablePreviousPage('${id}');">上一页</a>`);
        // 页选择列表,当前页/总页数
        s.push(`<select id="${id}_curpage" onchange="tableShowPage('${id}',this.value);">`);
        for (let i = 1; i <= pageCount; i++) {
            s.push(`<option value="${i}">${i}</option>`);
        }
        s.push(`</select>/<span id="${id}_pagecount">${pageCount}</span>`)
        // 下一页
        s.push(`<a href="javascript:tableNextPage('${id}');">下一页</a>`);
        // 末页
        s.push(`<a href="javascript:tableLastPage('${id}');">末页</a>`);
        // 隐藏数据,每页多少行
        s.push(`<input id="${id}_rpp" type="hidden" value="${rowPerPage}">`);
        //
        s.push("</div>");
        tbl.parentElement.innerHTML += s.join("");
        // 显示第1页
        tableShowPage(id, 1);
        //
        return true;
    }

    // 添加行记录操作链接,约定每行tr元素的rowdata属性为记录主数据
    // template中的{#}替换为记录主数据
    function tableAddRowOpt(id, template) {
        let rows = tableRows(id);
        rows[0].innerHTML += "<th>操作</th>";
        let rowData = "";
        let s = "";
        for (let i = 1; i < rows.length; i++) {
            rowData = rows[i].getAttribute("rowdata");
            s = template.replace(/{#}/g, rowData);
            rows[i].innerHTML += `<td>${s}</td>`;
        }
    }

    // 返回表元素的所有行,约定第1行为列名
    function tableRows(id) {
        let tbl = document.getElementById(id);
        let rows = tbl.getElementsByTagName("tr");
        if (rows.length > 0) return rows;
        else return null;
    }

    // 读取当前页
    function tableCurPage(id) {
        let sel = document.getElementById(id + "_curpage");
        return parseInt(sel.value);
    }

    // 读取总页数
    function tablePageCount(id) {
        let span = document.getElementById(id + "_pagecount");
        return parseInt(span.innerText);
    }

    // 读取每页多少行
    function tableRowPerPage(id) {
        let inp = document.getElementById(id + "_rpp");
        return parseInt(inp.value);
    }

    // 显示指定的页
    function tableShowPage(id, page) {
        let rowPerPage = tableRowPerPage(id);
        let rows = tableRows(id);
        // 计算显示页行索引,0为标题行
        let min = (page - 1) * rowPerPage + 1;
        // 当前页不包含max索引行
        let max = min + rowPerPage; 
        if (max > rows.length) max = rows.length;
        // 
        for (let i = 1; i < min; i++)
            rows[i].style.display = "none";
        for (let i = min; i < max; i++)
            rows[i].style.display = "table-row";
        for (let i = max; i < rows.length; i++)
            rows[i].style.display = "none";
        // 同步当前页列表
        let sel = document.getElementById(id + "_curpage");
        sel.value = page;
    }

    // 上一页
    function tablePreviousPage(id) {
        let curPage = tableCurPage(id);
        let nextPage = curPage - 1;
        if (nextPage < 1) nextPage = 1;
        tableShowPage(id, nextPage);
    }

    // 下一页
    function tableNextPage(id) {
        let curPage = tableCurPage(id);
        let pageCount = tablePageCount(id);
        let nextPage = curPage + 1;
        if (nextPage > pageCount) nextPage = pageCount;
        tableShowPage(id, nextPage);
    }

    // 末页
    function tableLastPage(id) {
        tableShowPage(id, tablePageCount(id));
    }

    // 测试代码
    tablePageInit("table1", 3);
    tableAddRowOpt("table1",
        `<a href="Edit.aspx?id={#}">编辑</a><a href="Per.aspx?id={#}">权限</a>`);
</script>

其中,style元素中定义了一些必要的CSS样式。id属性为result的div元素中定义了一个table元素,其id为table1,包含一行列名和10行数据。script元素中定义了表格分页和添加行记录操作链接的函数,最后是测试调用。页面显示效果如下图所示。

分页并添加行记录操作链接

下面关注script元素代码,JavaScript代码中包括了表格分页和添加行记录操作链接的一系列函数,下面分别讨论。

tablePageInit(id, rowPerPage)函数,表格分页浏览初始化。函数中,首先检查表格元素、数据行和每页行数的正确性,如果检查不通过,则函数返回false。然后,计算了总页数(pageCount)。接下来是导航栏的定义,包括显示首页,调用tableShowPage(id,1);显示上一页,调用tablePreviousPage(id);显示下一页,调用tableNextPage(id);显示末页,调用tableLastPage(id)。在“上一页”和“下一页”链接之间还显示了当前页的选择列表(<id>_curpage)和显示总页数的span元素(<id>_pagecount),其中,在列表中选择当前页时会调用tableShowPage(id,page)函数显示此页数据。在“末页”链接之后,使用隐藏的input元素(<id>_rpp)显示了每页显示多少行的数据。最后,将导航栏代码添加到表格下面,并显示第一页的数据。初始化完成后,函数会返回true。

tableAddRowOpt(id, template)函数,用于添加每行记录的操作链接。参数id指定table元素的id,参数template指定行记录操作的模板代码,其中的{#}会替换为行数据,即tr元素的rowdata属性值。函数中,在第一行的最后添加th元素,显示内容为“操作”;之后,在所有数据行最后添加td元素,并将替换行数据之后的template内容添加到单元格,如模板为“<a href='Edit.aspx?id={#}'>编辑</a>”,行数据为1,则单元格中显示的内容就是“<a href='Edit.aspx?id=1'>编辑</a>”。

tableRows(id)函数,返回指定id的表格中的所有tr元素集合,即所有行的集合。请注意,如果tr元素数量为0,则函数返回null。

tableCurPage(id)函数,从id属性为“<id>_curpage”的select元素读取当前页数据,返回整数值。

tablePageCount(id)函数,从id属性为“<id>_pagecount”的span元素中读取总页数,返回整数值。

tableRowPerPage(id)函数,从id属性为“<id>_rpp”的隐藏input元素中读取每页显示多少行,返回整数值。

tableShowPage(id, page)函数,显示指定页的数据行。函数中会通过指定的页码(page)、每页显示多少行和总行数计算当前显示的数据行;请注意,数据行是从第2行(索引1)开始的;表的第一行为列名,总是会显示。显示指定的页后,还会指定当前页列表同步当前页数据。

tablePreviousPage(id)函数,显示当前页的前一页,如果上一页小于1则显示第一页。

tableNextPage(id)函数,显示当前面的下一页,如果下一页大于总页数则显示最后一页。

tableLastPage(id)函数,显示最后一页。

代码的最后为测试代码,tablePageInit()函数中指定表格id为"table1",每页显示3行数据。使用tableAddRowOpt()函数添加行记录操作链接的模板为"<a href="Edit.aspx?id={#}">编辑</a><a href="Per.aspx?id={#}">权限</a>",如果行数据为1,则行的最后添加的td元素中的内容就是"<a href="Edit.aspx?id=1">编辑</a><a href="Per.aspx?id=1">权限</a>"。