表行必须在表中的任意位置交换,即,行 i 和行 j 必须更改位置,其中 i 和< em> j 不一定相邻。请参阅下面的当前实现。表行应按列排序; sort_index
指定一列,该列是通过按表标题之一生成的。
实施中的问题是,表头是通过单击标题而递增地排序的,而表头应是单击以进行排序的。
var table_index = {
watched: 0,
title: 1,
director: 2,
year: 3
};
var sort_index = 0;
$(document).ready(function()
{
var table_headers = document.getElementById("header-item").children;
for (var k = 0; k < table_headers.length; k++)
{
$("#" + table_headers[k].id).bind("click", function(e)
{
sort_index = table_index[e.target.id];
var table = document.getElementById("film-list");
for (var i = 1; i < table.rows.length - 1; i++)
{
var a = table.rows[i].getElementsByTagName("td")[sort_index].innerHTML;
for (var j = i + 1; j < table.rows.length; j++)
{
var b = table.rows[j].getElementsByTagName("td")[sort_index].innerHTML;
var swap = 0;
switch (sort_index)
{
// Alphabetic sort
case 0:
case 1:
case 2:
if (b.toLowerCase() < a.toLowerCase())
swap = 1;
break;
// Numeric sort
case 3:
if (b - a < 0)
swap = 1;
break;
}
if (swap == 1)
{
$(".row-item").eq(i - 1).after(table.rows[j]);
$(".row-item").eq(j - 1).after(table.rows[i]);
}
}
}
});
}
});
看来,真正的问题与循环内的闭包有关。单击标题时,实际上仅在DOM中更新最后一个表行交换,因此需要多次单击才能正确地对表进行排序。
我将为此发布自己的解决方案,以阐明真正的问题。
答案 0 :(得分:2)
我同意Mr Polywhirl的观点,即在DOM本身中这样做可能并不理想(尽管完全有可能,请参见下文)。现代Web开发倾向于使用模型/视图/控制器风格的架构(各种类型),其中模型(您的实际数据)与模型(DOM)的视图和控制器(浏览器,DOM和您的代码一起运行),对模型执行操作(然后在视图中反映出来)。有许多流行的MVC样式的框架,在我撰写本文时(随着时间的推移而变化),最重要的框架可能是React,Vue.js和Angular。我在下面提供了一个React示例。
但是同样,您可以直接在DOM上执行此操作。我看到您使用的是jQuery,因此在下面使用了它-请参阅内联注释。
// Hook `click` on the table header, but only call our callback if
// that click passes through a `th`
$(".sortable thead").on("click", "th", function() {
// Which column is this?
var index = $(this).index();
// Get the tbody
var tbody = $(this).closest("table").find("tbody");
// Disconnect the rows and get them as an array
var rows = tbody.children().detach().get();
// Sort it
rows.sort(function(left, right) {
// Get the text of the relevant td from left and right
var $left = $(left).children().eq(index);
var $right = $(right).children().eq(index);
return $left.text().localeCompare($right.text());
});
// Put them back in the tbody
tbody.append(rows);
});
td, th {
padding: 4px;
}
th {
cursor: pointer;
}
table {
border-collapse: collapse;
}
table, td, th {
border: 1px solid #ddd;
}
To sort the rows alphabetically by a column's contents, click its header.
<table class="sortable">
<thead>
<th>English</th>
<th>Spanish</th>
<th>Italian</th>
</thead>
<tbody>
<tr>
<td>One</td>
<td>Uno</td>
<td>Uno</td>
</tr>
<tr>
<td>Two</td>
<td>Dos</td>
<td>Due</td>
</tr>
<tr>
<td>Three</td>
<td>Tres</td>
<td>Tre</td>
</tr>
<tr>
<td>Four</td>
<td>Cuatro</td>
<td>Quattro</td>
</tr>
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
它可能会更短一些,但是我想保持清晰,而不是过于简洁。
请注意,这会删除行,对行进行排序并放回去,而不是引起各种就地DOM修改。
这是React示例:
// A React "component" for the table
class MyTable extends React.Component {
// Initializes the component
constructor(props) {
super(props);
this.state = {
// Start out unsorted, copying the array (but reusing the entries, as `row`
// properties -- we do that so we can use their original index as a key)
sorted: props.data.map((row, index) => ({index, row})),
sortKey: null
};
}
// Sort the view
sort(by) {
// Update state...
this.setState(({sorted, sortKey}) => {
if (sortKey === by) {
// ...no update needed, already sorting this way
return;
}
// Copy the array, then sort it (never change state in place)
sorted = sorted.slice();
sorted.sort((left, right) => left.row[by].localeCompare(right.row[by]));
// Return the state updates
return {sorted, sortKey: by};
});
}
// Render the component per current state
render() {
const {sorted} = this.state;
const {headers} = this.props;
return (
<table className="sortable">
<thead>
{headers.map(({title, lang}) => <th key={lang} onClick={() => this.sort(lang)}>{title}</th>)}
</thead>
<tbody>
{sorted.map(({row, index}) =>
<tr key={index}>
{headers.map(({lang}) => <td key={lang}>{row[lang]}</td>)}
</tr>
)}
</tbody>
</table>
);
}
}
// Mount the component
ReactDOM.render(
<MyTable
headers={[
{title: "English", lang: "en"},
{title: "Spanish", lang: "es"},
{title: "Italian", lang: "it"}
]}
data={[
{en: "One", es: "Uno", it: "Uno"},
{en: "Two", es: "Dos", it: "Due"},
{en: "Three", es: "Tres", it: "Tre"},
{en: "Four", es: "Cuatro", it: "Quattro"}
]}
/>,
document.getElementById("root")
);
td, th {
padding: 4px;
}
th {
cursor: pointer;
}
table {
border-collapse: collapse;
}
table, td, th {
border: 1px solid #ddd;
}
To sort the rows alphabetically by a column's contents, click its header.
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
使用各种ES2015 +功能,例如解构,箭头功能和速记属性;并且还使用JSX语法(这不是JavaScript功能;由代码段中的Babel处理)。
答案 1 :(得分:2)
您可以就地对HTML进行排序,也可以将数据绑定到表并在需要排序时重新呈现它。
我用过T.J. Crowder的代码用于就地排序,但将其变成了jQuery插件并添加了可绑定的表。您可以在下面看到两个示例。
def _validate_file_name(cplex, filename, description):
"""Validate filenames against the set of allowable characters in CPLEX.
Returns the filename, possibly enclosed in double-quotes, or raises
a ValueError is unallowable characters are found.
"""
if filename is None:
return filename
matches = _validate_file_name.illegal_characters.search(filename)
if matches:
raise ValueError(
"Unallowed character (%s) found in CPLEX %s file path/name.\n\t"
"For portability reasons, only [%s] are allowed. Filename: %s"
% (matches.group(), description,
_validate_file_name.allowed_characters.replace("\\",''),filename))
# CPLEX only supports quoting spaces starting in v12.8.
if ' ' in filename:
if cplex.version()[:2] >= (12,8):
filename = '"'+filename+'"'
else:
raise ValueError(
"Space detected in CPLEX %s file path/name\n\t%s\nand "
"CPLEX older than version 12.8. Please either upgrade "
"CPLEX or remove the space from the %s path."
% (description, filename, description))
return filename
_validate_file_name.allowed_characters = r"a-zA-Z0-9 \.\-_\%s" % (os.path.sep,)
_validate_file_name.illegal_characters = re.compile(
'[^%s]' % (_validate_file_name.allowed_characters,))
(function($) {
$.fn.sortable = function() {
this.find('thead').on('click', 'th', function(e) {
var columnIndex = $(this).index();
var $tbody = $(this).closest('table').find('tbody');
var rows = $tbody.children().detach().get();
rows.sort(function(left, right) {
var $left = $(left).children().eq(columnIndex);
var $right = $(right).children().eq(columnIndex);
return $left.text().localeCompare($right.text());
});
$tbody.append(rows);
});
return this;
};
$.fn.renderTable = function(data) {
var fields = Object.keys(data[0]);
return this.renderTableHeaders(fields).renderTableRows(fields, data);
};
$.fn.renderTableHeaders = function(fields) {
return this.append($.renderTableHeaders(fields));
}
$.fn.renderTableRows = function(fields, data) {
return this.append($.renderTableRows(fields, data));
};
$.tableFromJson = function(data) {
return $('<table>').renderTable(data);
};
$.renderTableHeaders = function(fields) {
return $('<thead>').append($('<tr>').append(fields
.map(field => $('<th>').text(field))));
};
$.renderTableRows = function(fields, data) {
return $('<tbody>').append(data
.map((rec, row) => $('<tr>').append(fields
.map((field, col) => $('<td>').text(rec[field])))));
};
$.bindableTable = function(data, sortable) {
var $table = $.tableFromJson(data).addClass('bindable');
if (sortable) {
$table.dataRef = data;
$table.addClass('sortable').find('thead').on('click', 'th', function(e) {
var dataIndex = $(this).text();
$table.dataRef.sort(function (a, b) {
var left = new String(a[dataIndex]);
var right = new String(b[dataIndex]);
return left.localeCompare(right);
});
var fields = Object.keys($table.dataRef[0]);
$table.find('tbody').replaceWith($.renderTableRows(fields, $table.dataRef));
});
}
return $table;
};
})(jQuery);
var jsonData = [
{ "id": 1, "name": "John", "age": 24, "make": "Chevrolet", "model": "Silverado", "year": 2016 },
{ "id": 2, "name": "Jack", "age": 36, "make": "Toyota", "model": "Corolla", "year": 2018 },
{ "id": 3, "name": "Jill", "age": 29, "make": "Ford", "model": "Escape", "year": 2015 }
];
$('body').append($('<h1>').text('HTML sort'));
$.tableFromJson(jsonData).addClass('stylized sortable').sortable().appendTo('body');
$('body').append($('<h1>').text('Databinding sort'));
$.bindableTable(jsonData, true).addClass('stylized').appendTo('body');
body {
padding: 0.25em !important;
}
h1 {
font-weight: bold !important;
margin-top: 0.75em !important;
margin-bottom: 0.33em !important;
}
table.stylized {
font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif;
font-size: 12px;
text-align: left;
border-collapse: collapse;
margin: 4px;
width: 600px;
}
table.stylized thead th {
text-transform: capitalize;
font-size: 13px;
color: #039;
background: #b9c9fe;
padding: 6px;
cursor: pointer;
}
table.stylized tbody tr:nth-child(odd) {
background: #f2f5ff;
}
table.stylized tbody tr:nth-child(even) {
background: #e8edff;
}
table.stylized tbody td {
border-top: 1px solid #fff;
color: #669;
padding: 6px;
}
table.stylized tbody tr:hover td {
background: #d0dafd;
}
答案 2 :(得分:0)
如上所述,最初的问题是交换行。似乎真正的问题与循环内的闭包有关。单击标题时,实际上仅在DOM中更新最后一个表行交换,因此需要多次单击才能正确地对表进行排序。
一种可能的解决方案是使用内置的排序功能,如下所示。
var table_index = {
watched: 0,
title: 1,
director: 2,
year: 3
};
var sort_index = 0;
var tbody = $("tbody").children().get();
$(document).ready(function()
{
var table_headers = $("thead").children();
for (var k = 0; k < table_headers.length; k++)
{
$("#" + table_headers[k].id).bind("click", function(e)
{
sort_index = table_index[e.target.id];
switch (sort_index)
{
// Alphabetic sort
case 0:
case 1:
case 2:
tbody.sort(function(a, b) {
var l = $(a).children().eq(sort_index).text();
var r = $(b).children().eq(sort_index).text();
if (r.toLowerCase() < l.toLowerCase())
return 1;
else if (r.toLowerCase() > l.toLowerCase())
return -1;
else
return 0;
});
break;
// Numeric sort
case 3:
tbody.sort(function(a, b) {
var l = $(a).children().eq(sort_index).text();
var r = $(b).children().eq(sort_index).text();
return l - r;
});
break;
}
$("tbody").children().detach();
$("tbody").append(tbody);
});
}
});