Lab 02. Table Sorter

                                                                

题目

在给定源代码:source-code.zip的基础上,完成Table sorter。

Table sorter包括JavaScript和一点CSS,能够让原始的html table变得可以分别按照各栏数据值,对各行排序。

效果

  1. 在表头任意一个栏目中点击一下,下面各行将按照此栏目值的升序排序

    1. 按照字符串比较来确定顺序

  2. 再次点击该栏目,变更为降序排序

  3. 点击其它栏目,则按其它栏目的值重新排序

  4. 注意,排序时,栏目奇偶行的背景色保持奇数白色、偶数浅灰色

未点击点击再击点击其它

* 虽然上面例图中只显示了To-Do表格的变化,实际上Staff表格也是sortable的。

要求

  1. 不能改动原html,只能够添加js和css文件

  2. 不能使用任何类库,只能用原生DOM API

  3. JavaScript必须模块化,JS的调用入口,必须按照下面的图示:
    17172129_pI76.png
    也就是说,JS中要完成makeAllTablesSortable(table-doms) 方法,该方法接受一组普通table DOM元素,将它们全部变成sortable

附加要求

学有余力的同学还可以尝试用这个makeAllTablesSortable,使其它网页的table变得sortable。具体做法是打开一个有table的网页,开启控制台,然后在控制台中执行你的makeAllTablesSortable方法,看看能否将tables变得sortable。

完成了附加要求的同学,请在提交的README中给出URL列表,说明可以变更的网页。

重点

  1. JavaScript基本语法

  2. 程序的模块化设计

  3. DOM 

  4. DOM Event

高级提点:Object Oriented Programming


简单的css:

th {
    border: 1px solid #98A9EC;
    background-color: #031B7F;
    color: white;
    transition:background-color 1s;
}

th:hover {
    background-color: #A3B1FC;
}

td {
    border: 1px solid #7181BC;
    padding: 2px;
}

td, th {
    text-align: left;
    height: 20px;
    padding: 2px;
}

.alternate {
    background-color: #DDDDDD;
}

table {
    border:0;
    border-spacing:0;
  }

  #todo {
      width: 470px;
  }

 #staff  {
     width: 420px;
  }

.Ascending {
    background-image: url("ascend.png");
    background-position: right;
    background-repeat: no-repeat;
    background-color: #A3B1FC;
}

.Descending {
    background-image: url("descend.png");
    background-position: right;
    background-repeat: no-repeat;
    background-color: #A3B1FC;
}


js代码:

/*
    Author: ye jiaqi
    Time: 13 March 2015
*/

// making all tables sortable
window.onload = function() {
    var tables = getAllTables();
    makeAllTablesSortable(tables);
}

// to get all tables in the web page
function getAllTables() {
    return document.getElementsByTagName("table");
}

// make the tables sortable by clicking on the table head
function makeAllTablesSortable(tables) {
    for(var i = 0; i < tables.length; i ++) {
        // get all table heads for each table
        var table_heads = tables[i].getElementsByTagName("th");
        // ther is someone who do not use the "th" tag
        if (table_heads.len == 0) {
            table_heads = tables[i].rows[0];
        }

        // give each thead an id to clarify each colum
        for(var j = 0; j < table_heads.length; j++) {
            table_heads[j].setAttribute("id", j);
        }

        // for each click on the the head, make a response
        for(var j = 0; j < table_heads.length; j++) {
            // give another function
            table_heads[j].onclick = function() {
                // this.parentNode.parentNode.parentNode means the table
                sort(this.parentNode.parentNode.parentNode, this);
            }
        }
    }
}

function sort(table, head) {
    var to_sort = [];
    head_id = head.id;
    row_len = table.rows.length;
    // get the Sequence if whether the table colum is already sorted or not
    Sequence = head.getAttribute("class");

    // get each row for sorting
    for(var i = 1; i < row_len; i++) {
        to_sort[i] = table.rows[i];
    }

    // sort it
    to_sort.sort(compare(Sequence));

    // prevent reference error
    for(var i = 0; i < row_len-1; i++) {
        to_sort[i] = to_sort[i].innerHTML;
    }

    // change the rows
    for(var i = 0; i < row_len-1; i++) {
        table.rows[i+1].innerHTML = to_sort[i];
    }

    // set other soeted colum to be none
    for (var i = 0; i < table.rows[0].cells.length; i++) {
        table.rows[0].cells[i].setAttribute("class", "")
    }

    // set the Sequnce
    if(Sequence != "Ascending")
        head.setAttribute("class", "Ascending")
    else
        head.setAttribute("class", "Descending")

}

