我偶然发现Nick Grealy对这里关于排序表(https://stackoverflow.com/a/49041392)的问题的回答,jedwards修复了在body (https://stackoverflow.com/a/53880407)中有行的人。这是一段很好的代码,它完美地满足了我的要求。
我只是很难在表的标题中实现排序符号,当表对特定列进行升序或降序排序时。我想到了这个工作还不错:
const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent;
const comparer = (idx, asc) => (a, b) => ((v1, v2) =>
v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2)
)(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx));
document.querySelectorAll('th').forEach(th => th.addEventListener('click', ((e) => { //own code: added "e" to use further down
const table = th.closest('table');
const tbody = table.querySelector('tbody');
let buttons = document.getElementsByTagName("button"); //getting all button tags
const arrayButtons = ['0', '1', '2']; //own code: for use further down, as I have 3 buttons
Array.from(tbody.querySelectorAll('tr'))
.sort(comparer(Array.from(th.parentNode.children).indexOf(th), this.asc = !this.asc))
.forEach(tr => tbody.appendChild(tr));
arrayButtons.forEach(e => buttons[e].setAttribute("data-dir", "")); //own code: reset all data-dir fields in buttons
if (this.asc) { //own code: if ascending, fill in asc in data-dir field in button, else data-dir desc gets filled in.
e.target.setAttribute("data-dir", "asc");
} else {
e.target.setAttribute("data-dir", "desc");
}
})));
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th button {
background-color: transparent;
border: none;
cursor: pointer;
font: inherit;
color:inherit;
width: 100%;
}
th button[data-dir="asc"]::after {
content: " " url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 13' xml:space='preserve' width='13' height='13' transform='scale(1 -1)'%3E%3Cpath d='M2.039 5.171c.114.114.25.172.406.172h8.107a.55.55 0 0 0 .406-.172c.114-.114.172-.25.172-.406s-.057-.292-.172-.406L6.906.302C6.792.187 6.658.13 6.5.13s-.292.057-.406.172L2.039 4.355a.557.557 0 0 0-.172.406.541.541 0 0 0 .172.408zM6.5 12.473a.163.163 0 0 1-.127-.055L2.319 8.364a.159.159 0 0 1-.055-.127c0-.042.01-.081.055-.127s.084-.055.127-.055h8.107c.042 0 .081.01.127.055s.055.084.055.127a.163.163 0 0 1-.055.127l-4.052 4.054c-.047.045-.084.055-.127.055m0 .396a.55.55 0 0 0 .406-.172l4.054-4.054a.557.557 0 0 0 .172-.406.549.549 0 0 0-.172-.406.557.557 0 0 0-.406-.172H2.447a.549.549 0 0 0-.406.172.557.557 0 0 0-.172.406.55.55 0 0 0 .172.406l4.054 4.053c.114.114.25.172.406.172z' fill='%23000'/%3E%3C/svg%3E");
}
th button[data-dir="desc"]::after {
content: " " url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 13' xml:space='preserve' width='13' height='13'%3E%3Cpath d='M2.039 5.171c.114.114.25.172.406.172h8.107a.55.55 0 0 0 .406-.172c.114-.114.172-.25.172-.406s-.057-.292-.172-.406L6.906.302C6.792.187 6.658.13 6.5.13s-.292.057-.406.172L2.039 4.355a.557.557 0 0 0-.172.406.541.541 0 0 0 .172.408zM6.5 12.473a.163.163 0 0 1-.127-.055L2.319 8.364a.159.159 0 0 1-.055-.127c0-.042.01-.081.055-.127s.084-.055.127-.055h8.107c.042 0 .081.01.127.055s.055.084.055.127a.163.163 0 0 1-.055.127l-4.052 4.054c-.047.045-.084.055-.127.055m0 .396a.55.55 0 0 0 .406-.172l4.054-4.054a.557.557 0 0 0 .172-.406.549.549 0 0 0-.172-.406.557.557 0 0 0-.406-.172H2.447a.549.549 0 0 0-.406.172.557.557 0 0 0-.172.406.55.55 0 0 0 .172.406l4.054 4.053c.114.114.25.172.406.172z' fill='%23000'/%3E%3C/svg%3E");
}
<table width='100%'>
<thead>
<tr>
<th width='20%'>Image</th>
<th width='20%'><button data-dir=''>Number</button></th>
<th width='40%'><button data-dir=''>Name</button></th>
<th width='20%'><button data-dir=''>Postal code</button></th>
</tr>
</thead>
<tbody>
<tr>
<td>IMG1</td>
<td>xxx</td>
<td>John Johnson</td>
<td>56430</td>
</tr>
<tr>
<td>IMG2</td>
<td>yyy</td>
<td>Sally Johnson</td>
<td>56430</td>
</tr>
</tbody>
</table>
代码有一些问题,我似乎无法解决,我请求帮助修复:
- 当我单击图像列的第th-header时,它设置第th的数据-dir并从所有其他列中删除数据-dir,基本上开始对图像列进行排序。image列没有按钮,也不应该能够触发排序函数或data-dir函数。有办法解决这个问题吗?
- 当表已经升序排序时,例如名称列,当单击名称列的第th标头时,它首先升序排序(即使它已经是),然后在第二次单击降序排序,而不是立即降序排序。有办法解决这个问题吗?
- 是代码优化,因为它是,特别是部分我添加了let按钮,const arrayButtons和if else for setAttribute of data-dir?它还能更优化吗?
我正在努力学习,所以任何反馈都会很感激。:)
提前感谢!
这是一个工作版本,有几个问题。它甚至对Image进行排序列,因为您使用了document.querySelectorAll('th')
并在所有这些元素上添加了一个单击侦听器,而不是对<button>
元素这样做。
您的版本不记得以前的排序方向,因为您使用了this.asc
,这是一个全局变量,而不是与特定列相关的东西。为了解决这个问题,我使用button.dataset.prevDir
来跟踪以前的方向,而不总是显示箭头。
对于优化。不,arrayButtons
不是要走的路。在我的例子中,我使用了buttons
,这是所有按钮的列表,而不是setAttribute
,我使用了dataset
。
const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent;
const comparer = (idx, asc) => (a, b) => ((v1, v2) =>
v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2)
)(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx));
const buttons = document.querySelectorAll('button');
buttons.forEach(button => button.addEventListener('click', ((e) => {
const th = button.parentNode;
const table = th.closest('table');
const tbody = table.querySelector('tbody');
button.dataset.prevDir = button.dataset.prevDir === 'asc' ? 'desc' : 'asc';
buttons.forEach((btn) => (btn.dataset.dir = ''));
Array.from(tbody.querySelectorAll('tr'))
.sort(comparer(Array.from(th.parentNode.children).indexOf(th), button.dataset.prevDir === 'asc'))
.forEach(tr => tbody.appendChild(tr));
button.dataset.dir = button.dataset.prevDir;
})));
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th button {
background-color: transparent;
border: none;
cursor: pointer;
font: inherit;
color:inherit;
width: 100%;
}
th button[data-dir="asc"]::after {
content: " " url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 13' xml:space='preserve' width='13' height='13' transform='scale(1 -1)'%3E%3Cpath d='M2.039 5.171c.114.114.25.172.406.172h8.107a.55.55 0 0 0 .406-.172c.114-.114.172-.25.172-.406s-.057-.292-.172-.406L6.906.302C6.792.187 6.658.13 6.5.13s-.292.057-.406.172L2.039 4.355a.557.557 0 0 0-.172.406.541.541 0 0 0 .172.408zM6.5 12.473a.163.163 0 0 1-.127-.055L2.319 8.364a.159.159 0 0 1-.055-.127c0-.042.01-.081.055-.127s.084-.055.127-.055h8.107c.042 0 .081.01.127.055s.055.084.055.127a.163.163 0 0 1-.055.127l-4.052 4.054c-.047.045-.084.055-.127.055m0 .396a.55.55 0 0 0 .406-.172l4.054-4.054a.557.557 0 0 0 .172-.406.549.549 0 0 0-.172-.406.557.557 0 0 0-.406-.172H2.447a.549.549 0 0 0-.406.172.557.557 0 0 0-.172.406.55.55 0 0 0 .172.406l4.054 4.053c.114.114.25.172.406.172z' fill='%23000'/%3E%3C/svg%3E");
}
th button[data-dir="desc"]::after {
content: " " url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 13' xml:space='preserve' width='13' height='13'%3E%3Cpath d='M2.039 5.171c.114.114.25.172.406.172h8.107a.55.55 0 0 0 .406-.172c.114-.114.172-.25.172-.406s-.057-.292-.172-.406L6.906.302C6.792.187 6.658.13 6.5.13s-.292.057-.406.172L2.039 4.355a.557.557 0 0 0-.172.406.541.541 0 0 0 .172.408zM6.5 12.473a.163.163 0 0 1-.127-.055L2.319 8.364a.159.159 0 0 1-.055-.127c0-.042.01-.081.055-.127s.084-.055.127-.055h8.107c.042 0 .081.01.127.055s.055.084.055.127a.163.163 0 0 1-.055.127l-4.052 4.054c-.047.045-.084.055-.127.055m0 .396a.55.55 0 0 0 .406-.172l4.054-4.054a.557.557 0 0 0 .172-.406.549.549 0 0 0-.172-.406.557.557 0 0 0-.406-.172H2.447a.549.549 0 0 0-.406.172.557.557 0 0 0-.172.406.55.55 0 0 0 .172.406l4.054 4.053c.114.114.25.172.406.172z' fill='%23000'/%3E%3C/svg%3E");
}
<table width='100%'>
<thead>
<tr>
<th width='20%'>Image</th>
<th width='20%'><button data-dir=''>Number</button></th>
<th width='40%'><button data-dir=''>Name</button></th>
<th width='20%'><button data-dir=''>Postal code</button></th>
</tr>
</thead>
<tbody>
<tr>
<td>IMG1</td>
<td>xxx</td>
<td>John Johnson</td>
<td>56430</td>
</tr>
<tr>
<td>IMG2</td>
<td>yyy</td>
<td>Sally Johnson</td>
<td>56430</td>
</tr>
</tbody>
</table>
import React, { useState } from "react";
import Column from "./Column";
import Row from "./Row";
import getSortedData from "./getSortedData";
import "./styles.css";
const data = [
{ name: "Manu", score: 98, age: 21 },
{ name: "Fran", score: 70, age: 43 },
{ name: "Jose", score: 22, age: 22 },
{ name: "Ana", score: 23, age: 99 },
{ name: "Luis", score: 33, age: 14 },
{ name: "Simon", score: 47, age: 54 },
{ name: "Xavier", score: 44, age: 35 }
];
export default function App() {
const [sortedBy, setSortedBy] = useState(null);
const [direction, setDirection] = useState("UP");
const onSortBy = name => {
if (sortedBy !== name) {
setSortedBy(name);
} else if (direction === "UP") {
setDirection("DOWN");
} else {
setSortedBy(null);
setDirection("UP");
}
};
const sortedData = getSortedData(data, sortedBy, direction);
return (
<div className="App">
<table>
<tr>
<Column
name="name"
direction={direction}
sortedBy={sortedBy}
onSortBy={onSortBy}
/>
<Column
name="score"
direction={direction}
sortedBy={sortedBy}
onSortBy={onSortBy}
/>
<Column
name="age"
direction={direction}
sortedBy={sortedBy}
onSortBy={onSortBy}
/>
</tr>
{sortedData.map(player => (
<Row player={player} />
))}
</table>
</div>
);
}