js实现table排序

2,183 阅读2分钟

现在很多组件库都包含table,并且都带有排序功能。虽然已经接触前端很长一段时间了,但是一直都在使用这些轮子,没有实现过排序功能。昨天做笔试题的时候,碰见这么一道写table排序的题目,居然一点思路都没有。痛定思痛,于是好好研究了一下。

先是基础的html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container">
        <table>
            <thead>
                <tr><th>ID</th><th>商品条码</th><th>价格</th></tr>
            </thead>
            <tbody>
                <tr><td>1</td><td>60006</td><td>22</td></tr>
                <tr><td>2</td><td>20002</td><td>11</td></tr>
                <tr><td>3</td><td>30003</td><td>66</td></tr>
                <tr><td>4</td><td>10001</td><td>33</td></tr>
                <tr><td>5</td><td>50005</td><td>44</td></tr>
                <tr><td>6</td><td>40004</td><td>55</td></tr>
            </tbody>
        </table>
    </div>
    <script src="./index.js"></script>

</body>
</html>

看body里的container类,里面就包含了一个简单的table,有三列数据:ID、商品条码、价格。现在要求点击表头,就可以进行升序或者降序排列。用代码语言就是,给表头元素添加一个click事件,对行元素进行排序。

js实现如下:

const order = {
    init (param) {
        const that = this;
        const table = param.el;
        if (!table) return;
        // 获取tbody节点
        const tbody = table.getElementsByTagName('tbody')[0];
        // 获取所有th节点,并将其转为数组
        const ths = Array.from(table.getElementsByTagName('th'));
        // 获取所有tr节点,并将其转为数组
        const trs = Array.from(tbody.getElementsByTagName('tr'));
        const list = this.getBodyList(trs);
        ths.forEach((th, index) => {
            // 为th绑定点击事件
            th["addEventListener"]('click', () => {
                // 判断当前数据是否为升序
                const isAsc = this.isAsc(list, index);
                // 如果当前为升序,则将list降序排序;如果当前为降序,则将list升序排序;
                list.sort((a, b) => isAsc ? b.value[index]- a.value[index]: a.value[index] - b.value[index]);
                // 将排序后的list重新插入tbody中
                list.forEach((tr, index) => {
                    tbody.appendChild(tr.tr)
                });
            });
        });
    },
    getBodyList (trs) {
        console.log(trs)
        return trs.map(tr => {
            // 获取tr的所有td节点,并将其转为数组
            const tds = Array.from(tr.getElementsByTagName('td'));
            // 将td的内容转为数字
            const val = tds.map(td => parseInt(td.innerHTML));
            return {tr: tr, value: val};
        });
    },
    isAsc (list, index) {
        // 判断list的value中第index个值是否为升序排列
        let arr = list.map(tr => tr.value[index])
        let flag = true;
        for(let i=0; i<arr.length - 1; i++){
            if(arr[i] > arr[i+1]){
                flag = false;
                break;
            }
        }
        return flag;
    }
};

order.init({
    // 请获取class=container下的table的节点
    el: document.getElementsByClassName('container')[0].getElementsByTagName('table')[0]
});

需要注意的点:

  • getElementsByTagName返回的是HTMLCollection类数组类型的数据,不能直接当成数组类型使用,但可以通过Array.from()转化成数组
  • 在判断当前是否为升序排列时,需要先将需要判断的那一列数据抽取出来
  • 排序函数sort如果是a - b就是升序排列,b - a就是降序排列
  • appendChild在插入已经存在的节点时,实际操作是将节点进行移动,因此不用担心会出现两个相同的tr节点