function compare(Sequence) {
    return function(row1,row2) {
        var value1 = row1.cells[head_id].innerHTML;
        var value2 = row2.cells[head_id].innerHTML;

        // use  diffrenet sorting method for different status

        if (value1 < value2) {
            return  (Sequence == "Ascending" ? 1 : -1);
        } else if (value1 > value2) {
            return  (Sequence == "Ascending" ? -1 : 1);
        } else  {
            return 0;
        }
    }
}


github:https://github.com/ghostbody/hw2-table-sorter

转载于:https://my.oschina.net/yejq08/blog/415947

<think> 我们正在讨论antd Table组件的排序功能。用户的问题是:在使用sorter进行排序时,如何根据数据子集的元素进行排序? 根据引用[3],我们知道Table组件的onChange事件中可以获取到sorter对象,其中包含排序信息(字段、排序方式等)。但用户希望排序的依据不是直接的数据字段,而是数据子集的元素(例如,数据项中有一个数组字段,我们想根据这个数组的某个元素来排序)。 思路: 1. 通常,Table的columns中的sorter属性可以指定为一个函数,用于自定义排序逻辑。 2. 在这个自定义排序函数中,我们可以访问当前行的数据,然后根据数据中的子集元素(比如数组的某个索引位置,或者对象的某个深层次属性)来比较。 示例: 假设数据源dataSource的每一项中有一个字段`subArray`,它是一个数组,我们想根据这个数组的第一个元素进行排序。 步骤: 1. 在column定义中,设置sorter为一个函数,该函数接收两个参数a和b(代表两行数据)。 2. 在函数中,我们取出a.subArray[0]和b.subArray[0],然后比较它们。 注意:排序函数需要返回一个数字,负数表示a在b前面,正数表示a在b后面,0表示相等。 另外,antd Tablesorter还可以配置多列排序,但这里我们只考虑单列。 代码示例: ```jsx import { Table } from 'antd'; const dataSource = [ { key: '1', name: 'John', subArray: [5, 10], }, { key: '2', name: 'Doe', subArray: [3, 20], }, { key: '3', name: 'Smith', subArray: [8, 15], }, ]; const columns = [ { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'First Element', dataIndex: 'subArray', key: 'subArray', // 自定义排序函数 sorter: (a, b) => { // 取数组的第一个元素进行比较 const aVal = a.subArray[0]; const bVal = b.subArray[0]; return aVal - bVal; // 升序排序(从小到大) }, // 为了显示,我们可以自定义渲染这个列,显示数组的第一个元素 render: (subArray) => subArray[0], }, ]; function MyTable() { return <Table dataSource={dataSource} columns={columns} />; } ``` 这样,当我们点击“First Element”这一列的表头排序时,就会根据数组的第一个元素进行排序。 但是,如果用户的需求是排序依据不是直接的数据字段,而是需要从数据中计算出来的值,同样可以使用这种方式。 另外,如果排序依据是深层次的对象属性,比如`a.obj.subObj.value`,也可以在这个函数中提取。 注意:如果数据量很大,或者排序计算复杂,可能会影响性能,但一般情况下可以满足需求。 如果用户希望同时支持前端排序和后端排序,那么需要根据情况处理。上述方法是前端排序。如果数据量较大,建议使用后端排序,此时不需要定义sorter函数,而是在onChange事件中获取排序信息,然后发送到后端,由后端返回排序后的数据。 在onChange事件中,我们可以获取到sorter对象,其中包含排序字段和排序方向,然后我们可以将排序信息发送到后端。 例如,在onChange中: ```jsx const handleTableChange = (pagination, filters, sorter) => { // sorter可能是单个对象(单列排序)或数组(多列排序) // 这里我们假设只处理单列排序 if (sorter.field) { // 这里sorter.field是当前排序的字段(即columns中定义的key或dataIndex) const order = sorter.order; // 'ascend' 或 'descend' // 然后我们可以根据这个字段和排序方向请求后端 // 注意:如果排序依据是子集元素,那么我们需要告诉后端按照哪个子字段排序,这里可能需要自定义参数名 // 例如,我们按照subArray的第一个元素排序,那么我们可以定义一个参数sortField='subArray[0]' // 但具体参数需要和后端约定 requestBackend({ sortField: `${sorter.field}[0]`, // 或者自定义一个映射关系 sortOrder: order === 'ascend' ? 'asc' : 'desc' }); } }; ``` 但是,这种情况下,我们通常不在columns中定义sorter函数,而是设置sorter: true,表示该列可排序,但实际排序由后端处理。 总结: 1. 前端排序:在column的sorter属性中定义自定义排序函数,在函数中访问子集元素进行比较。 2. 后端排序:在Table的onChange事件中获取排序信息,然后发送给后端,由后端处理。 根据用户的问题,他可能是在前端排序,并且需要取子集元素,所以第一种方法更符合。 因此,我们给出第一种方法的详细示例。 注意:如果子集元素可能为空,需要在排序函数中处理边界情况。 例如: ```js sorter: (a, b) => { const aVal = a.subArray ? a.subArray[0] : 0; // 如果不存在则给默认值 const bVal = b.subArray ? b.subArray[0] : 0; return aVal - bVal; } ``` 这样,我们就解决了如何根据数据子集元素进行排序的问题。 </think> 在 Ant Design Table 中使用 sorter 对数据子集元素进行排序时,可以通过自定义排序函数实现。以下是具体方法和代码示例: ### 解决方案 1. **在列定义中自定义排序函数** 通过 `sorter` 属性指定自定义函数,访问数据子集的元素进行比较: ```typescript import { Table } from 'antd'; const columns = [ { title: '子集元素排序', dataIndex: 'nestedData', key: 'nestedData', // 自定义排序函数 sorter: (a, b) => { // 访问子集元素(示例中取 nestedData 的第一个元素) const valA = a.nestedData?.[0]?.value || 0; const valB = b.nestedData?.[0]?.value || 0; return valA - valB; // 升序排序 }, render: (nestedData) => nestedData[0]?.value, // 显示子集元素 }, ]; ``` 2. **处理复杂数据结构** 若子集元素在深层嵌套结构中,使用可选链安全访问: ```typescript sorter: (a, b) => { // 安全访问:a.data.subset[0].targetValue const valA = a?.data?.subset?.[0]?.targetValue ?? 0; const valB = b?.data?.subset?.[0]?.targetValue ?? 0; return valA - valB; } ``` 3. **支持升降序** 在 `onChange` 事件中处理排序逻辑: ```typescript const handleTableChange = ( pagination, filters, sorter: SorterResult<DataType> | SorterResult<DataType>[] ) => { if (Array.isArray(sorter)) { // 多列排序处理 } else { const { field, order } = sorter; if (field === 'nestedData' && order) { // 根据 order 执行后端排序或本地数据处理 const direction = order === 'ascend' ? 'asc' : 'desc'; fetchSortedData(direction); // 发送排序请求到后端 } } }; <Table columns={columns} onChange={handleTableChange} // 其他配置... /> ``` ### 关键点说明 1. **安全访问** 使用 `?.` 可选链操作符和 `??` 空值合并运算符避免访问未定义路径导致的错误。 2. **排序函数规范** - 返回负值:`a` 排在 `b` 前 - 返回正值:`b` 排在 `a` 前 - 返回 `0`:保持原顺序 3. **性能优化** 若数据量较大(>1000条),建议通过 `onChange` 触发后端排序,避免前端性能瓶颈。 ### 示例场景 假设数据结构为: ```typescript interface DataType { id: string; metrics: { score: number; items: { value: number }[]; }[]; } ``` 对 `metrics[0].items[0].value` 排序的列配置: ```typescript { title: '首项分值', dataIndex: 'metrics', sorter: (a, b) => { const aVal = a.metrics?.[0]?.items?.[0]?.value || 0; const bVal = b.metrics?.[0]?.items?.[0]?.value || 0; return aVal - bVal; }, render: (metrics) => metrics[0]?.items[0]?.value, } ``` [^1][^2][^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值