现在很多组件库都包含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节点